brooks-lint logo brooks-lint

The Six Code Decay Risks

A field guide to the ways production code rots — and the classic engineering books that named each one fifty years before your codebase did it again.

Most linters count lines and cyclomatic complexity. They measure the surface. Decay happens underneath: in how responsibilities tangle, how knowledge duplicates, how dependencies invert. These six patterns are a synthesis of twelve classic software engineering books, applied to modern code review.

The six risks

  1. Cognitive Overload — how hard is this to understand?
  2. Change Propagation — what breaks when you touch one thing?
  3. Knowledge Duplication — is one decision expressed in many places?
  4. Accidental Complexity — is the code harder than the problem?
  5. Dependency Disorder — do dependencies flow one way?
  6. Domain Model Distortion — does the code mean what the business means?

Each risk below follows the same shape brooks-lint uses for every finding — the Iron Law: Symptom → Source → Consequence → Remedy. A symptom you can see in the diff, a source you can cite to a book, a consequence that explains why it matters, and a remedy you can actually apply.

1 · Cognitive Overload R1

Diagnostic question: How much mental effort does a human need to understand this?

Working memory holds about four chunks at once. Code that exceeds that budget causes mistakes, invites avoidance, and blocks the very refactoring that would fix it.

Signature symptoms

Sources: Fowler, Refactoring (Long Method, Long Parameter List, Message Chains, Primitive Obsession) · McConnell, Code Complete (high-quality routines, the power of variable names) · Ousterhout, A Philosophy of Software Design (deep modules) · Evans, Domain-Driven Design (ubiquitous language).

Remedy

Extract methods until each one operates at a single level of abstraction. Replace primitives with value types. Make modules deep: a simple interface over substantial functionality, not a thin wrapper.

2 · Change Propagation R2

Diagnostic question: How many unrelated things break when you change one thing?

This is the most expensive decay risk because it compounds: every future edit inherits the blast radius of the last one.

Signature symptoms

Sources: Fowler, Refactoring (Divergent Change, Shotgun Surgery) · Martin, Clean Architecture (Single Responsibility) · Hunt & Thomas, The Pragmatic Programmer (orthogonality) · Winters et al., Software Engineering at Google.

Remedy

Separate responsibilities into focused units and let a thin orchestrator call them. Aim for orthogonality — changing the loyalty formula should never risk breaking email notifications.

3 · Knowledge Duplication R3

Diagnostic question: Is the same decision expressed in more than one place?

DRY is about knowledge, not text. Two identical-looking blocks that encode different decisions are fine; one decision copied into three files is a latent bug waiting for the day someone updates two of them.

Signature symptoms

Sources: Hunt & Thomas, The Pragmatic Programmer (DRY) · Fowler, Refactoring (Duplicated Code) · Evans, Domain-Driven Design.

Remedy

Give each decision a single authoritative home. Extract shared logic; unify vocabulary so one concept has one name everywhere.

4 · Accidental Complexity R4

Diagnostic question: Is the code more complex than the problem it solves?

Brooks distinguished essential complexity (inherent to the problem) from accidental complexity (introduced by our solution). The second kind is the only kind you can delete.

Signature symptoms

Sources: Brooks, The Mythical Man-Month (essential vs. accidental complexity) · Fowler, Refactoring (Speculative Generality) · Ousterhout, A Philosophy of Software Design.

Remedy

Delete speculative abstractions until a second real consumer appears. Prefer the simplest design that solves today's problem — you can always add structure when the need is concrete.

5 · Dependency Disorder R5

Diagnostic question: Do dependencies flow in a consistent, predictable direction?

Architecture is mostly about the direction of the arrows. When high-level policy depends on low-level detail, the detail starts driving the design.

Signature symptoms

Sources: Martin, Clean Architecture (Dependency Inversion, the Dependency Rule) · Brooks, The Mythical Man-Month · Winters et al., Software Engineering at Google.

Remedy

Invert the dependency: define an interface the high-level code owns, and let infrastructure implement it. Break cycles by extracting the shared abstraction both sides can depend on.

6 · Domain Model Distortion R6

Diagnostic question: Does the code faithfully represent the problem it is solving?

When the model drifts from the domain, every reader has to translate in their head — and translation errors become bugs.

Signature symptoms

Sources: Evans, Domain-Driven Design (rich models, ubiquitous language) · Fowler, Refactoring (Anemic Domain Model).

Remedy

Move behaviour onto the objects that own the data. Align every name with the language the business uses. Make illegal states unrepresentable.

And the test suite decays too

brooks-lint applies the same lens to tests, with six parallel risks (T1–T6) drawn from xUnit Test Patterns, The Art of Unit Testing, How Google Tests Software, and Working Effectively with Legacy Code:

RiskThe question it asks
Test ObscurityCan you tell what a test verifies without reading its implementation?
Test BrittlenessDoes a refactor that preserves behaviour still break the test?
Test DuplicationIs the same setup or assertion copied across many tests?
Mock AbuseDoes the test verify interactions instead of outcomes?
Coverage IllusionDoes high coverage hide tests that assert nothing meaningful?
Architecture MismatchDo the tests fight the code's seams instead of using them?

See it run on your own code

brooks-lint is a free, open-source plugin for Claude Code, Gemini CLI, and Codex. Install it and review any file in one command:

/plugin marketplace add hyhmrright/brooks-lint
/brooks-review

⭐ Get it on GitHub