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.
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.
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.
a.getB().getC().doD()String/int instead of purpose-built typesSources: 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).
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.
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.
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.
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.
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.
Sources: Hunt & Thomas, The Pragmatic Programmer (DRY) · Fowler, Refactoring (Duplicated Code) · Evans, Domain-Driven Design.
Give each decision a single authoritative home. Extract shared logic; unify vocabulary so one concept has one name everywhere.
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.
Sources: Brooks, The Mythical Man-Month (essential vs. accidental complexity) · Fowler, Refactoring (Speculative Generality) · Ousterhout, A Philosophy of Software Design.
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.
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.
Sources: Martin, Clean Architecture (Dependency Inversion, the Dependency Rule) · Brooks, The Mythical Man-Month · Winters et al., Software Engineering at Google.
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.
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.
Sources: Evans, Domain-Driven Design (rich models, ubiquitous language) · Fowler, Refactoring (Anemic Domain Model).
Move behaviour onto the objects that own the data. Align every name with the language the business uses. Make illegal states unrepresentable.
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:
| Risk | The question it asks |
|---|---|
| Test Obscurity | Can you tell what a test verifies without reading its implementation? |
| Test Brittleness | Does a refactor that preserves behaviour still break the test? |
| Test Duplication | Is the same setup or assertion copied across many tests? |
| Mock Abuse | Does the test verify interactions instead of outcomes? |
| Coverage Illusion | Does high coverage hide tests that assert nothing meaningful? |
| Architecture Mismatch | Do the tests fight the code's seams instead of using them? |
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