Ask a team why a piece of software behaves the way it does, and watch where they go to find out. They read the code. They read the tests, if the tests describe intent rather than implementation. They search the ticket tracker for the change that introduced the behaviour, then the discussion behind the change, then the person who remembers the discussion. Often the answer is only reassembled in a meeting, held for as long as the meeting lasts, and reconstructed again the next time someone asks.
This reconstruction is a recurring tax. Code can be correct, readable and well-tested and still be a poor representation of the business behaviour distributed across it. A pricing rule lives in one service, its exception in another, its deliberate omission nowhere at all — a decision not to do something rarely leaves a trace in code. The system executes the accepted behaviour faithfully without ever stating it, so every change that touches the area pays to rediscover it.
Coding agents change the economics of that tax in both directions. They can make reconstruction faster — an agent can scan more of a system in the time I would spend orienting myself — but they also make it possible to re-enter the same area more often as attempted changes get cheaper. Each attempt still has to re-derive the same intent. And an agent reading behaviour out of code is inferring what the system does, not consulting what the team decided it should do. Where those differ — an old compromise, or a bug customers now depend on — it cannot tell an intended rule from an accidental one.
This is the gap a specification is meant to close: a place where the accepted behaviour of the system is stated, kept current, and consulted before it is changed. Most of what the industry now calls a specification cannot close it, because it does not last long enough to be consulted twice. A specification only earns the name if later work has to answer to it; the rest is a plan wearing the word. And the reason the distinction is worth an argument is not that a durable specification makes software more correct — I do not think it does — but that it lets a team do a few old things markedly better.
A specification that disappears with the task is a plan
Here is the boundary stated plainly. A specification is a persistent, structured model of the system’s accepted behaviour, which later work deliberately changes. An artefact scoped to a single task is a plan — however detailed, however carefully reviewed, and even when it is committed to the repository.
The test is lifecycle, not detail or storage. A plan is created for a task, consumed as the task is done, and then spent. It may be consulted later as a historical record, but later work is not expected to update it as the account of current behaviour. A specification is not read once and set aside; work proposes a change to it, and the accepted change becomes part of it. A plan is written against your understanding of the system; a specification is the recorded understanding, and the next plan is written against that.
Two workflows readers already know sit cleanly on the plan side of that line. Vibe coding keeps the running code and discards everything else — the prompt was scaffolding, the code is what survives. An agent’s plan mode goes one better and writes the plan down, sometimes commits it, but it is still scoped to the task in front of it; once the change lands, the team does not maintain it as current behaviour. Neither is a lesser practice — a good plan is valuable, a committed one a useful record. But saving a plan does not make it a model of the system; it makes it an artefact of one change.
You might suspect I am inventing a distinction the field already respects. It does not. The tools flying the specification-driven banner produce a specification-like file before code and then agree on almost nothing else, least of all how long that file is meant to live. At one extreme the file is an input to a delivery cycle rather than a maintained model of the system. GSD Core persists a great deal of state across sessions and commits a SPEC.md for a phase, but completing a milestone archives its requirements and removes the current requirements file before the next milestone creates a new one. Harper Reed’s much-copied workflow likewise commits a spec.md and then uses it as scaffolding, not as a standing account of the system.12 At the other extreme the file is the source — the human edits the spec and the code is regenerated output, marked not to be touched by hand — which is the doctrine GitHub’s Spec Kit states without hedging: “code serves specifications,” implementation as “the continuously regenerated output.”3 In between sit the durable-but-partial living documents, held per feature in Kiro or merged into a single canonical tree in OpenSpec.45 Every one produces a file before code; what separates them is exactly the test above — whether later work must maintain it as the account of current behaviour.
Birgitta Böckeler has mapped this range more carefully than a paragraph can, naming its rungs spec-first, spec-anchored and spec-as-source.6 I lean on her map but part company with it in one deliberate way: she describes the territory and declines to crown a winner, treating even throwaway spec-first as legitimate. I am privileging a single rung — the persistent one — as holding real and under-appreciated power. That is a narrowing she chose not to make, so I am filling a gap she left open rather than overturning a consensus.
One qualification keeps this from overreaching. The rung I am defending is not spec-as-source. The code still lives, is still read, and is still edited by hand; the specification is the account of accepted behaviour, not the sole editable origin of the software. That changes the failure surface rather than removing it. The cautions about ceding software design to a model that generates the code land most directly at the top of the ladder. The drift objection does not: keeping two editable artefacts makes it central to the rung I am defending, and I return to it below. The recent case for still designing code for humans supports keeping code in human hands; it cannot tell us whether maintaining the parallel behavioural model will pay for itself.7
The power is not correctness. It is doing XP better
It would be easy to read all this as a bet on correctness — that a durable specification makes the software more likely to be right. I do not think the evidence supports that, and I am not going to argue it. Nothing here forces the running system to match the specification over time; keeping software honest to its specification is a hard problem, and a separate one, for another article.
The opportunity in the persistent model is less obvious: it can strengthen two Extreme Programming practices, precisely because it persists.
Persistence can enable less upfront specification. This sounds backwards. Without a canonical model, a change may have to re-establish the accepted behaviour around it from code, tests and memory. A standing model creates a different possibility: when a small request is interpreted against behaviour that is already stated, only the delta has to be elaborated; the rest is already on the page.
The obvious objection is that the standing model is itself a large specification, so the cost has only moved. Once upkeep is counted, it may have. The narrower point is about when the detail is created: the model accretes one delta at a time, each the just-in-time elaboration of a small slice, so its eventual density was paid for as real work demanded it — never as a batch written ahead of the code. That is the difference from an upfront requirements document: not that the model is smaller or cheaper in total, but that no one had to write it whole before implementation began.
This is Ron Jeffries’ Card, Conversation, Confirmation read forward twenty-five years.8 The card was a placeholder; the requirement was elaborated just-in-time, in conversation; the confirmation was the acceptance criteria. The specification is the modern analogue of Confirmation — a durable record of what “done” was agreed to mean — not a return to a heavyweight requirements document written before the work begins. It is the just-in-time, small-slice branch of the family tree, not the upfront-batch branch.
It evolves pairing past engineer-to-engineer to engineer-and-product. I have argued before that pairing may be moving earlier — towards premises, examples and specifications — because, in my experience, the costliest mistake is usually not a poor implementation but a plausible implementation of a mistaken premise. The specification delta is where that becomes concrete: proposing a reviewed change to accepted behaviour is a repeatable surface where product understanding flows in and wrong assumptions surface before cheap code generation turns them into expensive rework. This one is an observation, not a measurement: I have started to see product managers pair, and mob, with engineers while an agent drafts a specification change — not to review code but to interrogate intent while it is still cheap to change. The product manager leaves with a sharper grip on their own technical product, taken from a stated model rather than reconstructed from code.
Neither is a correctness claim; both are claims about intent and collaboration, and together they are the whole case for the persistent model. Its value does not depend on being the sole editable source from which code is generated, or on covering all behaviour. It does depend on being authoritative within the behaviour it describes.
Walking the loop with a concrete change
The two gains rest on a single mechanism, and it is easiest to watch it turn on something small. A product manager asks for one thing: let a first-order welcome discount be combined with account credit. One line. The agent interprets it against the current specification, and the request lands in a dense corner of it — a welcome discount applies once per customer, to the subtotal, before delivery; account credit applies afterwards, to the remaining balance and is earned only on cash paid; and, stated flatly, at most one promotional discount applies to an order.
That last rule is where the code cannot settle the question. Code and tests may reveal how the terms are represented and show that the two have never combined; they cannot decide whether someone intended that separation. Is account credit a promotional discount, caught by the rule, or a payment instrument the rule never meant to cover? The specification is silent, and silence reads the same whether it was a decision or an oversight. So the agent does not guess. It surfaces the questions a human has to answer: is the single-discount rule policy or omission; does the welcome discount still compute against the full subtotal once credit is in play; can the pair drive the balance below zero, and who absorbs the remainder; and, since credit can be earned repeatedly while the welcome discount is once-per-customer, whether combining them opens a farming loop.
A person settles them, and the decisions are about intent, not implementation. Account credit is a payment instrument; the single-discount rule was silent on it, not opposed — the omission was accidental. The welcome discount keeps computing against the subtotal, and credit still applies to what remains, capped at the balance. An existing rule earns credit only on cash paid, so the combination creates no new farming loop. Refunds and partial cancellation are named as unchanged and deliberately left alone.
What merges back is not that reasoning, held in someone’s memory until the next argument, but a single typed change to the model — the modification and the addition side by side, ready to review. OpenSpec writes it as a delta against the current specification:5
## MODIFIED Requirements
### Requirement: One promotional discount per order
The system SHALL apply at most one promotional discount to an order. This
limit governs promotions only; payment instruments such as account credit
are exempt and do not count against it.
#### Scenario: Welcome discount combined with account credit
- WHEN a first-order customer holds both a welcome discount and account credit
- THEN the welcome discount applies to the subtotal
- AND account credit applies to the remaining balance, capped at that balance
## ADDED Requirements
### Requirement: Account credit stacks after a promotional discount
The system SHALL deduct account credit from the balance that remains after any
promotional discount, and SHALL NOT allow the combined reduction to exceed the
order total.
The delta stays atomic — one rule clarified, one interaction recorded; the moment it began rewriting refunds and cancellation too would be the signal the request had left its small slice.
Now hold the two lifecycles side by side. A plan would have said add credit to the allowed-stacking check and ship. The rationale might survive in a ticket, test name or historical plan, but it would have no maintained home as current policy; the next person to touch promotions would have to find and interpret it before deciding whether to “fix” the apparent stacking bug. The delta records that decision as the accepted rule. The request stayed small, the existing specification carried the density around it, and the artefact is a change to the model, not a document about the task. That difference in lifecycle is what turns the two gains above from hopeful description into something a team can run.
I have watched this fail before
The objection I take most seriously is not one I have to borrow from a critic. I share it. I have maintained a long-lived requirements document that was quietly false within a quarter — everyone had stopped reading it, and no one had told it — and nothing about my calling this new artefact a specification makes it immune.
That failure has two distinct shapes, and they are worth separating, because the practice can mitigate one and not answer the other.
The first is rigidity. A specification agreed before the code is a blueprint written at the moment of least knowledge, and implementation is where its mistaken assumptions surface. Held too firmly, it forces the system to honour decisions that turned out to be wrong.
The second is decay. Documents drift against the system they describe unless someone pays, continually and deliberately, to keep them current — and requirements written this way turn out to be inappropriate almost as soon as they are written.910 A persisted natural-language model is a standing invitation to do it again: to maintain a second, incomplete representation of the software that quietly stops being true.
There is a sharper edge to the second worry, aimed at natural language itself. Prose is a lossy medium for behaviour. It skips the intermediate abstractions that make large-scale engineering possible, and machine-recovered traceability from it is good enough to assist a human, not to trust unattended.11 Push a specification precise enough to pin an agent down and it stops being prose in any useful sense — it becomes, in one recent phrasing, “programming in a worse, more ambiguous programming language,” echoing Dijkstra’s 1978 objection to natural-language programming.7 Model-driven development encountered the same problems — round-tripping, drift, the cost of formalism — which is why even its advocates keep a catalogue of why it fails.12 The warning that spec-as-source courts “the downsides of both MDD and LLMs: inflexibility and non-determinism” restates a very old worry.6
And underneath both shapes the pragmatists have a pointed question. If specifications are prompts, and the code is what survives, why maintain a second artefact at all? Its upkeep may simply cost more than it returns, especially while nothing forces the running system to conform to it.
I can mitigate the first shape and not answer the second. Each delta is created just-in-time for a small vertical slice and stays cheap to revise as the team learns. A specification agreed before code is not a finished blueprint but, in Unmesh Joshi’s phrase, “a hypothesis to be revised.”13 The loop works because each round is a small, reviewable change, not a standing document defended against reality. That reduces rigidity; it does not prevent a team from turning the model into a gate that resists new knowledge.
Decay I cannot answer, and I will not pretend to. Nothing in the workflows examined forces a persisted prose model to remain faithful to the running system over time, and a model that accretes cheap deltas while no one re-validates the whole is exactly how a large, quietly-false document forms. That is the real weight of the pragmatists’ question, and it stays open here. Kent Beck’s instinct is worth sitting with: in augmented coding his durable, trusted artefact is the test suite — an executable specification — alongside a task-scoped plan, not a persistent prose model.14 Whether the durable artefact should be prose or executable, and whether something like BDD is the layer that makes it so, is the tension the next article takes up — not one I am claiming to have solved here.
Adopting it on software that already exists
Most teams are not starting from an empty repository, and neither route into an existing system is free. You can reverse-engineer a baseline — have an agent infer the accepted behaviour from the code and tests, then validate it with people who can distinguish intent from accident. The inferred baseline describes what the system does, bugs and dead compromises included. A baseline nobody has validated is not a specification; it is the reconstruction tax paid once and then trusted, which is worse than paying it each time.
Or you can grow coverage incrementally, letting the specification accrete around the areas you actually change. This spreads the cost and keeps it attached to real work, but a request that looks like a new requirement over existing code may in fact contradict accepted behaviour the specification does not yet describe. Treat those as higher-risk and route them to human judgement, because the model cannot yet warn you that you are changing something rather than adding it. Neither route removes the reconstruction tax; both convert it into an explicit, reviewable cost.
Where this is still moving
The claim is narrow: a persistent model can let a small change elaborate only its delta and can give product and engineering a shared surface for reviewing intent. That does not establish that the software will be more correct or that the model will cost less than it returns.
The right granularity is unknown — how much behaviour one specification should hold before it becomes the requirements document the drift literature warns about. Repository and ownership boundaries are unknown; OpenSpec’s early cross-repository work shows the boundary being pushed, not that broad, shared specifications work.5 The context economics are unknown — a specification dense enough to be useful is also expensive to keep in an agent’s working context. And conformance over time — whether the running system still matches what the model says — is the largest open problem of all, the one this article has deliberately left standing.
The persistent model earns its keep only while it stays cheap to change and tethered to reality. That is the tension this whole lineage has circled, from XP’s Confirmation through living documentation to agentic specifications — not one the tools have newly solved, but the one worth watching.
So the next time a tool says it has written a specification, do not judge it by the filename or the detail inside. Ask what happens after the code lands. Will the artefact become a historical record, or will the next change have to consult and revise it as the account of accepted behaviour?
If it becomes history, it may still be an excellent plan. If it persists, the team has made the harder bargain: in return for a place where product and engineering can examine intent before implementation, it accepts an obligation to keep that place authoritative where it speaks and honest as the system changes.
The file does not make it a specification. The obligation carried into the next change does.
References
Open GSD, GSD Core,
spec-phase.mdandcomplete-milestone.md, checked 6 August 2026. https://github.com/open-gsd/gsd-core/blob/fee72d55604461b86176ee970357f358e8b05cc9/commands/gsd/spec-phase.md and https://github.com/open-gsd/gsd-core/blob/fee72d55604461b86176ee970357f358e8b05cc9/commands/gsd/complete-milestone.md. ↩︎Harper Reed, “My LLM codegen workflow atm,” 2025. https://harper.blog/2025/02/16/my-llm-codegen-workflow-atm/ ↩︎
GitHub, Spec Kit,
spec-driven.md. https://github.com/github/spec-kit/blob/main/spec-driven.md ↩︎Amazon Web Services, Kiro documentation, “Feature specs.” https://kiro.dev/docs/specs/feature-specs/ ↩︎
Fission-AI, OpenSpec,
docs/concepts.mdand the Stores beta user guide, checked 6 August 2026. https://github.com/Fission-AI/OpenSpec/blob/d57889664cab4f2f061d236ec3ff82a5578701bb/docs/concepts.md and https://github.com/Fission-AI/OpenSpec/blob/d57889664cab4f2f061d236ec3ff82a5578701bb/docs/stores-beta/user-guide.md. ↩︎ ↩︎ ↩︎Birgitta Böckeler, “Understanding Spec-Driven Development: Kiro, spec-kit, and Tessl,” martinfowler.com, 15 October 2025. https://martinfowler.com/articles/exploring-gen-ai/sdd-3-tools.html ↩︎ ↩︎
Valentina Servile, “Should we still design code for humans?” Thoughtworks, 23 July 2026, citing Edsger W. Dijkstra, “On the foolishness of ’natural language programming’” (EWD667), 1978. https://www.thoughtworks.com/insights/blog/programming-languages/should-still-design-code-humans ↩︎ ↩︎
Ron Jeffries, “Essential XP: Card, Conversation, Confirmation,” 2001. https://ronjeffries.com/xprog/articles/expcardconversationconfirmation/ ↩︎
David L. Parnas, “Software Aging,” Proceedings of the 16th International Conference on Software Engineering, 1994. https://doi.org/10.1109/ICSE.1994.296790 ↩︎
Krzysztof Wnuk, Tony Gorschek and Showayb Zahda, “Obsolete software requirements,” Information and Software Technology 55(6), 2013, pp. 921–940. https://doi.org/10.1016/j.infsof.2012.12.001 ↩︎
Jan-Philipp Steghöfer and Markus Borg, “An Abstraction Is Worth a Thousand Vibes,” IEEE Software 43(1), 2026, pp. 13–16. https://doi.org/10.1109/MS.2025.3621786 ↩︎
Johan den Haan, “8 Reasons Why Model-Driven Approaches (will) Fail,” InfoQ, 2008. https://www.infoq.com/articles/8-reasons-why-MDE-fails/ ↩︎
Martin Fowler and Unmesh Joshi, “Conversation: LLMs and Building Abstractions,” martinfowler.com, 26 August 2025 — the “Upfront Specification Impossibility” concept appears within. https://martinfowler.com/articles/convo-llm-abstractions.html ↩︎
Kent Beck, “Augmented Coding: Beyond the Vibes,” Tidy First? Substack, 2025. https://newsletter.kentbeck.com/p/augmented-coding-beyond-the-vibes ↩︎