Skip to content
Back to all field notes sdd · 6 min

Spec drift: why your spec and your code stop telling the same story

You wrote a spec. Forty agent runs later it describes a product that no longer exists. That gap has a name, and a cost.

field note 6 min

You wrote a spec at the start of the week. It described the checkout flow: three steps, one guest path, no stored cards. By Friday the code has a saved-card option, a two-step express path, and a coupon field nobody asked for. The spec still says three steps and no stored cards.

Neither the spec nor the code is lying on purpose. They have simply stopped telling the same story. That gap is spec drift, and once you start looking for it you find it everywhere agents touch a codebase.

What spec drift actually is

Spec drift is the growing distance between the contract you wrote and the behavior that now ships. It is the failure mode spec-driven development exists to prevent, and the one it quietly succumbs to when nobody maintains the contract. The spec described intent at one moment. The code kept moving. Every change that lands without updating the contract widens the gap.

This is not the same as a bad spec. A bad spec is wrong the day you write it. A drifted spec was right and went stale. The difference matters, because a drifted spec is more dangerous than an obviously wrong one. It still looks authoritative. People still cite it in reviews. Agents still read it as ground truth. It has the shape of a contract and the content of a rumor.

The spec is supposed to be the contract between intent and implementation. Drift breaks that contract quietly, without an error message.

Why agents make drift worse, not better

Drift is an old problem. Documentation has rotted since the first README. What changed is the rate.

When a human wrote every line, drift accumulated at human speed. You touched three files a day and roughly remembered what the spec said. Now an agent touches thirty files in an afternoon, and it does not carry your memory of the contract between runs. It carries whatever context you loaded into that session. Agents lose context between sessions by default, so the second run does not know what the first run promised, and neither run updates the document you filed on Monday.

Run several sessions in parallel and it gets worse. Each one is locally reasonable. Each one edges the code a little further from the written contract. Agents will drift whether you plan for it or not, and the spec is usually the first casualty because nothing forces a run to reconcile against it.

The result is familiar: code that is locally correct and globally incoherent. Every diff passed review on its own terms. The system as a whole no longer matches the thing you said you were building.

The four places drift hides

Drift is rarely one big divergence. It is a hundred small ones, and they collect in predictable places.

1. Behavior the spec never mentioned

The agent added a coupon field. It works. It has tests. The spec has no idea it exists. New behavior with no contract is the most common form of drift, and the hardest to notice, because there is nothing in the spec to contradict.

2. Constraints the code quietly broke

The spec said “no stored cards.” Somewhere in a refactor, a caching layer started holding a tokenized card for the session. Nobody decided to violate the constraint. It leaked in through a change that looked unrelated. Broken boundaries are the most expensive drift, because the constraint was usually there for a reason you have now forgotten.

3. Decisions that reversed without a note

Monday’s spec chose optimistic UI updates. Thursday’s bug fix switched to server confirmation because optimistic updates caused a flicker. Good call. But the spec still argues for the abandoned approach, so the next agent that reads it will try to “fix” the code back to the flicker.

4. Acceptance criteria that no longer match

The spec’s acceptance criteria described the three-step flow. The flow is now two steps. The criteria still pass because nobody runs them against the current behavior, or they fail and everyone has learned to ignore that particular red. Either way the contract has stopped meaning anything.

The cost you are already paying

Drift does not send you a bill. It taxes you in slower reviews, agents that confidently build against a stale contract, and the meeting where two people discover they were working from different versions of the truth. It is the substrate under the “I never agreed to that” problem: the spec said one thing, the product does another, and nobody can reconstruct when the two split.

The deeper cost is trust. Once a team learns that the spec might be lying, they stop reading it. And a spec nobody trusts is worse than no spec, because you paid to write it and you still cannot rely on it.

Catching drift before it compounds

You cannot prevent drift. Code changes; that is the point of code. What you can do is shorten the distance between a change landing and the contract catching up.

A few things help in practice:

  • Make the spec cheap to update. If updating the contract is a ceremony, nobody does it under deadline. This is the whole argument for a lightweight spec-driven workflow: a spec small enough that keeping it current is not a project of its own.
  • Reconcile at the end of a run, not the end of a quarter. The moment to update the contract is when the change is fresh, before the next session forgets why it happened.
  • Attach the spec to the code, not to a folder. A contract that lives beside the implementation and the evidence can be checked against reality. A contract that lives in a document you have to remember to open cannot. This is the core idea behind living documentation: docs that update as a byproduct of work instead of a separate chore.
  • Treat the spec as portable, not disposable. A portable specification that carries its intent, decisions, and evidence between tools is far harder to abandon than a Markdown file one agent wrote and the next one ignored.

The direction all of this points is the same: stop treating the spec as a document you write once and start treating it as a living spec that moves with the code.

Where PaellaDoc fits

Spec drift is the problem PaellaDoc is built around. Instead of a spec that sits in a folder while agents reshape the code beside it, the contract lives in a local model connected to the product, the repository, and the evidence from each run. When a change lands, the spec is a place the change can be reconciled against, not a file someone has to remember exists. The point is not to freeze the code. It is to make sure that when the code moves, the story you told about it moves too.

FAQ

Is spec drift the same as technical debt? No. Technical debt is a gap between the code and a healthy design. Spec drift is a gap between the code and the contract you wrote about it. You can have clean code that has drifted badly from its spec, and a messy codebase whose spec is perfectly current.

Can I just delete the spec once the code works? You can, and many people do. The problem shows up later, when a new agent or teammate needs to change the code and there is no record of what must stay true. Deleting a drifted spec removes the symptom and keeps the disease.

How often should a spec be updated? At the rhythm of the work, not the calendar. The useful moment is when a change lands and the reason is still fresh. Batching spec updates for later is how drift accumulates in the first place.