Drafting code got cheap about eighteen months ago. Reading it did not, and that is the whole problem in one sentence.

Here is the plain version of what changed. When a model can produce a plausible implementation in seconds, the bottleneck moves from writing to judging, and most teams never moved their process with it. They kept reviewing the code, which now arrives faster than anyone can read it, instead of reviewing the thing that determines whether the code is right in the first place.

Spec-driven development is the correction: you make the specification the artifact you argue about, and the code becomes the output of a decision that was already reviewed.

This is not a definition post, and it is not a claim that nobody has said this before. Birgitta Böckeler took three of these tools apart on Martin Fowler's site and came away sceptical, Thoughtworks has the practice at Assess rather than Adopt, and the sharpest published attack calls the whole thing the waterfall striking back. What follows is the loop we run at 2muchcoffee, why each step survives contact with client work, what it costs, and where those critics are right.

The asymmetry the whole practice exists to manage: the tools get a bigger working memory every release, and the person accountable for the system does not.

The failure spec-driven development exists to prevent: comprehension debt

The failure mode has a name now. Comprehension debt is the gap between how much code exists in your system and how much of it any human genuinely understands. It behaves differently from technical debt, which announces itself: slow builds, a module everyone avoids, the dread of touching one file.

Comprehension debt announces nothing. It produces confident teams and clean dashboards right up until someone has to change a thing nobody on the payroll has read.

The mechanism underneath it has numbers now. Anthropic ran randomised experiments on developers learning an unfamiliar asynchronous library, published as How AI Impacts Skill Formation. The finding, in the paper's own words, is that AI use impairs conceptual understanding, code reading, and debugging abilities, without delivering significant efficiency gains on average. Reading the study, Osmani reports the shape of it: 52 engineers, both groups finishing in about the same time, and the AI-assisted group scoring 50 percent on a comprehension quiz against the control's 67, with the largest declines in debugging.

Read that pairing again, because it is half the argument for changing your process. Same output, same speed, materially less understanding of what was built.

The other half was measured on the codebase rather than the engineer. A team from Carnegie Mellon ran a difference-in-differences study on GitHub projects that adopted Cursor against matched projects that did not, published at MSR 2026 under the title Speed at the Cost of Quality. Their finding: adoption produced a statistically significant, large, but transient increase in development velocity, alongside a substantial and persistent increase in static analysis warnings and code complexity.

Transient speed, persistent complexity. That asymmetry is the thing a process has to answer, because it means the win shows up this quarter and the bill arrives later, charged to whoever is still there.

Same speed, less understanding. The cost never appears in the sprint that created it.

The spec is the artifact now

Here is the mechanic that makes the whole loop work, and it is easy to miss because it sounds like project-management advice rather than engineering.

A model given a vague instruction returns plausible code. Plausible is the expensive kind of wrong, because it passes the glance test and fails the question you have not thought to ask yet. A model given a task whose acceptance criteria and tests already exist returns something you can check against something. The output is not better because the model is smarter. It is better because you moved the ambiguity out of the generation step and into a document a human argued about first.

The specification is not paperwork that precedes the real work. It is the reviewable artifact, and the code is downstream of it.

What you hand the modelWhat comes backWhat you can check it against
A vague instruction: "add rate limiting to the API"A plausible implementation, one of a dozen reasonable interpretations, with the edge cases silently decided for youYour own judgment, applied line by line, at reading speed
A specified task: the limit, the window, the identity it keys on, the behaviour on breach, the tests that prove eachAn implementation that either satisfies the criteria or visibly does notThe criteria, mechanically, before anyone reads a line

The spec-driven loop, step by step

There are tools that will hand you a version of this, and we use none of them. GitHub open-sourced Spec Kit, AWS ships Kiro as an agentic IDE built around specs, and there are lighter open-source takes. Passing on them is not a criticism: our loop predates the tooling and stays deliberately boring, which makes it portable across the models and editors our engineers actually prefer.

If you arrive here from the Spec Kit announcement, the rough mapping is: our grilling step covers the ground of its specify and plan phases, decomposition is its tasks phase, and the build passes below are its implement phase run at a smaller grain. The difference that matters is the unit. Spec Kit's flow runs once per feature; ours runs once per task, so no single document ever grows big enough to be skimmed instead of read.

Five stages. None of them is clever.

1. Interrogate the idea before anything is built

What it is for, what it must not do, what happens at the edges. We grill the idea, and the output is a document rather than a chat log. Most projects fail in the gap between what someone asked for and what they meant, and this is the step that closes that gap while closing it is still cheap.

2. Decompose the document into tasks with tests

What "done" means is settled before a line is written. This is the step that makes AI output reviewable at all, for the reason in the table above.

Here is the shape of one, using the rate-limiting task from the table. The details are illustrative; the size and the structure are what our task specs look like when they leave this step.

