Two cooks make you the same soup. Cook A uses only what's on the cutting board
and leaves the kitchen spotless. Cook B grabs random spices off the shelf, and on
the way out, somehow sets the curtains on fire.
Both made soup. But you trust Cook A way more. In code, Cook A writes pure
functions, and Cook B leaves side effects everywhere.
The idea in one line
A pure function only uses its inputs and only returns an output. A side
effect is anything it touches outside of that, like changing a global,
writing a file, or hitting the network.
The metaphor: the tidy cook
A pure function is the tidy cook:
- Uses only the ingredients you hand it (its inputs).
- Gives back only the dish (its return value).
- Changes nothing else in the kitchen.
Hand it the same ingredients, get the same dish. Every time. (Yes, that means
pure functions are also deterministic.)
PURE IMPURE (has side effects)
in -> [ function ] -> out in -> [ function ] -> out
nothing else moves ...and also: writes a file,
changes a global, logs, fetches
How it looks in code
// Pure: depends only on input, returns only output, touches nothing else
function add(a, b) {
return a + b
}
// Impure: reaches outside itself (a side effect)
let total = 0
function addToTotal(n) {
total += n // changes a global. now the outside world moved.
console.log(total) // also writes to the console. another side effect.
}
add is a dream to test: add(2, 3) is always 5. addToTotal is trickier,
because its result depends on what total was before, and it messes with the
world on its way out.
Wait, aren't side effects necessary?
Yes! A program with zero side effects does nothing useful. Saving data, showing a
page, calling an API: those are all side effects, and you need them.
The goal is not "no side effects." The goal is to keep them in their own
corner, so most of your code stays pure and easy to trust.
// Keep the thinking pure...
function nextCount(count) {
return count + 1 // pure: easy to test
}
// ...and do the messy stuff at the edge
function handleClick() {
setCount(nextCount(count)) // the side effect (updating the screen) lives here
}
A real case
This is why utility and helper functions are usually pure: format a price,
calculate a total, validate an email. They're easy to test because you just feed
inputs and check outputs. No database, no clock, no mocking circus.
The messy parts (saving to a database, sending an email) get pushed to the edges
where they're easy to spot.
Gotchas juniors hit
1. Calling logging or console.log "harmless."
It's still a side effect. Usually fine, but it means the function isn't pure.
2. Secretly mutating an input.
A function that does arr.push(x) to an array you passed in changed your data.
That's a side effect hiding in plain sight. Return a new value instead.
3. Thinking pure functions are useless because they "just return stuff."
That "just returning stuff" is exactly why they're easy to test, reuse, and
trust. That's the win.
Recap
- Pure function = uses only its inputs, returns only an output, touches nothing else. The tidy cook.
- Side effect = touching the outside world (globals, files, network, screen, even logs).
- You need side effects, just keep them at the edges.
- Pure code is easy to test and reuse. That's the whole point.
Your turn
Pick one function in your project. Ask: does it touch anything besides its inputs
and return value? If yes, can you pull that messy part out and leave a clean,
pure core? Try it, then explain "the tidy cook vs the messy cook" to a friend.
Top comments (0)