Side Effects
debt(d7/e5/b5/t5)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints list phpstan, but automated detection is explicitly marked 'no' — phpstan can flag some patterns but hidden side effects (e.g. a getter warming a cache, a constructor making HTTP calls) are not reliably caught by static analysis alone. The code_pattern examples (function returning value AND writing to database, getter with cache warming) require a human reviewer to recognise the dual-purpose behaviour. No linter reliably catches the general case.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix points to Command-Query Separation — separating read and write responsibilities. While the principle sounds simple, applying it to functions that currently do both (return a value and write to DB, or constructors with IO) typically requires splitting function signatures, updating all call sites within a component, and adjusting tests. It goes beyond a single-line swap but usually stays within one component unless the side-effecting function is widely shared.
Closest to 'persistent productivity tax' (b5). Hidden side effects apply broadly across web, cli, and queue-worker contexts (all three listed in applies_to). They slow down testing (functions that return different values for same input break caching and mocking), make composition risky, and force future maintainers to read implementations rather than signatures. They don't define the entire system's shape but persistently tax many work streams — especially testing and debugging.
Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The canonical misconception is explicitly stated: developers believe side effects are always bad and should be eliminated, when in reality the goal is to isolate and make them explicit. This is a well-documented gotcha in functional and clean-code literature that most intermediate developers eventually encounter and correct, but it misleads beginners into either eliminating necessary effects or ignoring the problem entirely. It contradicts naive intuition but is widely documented.
Also Known As
TL;DR
Explanation
Side effects include: modifying global or class-level state, writing to files or databases, sending emails, mutating arguments passed by reference, and throwing exceptions. They are necessary for useful programs but should be isolated to well-defined boundaries and minimised in business logic. Unexpected side effects are a major source of bugs. The command-query separation principle (CQS) formalises this: methods that return values should not modify state, and methods that modify state should not return values.
Common Misconception
Why It Matters
Common Mistakes
- Functions that modify global state, static properties, or their arguments without signalling it in the signature.
- Constructors with side effects — database writes, HTTP calls, or file I/O in __construct().
- Not distinguishing between acceptable necessary side effects (persisting data) and hidden accidental ones.
- Functions that return different values for the same input due to hidden state — breaks caching and testing.
Code Examples
// Hidden side effects — callers can't predict what changes
function calculateTotal(array $cart): float {
$this->lastCalculated = time(); // hidden mutation
$this->analytics->track('cart_viewed'); // hidden I/O
$total = array_sum(array_column($cart, 'price'));
$this->cache->set('cart_total', $total); // hidden write
return $total;
}
// Pure calculation — no side effects
function calculateTotal(array $cart): float {
return array_sum(array_column($cart, 'price'));
}
// Side effects handled explicitly by the caller
$total = calculateTotal($cart);
$this->analytics->track('cart_viewed');
$this->cache->set('cart_total:' . $cartId, $total);