Specs rot faster than code
Code has a compiler, a test suite and a production environment all pulling it toward truth. A specification has nothing. The difference never mattered much while humans read specs and silently corrected them. Agents read them literally.
Everyone knows documentation goes stale. It has been a running joke for as long as there has been documentation. The joke was tolerable because staleness was mostly harmless: an engineer would read the wrong sentence, think “that is not how it works any more”, and carry on.
That silent correction was doing enormous load-bearing work, and it has stopped.
My position: specifications rot structurally faster than code, for reasons that have nothing to do with discipline, and the reason it never hurt much was a human correction layer that agents do not have. The consequence is that a spec’s decay rate now matters more than its initial quality, and you should be designing for freshness rather than for completeness.
Code cannot rot the same way
This is the asymmetry, and it is worth stating precisely because it is usually described as a discipline problem when it is a physics problem.
Code has three forces continuously pulling it toward truth. It has to compile or interpret, so it cannot drift into syntactic nonsense. It has a test suite that fails when behaviour changes unintentionally. And it runs in production, where being wrong generates complaints. None of these make code correct, but all of them make code current: whatever the code says, that is what the system does.
A specification has none of these. Nothing checks it. Nothing runs it. Nothing complains when it diverges. The only force acting on a document is somebody choosing to update it, and that choice competes with everything else on their week.
Forces acting on a specification
- Someone remembers to update it
- That is the complete list
Forces acting on code
- It must compile or fail loudly
- Tests fail when behaviour shifts
- Production surfaces divergence as complaints
- Reviewers read it because it is the thing shipping
- It is the artefact everyone's incentives point at
Given that asymmetry, the surprising thing is not that specs rot. It is that anyone expected otherwise.
The correction layer that disappeared
Here is why it did not use to matter much.
An engineer reading a spec is running a continuous plausibility check against everything else they know. They know the payments service moved off that queue last spring. They know that field was renamed. They read “the system sends a confirmation email” and think “it sends a notification now, email is one channel”, and they build the right thing without ever mentioning the discrepancy to anyone.
That correction was invisible, unpaid and universal. It meant a document could be forty percent stale and still produce correct outcomes, because the reader was quietly repairing it on the way in.
An agent does not do this. It reads the sentence as written, treats it as authoritative, and builds precisely what it says. It has no independent knowledge of the migration you did in spring, and where it does have contradicting evidence from the repository, it has no principled way of deciding which source wins. That is the shape of context blindness: output that is internally coherent and incompatible with the system it is joining, because the input it trusted described a system that no longer exists.
So a stale line stopped being ignored and started being executed. That is the whole change, and it is enough to invert how you should think about writing specs at all.
Decay rate over initial quality
If specs are executed literally, then the question “is this document good” is much less useful than “how fast does this document become wrong, and how would we know”.
The practical implications are unglamorous and mostly about structure rather than prose.
Bind criteria to code paths. A criterion that names the module, endpoint or file it governs can be flagged when that path changes. This is the single highest-value structural change available, because it manufactures the missing force: it gives the document a way to be disturbed when reality moves.
Give every criterion an owner and a date. Not a review cycle, which nobody honours. An owner who gets asked, and a visible last-confirmed date so that anyone reading knows what they are handling. A criterion last confirmed fourteen months ago is not the same object as one confirmed last week, and readers deserve to know which one they have.
Write fewer, more checkable statements. Long narrative specs rot faster than short ones because they contain more claims, most of them incidental. A document of eleven precise criteria has a much lower decay surface than nine pages of prose that happen to contain eleven criteria.
Delete confidently. A criterion nobody will maintain is worse than no criterion, because its presence implies it is current. Removing a stale statement is a genuine improvement to the document, not an admission of defeat.
The version that actually works
The pattern I would push hardest: keep the criteria next to the code they govern, in the repository, in the change that implements them.
That single move gets you most of what the previous section describes for very little effort. The criteria are then subject to the same review as the code, they move with it, and a change that alters behaviour has the criterion sitting in the same diff, where updating it is the path of least resistance rather than a separate act of virtue.
It is not a complete answer. Plenty of specification work belongs upstream, before there is any code to sit beside, and product artefacts have readers who will not go looking in a repository. But for the durable statement of what a system must do, proximity beats process every time.
Where this breaks down
Bound criteria produce alert fatigue very quickly. If every change to a file flags three criteria for confirmation, people will confirm them without reading within about two weeks, and you will have built a ritual that manufactures false currency. Getting the granularity right is genuinely hard, and the failure mode is a document that says “confirmed last Tuesday” and is wrong.
Putting specs in the repository loses the audience they had. Support, sales engineering, compliance and the people who need to know what the product promises do not read source control, and telling them to is not a strategy. You may end up maintaining two representations, which is exactly the duplication that caused the rot in the first place.
The strongest counter is to stop writing specs and write tests instead. If a criterion is checkable, encode it as an executable check and let the suite be the specification, which gets you the error-correcting environment for free. I think that fails for the criteria that matter most, which are the ones about intent, constraint and what must not happen, and those are exactly the ones tests express worst. But it is a serious position and it is held by serious people.
Freshness and correctness are not the same thing. A criterion confirmed yesterday can be confirmed wrong, and my emphasis on decay rate rather than initial quality could be read as licence to write sloppy criteria quickly. It is not meant that way, but the incentive is real: recency is easy to measure and correctness is not, so recency is what will end up on the dashboard.
And I sell tooling in this area, so discount accordingly. An argument that specifications need structural support is convenient for a company that makes structural support for specifications. The advice to keep criteria in the repository next to the code costs nothing and involves no vendor, and it is the part I would do first.
The takeaway
Specifications rot faster than code because code lives in an error-correcting environment and specifications live in a text file. That was survivable while every reader silently repaired what they read. Agents do not repair, they execute, so a stale line now ships.
Stop optimising for how good the document is on the day it is written. Optimise for how quickly you would find out that it has become wrong: bind criteria to the code paths they govern, keep them short and checkable, give them an owner and a date, and delete the ones nobody will maintain.
If you take one thing into next week: open the acceptance criteria for a feature that shipped a year ago and check each statement against the current behaviour. The proportion that is now false is your actual decay rate, and it is probably higher than anyone on the team would have guessed.