Spec-Driven Development: Let AI Write the Code, Not the Requirements
In the last article of this series I said the first approval gate in an agent pipeline — the one right after the spec — is the step most teams skip first, and pay for last. Here's why skipping it is so expensive, and what actually belongs in that spec.
A vague request isn't a shortcut, it's a delegated decision
A few weeks ago I asked an agent to "add pagination to the list endpoint." Five words, thirty seconds to type.
The agent picked a page size. It picked offset-based pagination instead of cursor-based. It shaped the response the way it thought made sense. None of it was wrong, exactly — it was just not what the frontend team and I had actually discussed two days earlier, in a completely different conversation the agent never saw.
I hadn't saved thirty seconds. I'd just handed a decision to whoever implemented the request, and this time "whoever" was an agent that had no way to know there even was a decision to make.
That's the part people miss about vague prompts: they don't disappear the ambiguity, they just move who resolves it. With a human teammate, that person usually stops and asks. An agent, by default, picks something plausible and keeps going.
A spec is a contract, not paperwork
"Spec" tends to sound like a document nobody reads. In practice, for a task an agent is about to implement, it's closer to a short contract both sides can be held to:
- Goal — what this actually needs to accomplish, not just the surface request.
- Acceptance criteria — the concrete conditions that decide if it's done.
- Edge cases — empty states, limits, concurrent access, whatever "normal" doesn't cover.
- Dependencies — which APIs, modules, or data it touches, and what it shouldn't.
- Constraints — existing patterns, design system components, architectural boundaries it has to respect.
- Out of scope — what this explicitly doesn't include, so nobody "helpfully" expands it.
This is where the context I described in earlier articles stops being general project knowledge and becomes something specific to this one task. The CLAUDE.md tells an agent how the project works. A spec tells it what this particular piece of work is supposed to do.
Let AI write the code, not decide the requirements
This is the line I actually care about, and it's easy to blur.
An agent can absolutely help build the spec — turning a rough request into a structured draft, surfacing edge cases you didn't think of, listing dependencies you forgot were there. That's exactly what a Refiner Agent is for.
What it shouldn't do is quietly resolve an open question on its own and move straight to implementation. If the pagination style wasn't decided, the right output isn't a guess dressed up as code — it's a flagged ambiguity waiting for an answer.
The failure mode isn't "the AI wrote bad code." It's "the AI decided something nobody asked it to decide, and it happened to be wrong."
Edge cases are cheaper on paper than in a diff
Writing "what happens when the list is empty?" as a bullet point takes ten seconds.
Finding out the agent handled it by silently returning null, three files deep in a component nobody thought to check, costs a debugging session and a PR round-trip.
Every edge case you catch while writing the spec is a sentence. Every edge case you catch during review is a conversation, a diff, and usually a re-implementation. The spec isn't extra work bolted onto the "real" task — it's the cheapest point in the whole process to catch a problem.
Tests should trace back to the spec, not to the implementation
There's a subtle trap here: verification that only checks the implementation against itself doesn't verify anything. If the agent writes the code and then writes tests for what the code does, both were shaped by the same set of assumptions.
Tests earn their keep when they're written against the acceptance criteria that existed before the implementation did — not against whatever the code ended up doing. That's the only version of "it passes the tests" that actually means the spec was met, instead of meaning the implementation is internally consistent with its own guesses.
Conclusion
Spec-Driven Development isn't about writing more documents. It's about deciding, on purpose, which questions get answered before code exists and which ones you're comfortable leaving to an agent's best guess.
A good spec doesn't slow the agent down. It's what makes its speed actually useful, instead of fast in a direction nobody asked for.
In the next article of this series I'll make this practical: Refiner, Implementer, Verifier: A Practical Multi-Agent Workflow — how I actually run these as three separate passes instead of three good intentions I try to hold in my head at once.
Until then, the question worth asking before you write the next prompt isn't:
Can the agent figure this out?
It's:
Is there a decision hiding in here that I haven't actually made yet?
If you've got a spec template or a Refiner setup that works well for you, I'd like to see it — feel free to reach out.