One module changes for many unrelated reasons — tax law updates, UI changes, API shape drift. Every commit touches concerns the committer didn't intend; reviewers carrying the cognitive load of the whole module verify changes outside their expertise, paying comprehension cost on every visit, and team merges collide on lines that were never about the same thing.
Each module changes for one reason — separation of concerns in practice, with changes clustering around a single axis of variation. The reader sees the axis in the module name; the team aligns ownership with the axis, and every cross-cutting concern lives in its own module with its own commit history.
Smellier version
function checkout(cart) {const tax = computeTax(cart, jurisdiction); // tax churnconst html = renderInvoice(cart, tax); // UI churnreturn postToGateway(html); // API churn}
Fresher version
function priced(cart) { return { ...cart, tax: computeTax(cart) }; }function rendered(cart) { return renderInvoice(cart); }function sent(html) { return postToGateway(html); }
Every team's churn lands in the same file; merges become contentious; testing one concern requires understanding all of them — high cognitive load for every reader. The team's verification cost compounds across the merged concerns; a change that broke one axis ships because the reviewer was focused on the axis the commit message named.
Splitting the module forces a coordination surface — two files where there used to be one, plus a shared seam that any cross-cutting change must touch in both. The blast radius of a cross-cutting change rises along the seam; coordination cost across the new module boundary must justify the per-axis isolation the split provides.
Owners and reviewers are obvious; tests are focused; teams can move in parallel without colliding. The team's maintenance cost per axis drops to the work that axis actually requires, rather than the work plus the verification surface of every other concern in the original module. New features ship without cross-team coordination.
Aggressive splitting on every change axis fragments cohesion — a class that genuinely belongs together gets shredded into pieces too small to express the original concept, adding accidental complexity without payoff. Each new module is another file in the navigation surface; readers chase concepts across boundaries the original cohesion didn't justify.