ONE TASK SPEC, WHOLE
Task: rate limit the public quote endpoint
Why: unauthenticated clients can exhaust it and starve paying traffic

Behaviour
- 100 requests per key per rolling 60-second window
- Unauthenticated requests key on client IP instead
- On breach: 429 with a Retry-After header, request body not echoed
- Limits live in config, not in constants

Out of scope
- Per-plan tiers (own task, depends on this one)
- Admin endpoints (already gated elsewhere)

Tests
- 100th request inside the window passes, the 101st gets a 429
- Retry-After matches the remainder of the window
- Two different keys never share a bucket
- A request 61 seconds after the first one passes again

That is the whole artifact. It fits on one screen, and a task spec that will not fit on one screen is nearly always two tasks wearing one name. When people ask what "tests written alongside" literally means, it means the Tests block above exists before any implementation does.

The first thing the build pass produces is those four lines as runnable tests, and the increment is not done until they pass. The model can draft them; a person reads every one, because the tests are the one part of the spec an agent cannot quietly reinterpret.

3. Build in small passes, not one large generation

We call it a ralph loop internally, which just means the same short cycle repeats: take the next task, produce a small increment, land it against its tests, repeat. Large one-shot generations are how teams end up with a codebase nobody understands, including the people who shipped it.

4. Review twice, once by a model and once by a person

Reading is the bottleneck once drafting is cheap, so reading is where senior time goes now: every line that reaches a client's main branch has been read and owned by a named engineer.

5. A person owns the calls that matter

Architecture, security boundaries, data models, anything touching money or patient data. The tooling drafts, refactors, writes migrations, fills in tests. It does not get the final say, and a team claiming otherwise has not hit the failure modes yet.

What spec-driven development costs

The honest part, because the pages ranking above this one tend to skip it.

Specs take real time up front, and that time is visible while the savings are not. You are asking a team to spend Monday writing a document instead of shipping something demonstrable, against a competitor who will show the client a working screen on Monday afternoon. The document does not demo.

The second cost is that specs rot, and this one is not just our experience. An arXiv write-up of the practice names specification rot as a standard pitfall: the spec drifts from reality, loses its value as documentation, and erodes trust.

The underlying human behaviour has been measured for decades. In a survey published in IEEE Software in 2003, Lethbridge, Singer and Forward asked engineers whether documentation is always outdated relative to the current state of a software system. Of the 45 who answered, 44 percent somewhat agreed and another 24 percent strongly agreed. Small sample, twenty-three years old, and still the most quoted number in the field because nobody who has worked in software finds it surprising.

What changes now is the consequence. A stale document used to mislead the next engineer who happened to read it, and engineers are good at noticing that a doc looks wrong. A stale spec misleads an agent that has no such instinct, which will execute the outdated plan confidently and flag nothing.

The third cost is cultural. Engineers who joined to build things do not always want to spend a morning arguing about acceptance criteria, and the resistance is legitimate rather than lazy: they are weighing the cost of the artifact against what it will actually be worth.

Cat Hicks, a learning scientist who studies developers, reads that resistance as a symptom rather than a character flaw: in her framing, comprehension debt is not caused by bad judgement about AI but by overproduction pressure, organizational conditions under which slow, understanding-building work is hard to justify. The counter-argument is not motivational, it is arithmetic: the alternative is reading generated code at the rate a model produces it, forever, which nobody can do either.

We have not measured our own speed or cost effect from this, and I am not going to invent a number. What I can say is which failure stopped happening: the one where a change request arrives and nobody on the team can explain why the system does what it does.

Where the critics are right

The strongest argument against all of this is that it is waterfall wearing new clothes, and it deserves a straight answer rather than a dismissal.

François Zaninotto makes the case most sharply: big design up front failed for a reason, software development is non-deterministic so planning does not remove uncertainty, and the practice turns developers into reviewers of markdown. Böckeler's version is quieter and lands harder, because she actually ran the tools: the generated documents were verbose and tedious to review, and the agent frequently did not follow all the instructions anyway. Structure that produces ceremony without producing control is worse than no structure, since now you are paying for both.

The agent ignoring the spec deserves its own paragraph, because every practitioner hits it and most write-ups stop at observing it. It happens in our loop too, and the handling is dull on purpose. A pass is small, so a divergence is cheap to spot, and the tests in the task spec fail loudly when the model has built something adjacent to what was asked.

When that happens we throw the pass away and rerun it rather than argue the code back into shape, because a fresh pass against a sharper spec is cheaper than a negotiation with a diff. And when the same task drifts twice, the spec is treated as the defect. Ambiguity in, adjacent code out. The fix goes into the document, not into the code review.

