All topics
Functionsintermediate

Pure Functions and Side Effects

Functions that always return the same output for the same input and don't modify anything outside themselves, versus functions that mutate state or perform I/O.

A pure function returns the same output for the same input and changes nothing outside itself.

A pure function is like a vending machine that dispenses the exact same snack every time you press the same button, using only the button press as input — no memory of previous purchases, no side conversations with other machines, and pressing the same button twice never changes anyone else's snack.

Key Concepts

1
    // pure
    const add = (a, b) => a + b;
    const withTax = (p) => ({ ...p, price: p.price * 1.18 });
2
    // impure -- mutates its argument
    const addTax = (p) => { p.price *= 1.18; return p; };
3
    // impure -- depends on outside state
    let rate = 1.18;
    const apply = (p) => p * rate;
4
    // impure -- performs I/O
    const save = (p) => db.insert(p);
5
Two properties, and both are needed. Same input, same output. No observable effect elsewhere.
6
Why it matters practically, rather than as a principle.
7
Testable. A pure function needs no setup and no mocks. Call it, check the return value.
8
Cacheable. If the output depends only on the input, the result can be memoised safely.
9
Safe to reorder and parallelise. Nothing depends on when it ran.
10
Readable. Everything it does is in its signature and its return.
11
Mutation is the side effect that bites most often.
    const sorted = items.sort();      // sorts items IN PLACE
    const sorted = [...items].sort(); // a copy
12
sort, reverse, splice, push and shift all mutate. map, filter, slice and concat do not. Knowing which is which prevents a whole class of bug where a function returns the right answer and quietly changes the caller's array.
sortreversesplicepushshift
13
Newer methods toSorted, toReversed and with are the non-mutating versions, added for exactly this reason.
toSortedtoReversedwith
14
You cannot make everything pure, and it is not the goal. A program that reads nothing and writes nothing does nothing.
15
The useful arrangement is to push effects to the edges: pure functions for the logic, with I/O and mutation in a thin layer around them.
16
    // the logic is pure and testable
    const nextState = reduce(state, action);
17
    // the effect is separate
    render(nextState);
18
Where this shows up. Redux reducers must be pure, React requires render to be pure, and memoisation is only correct on a pure function. Those rules are not style preferences — they are the conditions those systems rely on to work.
19
What the interviewer is probing.1. "What makes a function pure?" Probing: the two conditions. Stalls: "It returns a value." Moves up: the same input always gives the same output, and it has no observable side effect — no I/O, no mutation of anything outside it.
20
2. "Why do pure functions matter in practice?" Probing: the payoff. Stalls: "They are elegant." Moves up: they are testable without setup, cacheable, and safe to reorder or parallelise.
21
3. "Is a function reading a module-level constant pure?" Probing: the boundary. Stalls: "No, it reads outside." Moves up: yes if the value never changes; purity is about observable behaviour, not lexical isolation.
22
4. "Where do side effects belong?" Probing: architecture. Stalls: "Anywhere." Moves up: pushed to the edges — a pure core with I/O at the boundary, which is what makes the core testable.