A function that both returns a value AND mutates state — `findMiscreant(people)` that returns the miscreant AND alerts them.
Functions either return a value or mutate state, never both — callers can compose them without surprise.
Before the refactoring
function findMiscreant(people) {for (const p of people) {if (p.isMiscreant) { alert(p); return p; }}}
After the refactoring
function findMiscreant(people) { return people.find(p => p.isMiscreant); }function alertMiscreant(people) {const m = findMiscreant(people);if (m) alert(m);}
Callers can't query without triggering the side effect; tests must work around the dual contract; reasoning about side effects becomes non-local.
If the modification and the query truly cannot be separated (e.g. find-and-remove on a queue), the constraint is fundamental — leave the combined operation but document it.
Reasoning about side effects is local; tests target each shape independently.
Splitting a function whose query and modification truly cannot be separated — find-and-remove on a queue, atomic compare-and-swap — fragments operations that need atomicity.