A Constant Is Not a Side Effect
Why reading an outside variable doesn't always break purity, and why the const keyword matters far less than whether a value actually gets reassigned.
Last time, reading anything outside a function's own parameters counted as a side cause. Then I ran into a function that reads an outside variable and is still pure.
const SHIPPING_FLAT_FEE = 5;
function orderTotal(subtotal) {
return subtotal + SHIPPING_FLAT_FEE;
}SHIPPING_FLAT_FEE lives outside orderTotal. By the last post's rule, that should count as an indirect input.
It doesn't. Here's why.
A Value That Never Changes Might as Well Be Inlined
Picture the same function with the number written in directly:
function orderTotal(subtotal) {
return subtotal + 5;
}Nobody would call this impure. SHIPPING_FLAT_FEE is just a name for 5.
If a value never changes anywhere in the program, referencing it is exactly the same as writing that value in by hand. Nothing outside the function can actually affect the result, because nothing outside the function ever moves.
The Keyword Isn't What Matters
Here's the part that surprised me. Swap const for var:
var SHIPPING_FLAT_FEE = 5;Purity doesn't change. SHIPPING_FLAT_FEE still never gets reassigned anywhere in this program. That's the only thing that ever mattered.
const only stops one specific line from reassigning a variable. It says nothing about whether the whole program leaves that value alone. A var that's never touched again behaves exactly like a real constant, keyword or not.This isn't a reason to stop using const. It's a reason to stop trusting it as proof of anything. What actually decides purity is whether a value gets reassigned anywhere in the program, not which keyword declared it.
The Same Argument Covers Function References
This goes further than plain values. Say orderTotal calls a helper:
function roundToCents(amount) {
return Math.round(amount * 100) / 100;
}
function orderTotal(subtotal) {
return roundToCents(subtotal + SHIPPING_FLAT_FEE);
}roundToCents is just another identifier from outside orderTotal, the same as SHIPPING_FLAT_FEE was. It isn't declared const. It could theoretically get reassigned to a different function somewhere else in the file.
As long as it never actually does, calling it doesn't break purity either. This is exactly why utility libraries like Lodash work: every helper you import is trusted to keep meaning the same thing for the life of the program, so using one inside a "pure" function is no different from using a plain constant. It's the same trust a function's own name is supposed to earn, just extended to everything that function quietly relies on.
Judging a Snippet Alone Never Works
None of this is provable from four lines in isolation. You can only trust that SHIPPING_FLAT_FEE or roundToCents stays fixed by seeing the whole program and confirming neither ever gets reassigned.
That's an unavoidable cost of this rule. It's also exactly why the next part matters so much.
Why the Reader's Confidence Is the Real Point
Suppose you couldn't tell, just by looking, whether SHIPPING_FLAT_FEE changes elsewhere.
To trust one line, you'd have to mentally run every line that came before it. That's the actual cost of a side effect: it forces you to hold the whole program's history in your head just to understand one line of it.
A genuinely constant value removes that cost entirely. You read orderTotal, see that everything it touches is fixed, and you're done. No scrolling up. No checking the rest of the file.
What matters isn't whether a value can be reassigned. It's whether it's obvious to the reader that it never will be.
That's the real payoff of purity: a line of code you can trust in complete isolation. Naming that guarantee precisely, what it actually takes for a function call to earn that trust, is next.
Keep reading