Where they are right, in our experience: the failure mode is real, and it arrives the moment the spec becomes a deliverable rather than a decision. A four-line bug does not need a requirements document. A tool that turns a one-line fix into sixteen acceptance criteria has not made anyone safer, it has just moved the mess.

Where our loop diverges is the unit. We do not specify a system up front; we specify one task, immediately before it is built, and the loop repeats. That is the difference between a plan signed off in March and a decision made on Tuesday about the thing being built on Tuesday. The work stays spec driven without the spec ever growing past the task it describes.

The small unit is the only honest answer we have to the rot problem, and the Lethbridge survey is the reason we believe it.

The same survey that found engineers agree documentation is always out of date also found which documentation survives: bug-tracking notes, code comments and test cases stay current because each one is short, sits right next to the work, and has obvious operational value. Specifications and requirements rot because they are big, complex and structurally varied, so engineers judge updating them to be a poor use of time.

Their conclusion was that engineers are not lazy, they are making a value judgement, and it is usually correct. A spec small enough to sit beside the task it describes is on the right side of that judgement. A spec that describes the system is not.

The part of the critique we have not solved is the maintenance one. Amelia Wattenberger's framing is that every documentation-first initiative in software has failed because it asked humans to do continuous work nobody sees and nobody rewards. That is a fair description of the risk we are carrying. Our answer so far is to keep specs small and close to the work rather than to claim discipline will hold.

When the product moves money

If the product moves money or makes decisions people can challenge, this stops being a preference. A generated implementation that is merely plausible is a real liability once it is deciding balances, which is why correctness in AI-built money apps got its own piece: the same discipline pointed at ledgers and reconciliation.

The same loop is the structural answer to the vibe coding 80 percent problem: generated code gets you most of the way quickly and the last stretch is where the cost lives. A spec does not remove that stretch. It makes it visible before you have committed to an architecture that assumed it away.

One concrete action

Pick the last feature your team shipped with heavy AI assistance and ask one person who did not write it to explain, from the repo alone, why it works the way it does. Not whether it works. Why it is shaped that way. If the honest answer is that they would have to reconstruct the reasoning by reading the diff, you have found your comprehension gap, and the spec is the cheapest place to close it.

How 2muchcoffee runs this loop for clients

We build production software with this loop, and the part clients notice is not the speed, it is that they can ask why the system does something and get an answer. If your team is shipping faster than it can explain what it shipped, or the one-question test above just failed, that is the conversation worth having before the next quarter's changes land.

The plain way in is the AI and engineering work we do.

FAQ

What is spec-driven development? An approach where the specification, rather than the code, is the artifact the team reviews and argues about. The document defines behaviour, edge cases, and acceptance criteria before implementation, so the generated code can be checked mechanically against agreed criteria instead of judged line by line at reading speed.

Why does it matter more now than it did five years ago? Because drafting code became cheap and reading it did not. When a model produces a plausible implementation in seconds, the bottleneck moves from writing to judging. Reviewing code that arrives faster than anyone can read it does not scale; reviewing the decision that produced it does.

Is this just waterfall with a new name? No. Waterfall specifies the whole system before any of it is built. This specifies one task at a time, immediately before that task is built, and the loop repeats in small passes. The unit is a task with its tests, not a project document signed off months ahead.

What is comprehension debt? The gap between how much code exists in a system and how much of it any human genuinely understands. Unlike technical debt, it produces no friction to warn you. It surfaces when something has to change and nobody available has read the part that needs changing.

Does using AI to write code make a team worse at understanding it? An Anthropic randomised trial of 52 engineers found the AI-assisted group finished in about the same time and scored 50 percent on a comprehension quiz versus 67 for the control, with the largest declines in debugging. The researchers noted passive delegation impairs skill formation far more than question-driven use.

How is spec-driven development different from vibe coding? They are opposites in where the judgement happens. Vibe coding accepts generated output and checks it by running it. Spec-driven development decides what correct means before generating, so the output can be checked against agreed criteria rather than against whether it happened to work when you tried it.

Is spec-driven development just BDD with a new name? The criticism is fair and has been made by name. Behaviour-driven development already put agreed examples before implementation. What changed is who consumes them: acceptance criteria now constrain a generator that will otherwise pick one of a dozen readings, not just a human who would have asked.

What tools support spec-driven development? None of them is required. A written task with acceptance criteria and tests works in any editor, which is how we run the practice ourselves. We read all five, Spec Kit, Kiro, OpenSpec, BMAD and Tessl, from their own docs and changelogs in the tools piece.

What does spec-driven development actually cost? Time up front that is visible while the savings are not, ongoing effort to keep specs current as the system changes, and cultural resistance from engineers who would rather build than write acceptance criteria. The alternative cost is reading generated code at the rate a model produces it.

DM
Dmytro MelnychenkoCEO and CTO at 2muchcoffee, leading the agency's AI transformation and its engineering practice