Accidental Object Mutation Bugs
debt(d5/e3/b5/t7)
Closest to 'specialist tool catches it' (d5). ESLint with mutation-detection plugins (e.g. eslint-plugin-immutable, eslint-plugin-no-mutation) can catch common patterns like .push(), .sort(), .splice() automatically, but catching all mutation patterns—especially indirect mutations through function calls or nested property changes—requires deeper analysis. Many mutation bugs only surface at runtime or during testing.
Closest to 'simple parameterised fix' (e3). The quick_fix confirms one-line replacements (spread syntax [...arr], {...obj}, structuredClone()) solve most cases. Refactoring direct state mutations to use immutable patterns is typically localized to a single function or component, not cross-cutting.
Closest to 'persistent productivity tax' (b5). The term applies across web and CLI contexts. Preventing accidental mutation requires discipline in every array/object operation—developers must consistently choose immutable patterns (map/filter/slice over sort/splice/push), and React codebases especially bear this cost because state mutation silently breaks reactivity. This choice shapes coding patterns across multiple work streams but doesn't redefine the entire system architecture.
Closest to 'serious trap' (t7). The canonical misconception ('const prevents object mutation') directly contradicts how const actually works in JavaScript and is a documented gotcha that most JS developers eventually learn. The trap is severe because const gives a false sense of safety—developers expect it to prevent mutation but it only prevents reassignment, leading to silent bugs. This contradicts similar immutability guarantees in other languages (e.g. Rust's const correctness).
TL;DR
Explanation
JavaScript passes objects and arrays by reference. Mutating a function parameter mutates the caller's object. Common in: React state mutations (never mutate state directly), Redux reducers, array methods (sort(), splice(), reverse() mutate in place vs map(), filter(), slice() which return new arrays). Solutions: spread to clone ({...obj}), structuredClone() for deep clone, Object.freeze() to prevent mutation, immutable patterns. Mutation detection: Redux DevTools, React StrictMode double-invokes reducers to catch mutations.
Common Misconception
Why It Matters
Common Mistakes
- Sorting arrays in place without cloning: users.sort() mutates the original.
- Mutating React state directly: state.items.push(item) — never re-renders.
- Not knowing splice() mutates but slice() doesn't.
Code Examples
// React anti-pattern — direct state mutation:
const [items, setItems] = useState([]);
function addItem(item) {
items.push(item); // Mutates state — React won't re-render
setItems(items);
}
// Immutable patterns:
function addItem(item) {
setItems(prev => [...prev, item]); // New array — React re-renders
}
// Sort without mutation:
const sorted = [...users].sort((a, b) => a.name.localeCompare(b.name));
// Deep clone:
const safeCopy = structuredClone(complexObject);