Glossary

Immutability

Immutability means that once a value is created, it cannot be changed. Instead of modifying existing data, you create new data with the changes applied. This prevents a class of bugs caused by unexpected mutations.

Explanation

Primitive values in JavaScript (numbers, strings, booleans, null, undefined) are inherently immutable — you can't change the value 42, you can only create a new value. Objects and arrays are mutable by default — you can add, remove, or modify properties and elements. The pitfall: since objects are passed by reference, one function modifying an object can silently affect other code that holds a reference to the same object. Immutable programming means treating objects and arrays as if they're immutable: instead of modifying them, create new copies with the desired changes. JavaScript's spread operator and Object.assign() enable this: const newUser = {...user, age: 31} creates a new object with age changed, leaving the original user unchanged. For arrays: const newItems = [...items, newItem] adds an item immutably; items.filter(i => i.id !== id) removes immutably. React's entire state model is built on immutability: setState with the same object reference doesn't trigger re-renders; React detects state changes by reference comparison. If you mutate state directly (state.count++ instead of setState({...state, count: state.count + 1})), React doesn't detect the change and the UI doesn't update. This is one of the most common React bugs. Redux enforces immutability at the reducer level: reducers must return new state objects, never mutate the existing state. Immer.js is a popular library that lets you write "mutating" code but produces immutable updates under the hood — the mental model of mutation with the guarantees of immutability.

Code Example

javascript
// Mutable vs immutable patterns in JavaScript

// MUTABLE (problematic when shared)
const user = { name: 'Alice', age: 30, hobbies: ['reading'] };
function birthday(u) { u.age++; }  // mutates the original!
birthday(user);
console.log(user.age); // 31 — original is changed

// IMMUTABLE (safe to share)
function birthdayImmutable(u) {
  return { ...u, age: u.age + 1 }; // new object, original untouched
}
const older = birthdayImmutable(user);
console.log(user.age);  // 30 — unchanged
console.log(older.age); // 31

// Array immutability
const items = [1, 2, 3, 4, 5];
// Mutable operations (avoid when shared):
items.push(6);    // mutates
items.splice(1,1); // mutates

// Immutable alternatives:
const added    = [...items, 6];              // add
const removed  = items.filter(x => x !== 3); // remove
const updated  = items.map(x => x === 3 ? 99 : x); // update
const inserted = [...items.slice(0,2), 99, ...items.slice(2)]; // insert at index

// React: why mutation breaks state
// BAD: direct mutation — React doesn't detect change
const [user, setUser] = React.useState({ name: 'Alice', age: 30 });
user.age = 31; // mutation! setUser was never called, no re-render

// GOOD: immutable update
setUser(prev => ({ ...prev, age: prev.age + 1 })); // triggers re-render

Why It Matters for Engineers

Immutability eliminates an entire class of bugs: unexpected mutations, shared state corruption, and React/Redux state update failures. These bugs are particularly insidious because the mutation happens in one place but manifests as a UI error in a completely different component. Immutable data makes the cause-and-effect chain between state changes and UI updates explicit. Immutability also enables performance optimizations: React.memo, useMemo, and Redux's connect use reference equality (===) to determine if re-rendering is needed. With immutable updates, you can check "did this object change?" with a simple reference comparison instead of a deep equality check.

Related Terms

Variable · Object · Closure · Function

Learn This In Practice

Go deeper with the full module on Beyond Vibe Code.

Programming Foundations → →