You ask your coding agent for a small feature. It comes back with something that runs, looks right, and also renamed two functions, added a dependency you didn't ask for, and changed how errors are handled in a file you never mentioned. Then you spend the afternoon working out which of those changes were needed.
That pattern has a name now. In the X data we pulled for the week to 25 October, "vibe coding" showed up in 23,089 posts, and several DEV articles that drew discussion that week were about the cleanup: "STOP Shipping Broken Vibe Code", "The Hidden Cost of AI-Generated PRs", "The Verification Trap". Posts about spec-driven development, the most direct fix, were only 405 in the same window.
This article is the short version of that fix: a one-page spec you write before the agent writes code, with requirements precise enough to check and an explicit list of what the agent must not touch. It works with any agent, because it's just a Markdown file.
What spec-driven development actually asks of you
Spec-driven development (SDD) means you write a structured description of what to build before implementation starts, and the agent builds against it instead of against a one-line prompt. The usual loop has four phases: Specify (what and why), Plan (how, technically), Tasks (small, ordered steps) and Implement. Between each phase there is a human review: you read what the agent produced before it moves on.
You don't need a framework to do this, but it helps to see one. GitHub's Spec Kit runs the same idea as agent commands: you create a constitution (standing project principles) once, then for each feature you run specify, plan, tasks, implement and a converge step, "one at a time, reviewing each result before continuing", in the words of its README.
The important part isn't the tooling. It's that the agent stops guessing at scope, because scope is written down where you can both point at it.
The one-page spec
Here is a template you can copy into specs/<feature>.md. Every section is there because it removes one kind of guess.
# Spec: <feature name>
## Goal
One or two sentences: what the user can do after this ships, and why it matters.
## Context
Files, modules and existing behavior the agent must read first.
Link them. Don't paste them.
## In scope
- Bullet list of the changes this spec covers.
## Out of scope
- Things the agent must NOT change, even if they look wrong.
- No new dependencies unless listed here.
## Requirements (EARS)
- R1: When <trigger>, the <system> shall <response>.
- R2: If <unwanted condition>, then the <system> shall <response>.
## Acceptance checks
- A1 (R1): how you will verify it: a test name, a command, a manual step.
## Constraints
Stack, style rules, performance or security limits that apply.
## Open questions
Anything unclear. The agent adds to this list instead of guessing.
Two sections do most of the work against vibe-coding drift.
Out of scope is the one people skip, and it's the one that stops the "while I was there, I also refactored..." changes. If you don't want the agent touching the auth module, say so. If it believes the auth module needs fixing, it can say that in Open questions, and you decide.
Open questions gives the agent a legitimate place to put uncertainty. Without it, an ambiguous requirement turns into a silent assumption in the code. With it, the assumption turns into a line you read during review.
Write requirements an agent can't misread: EARS
Most requirements fail in the same way: "the export should handle errors gracefully" means something different to every reader, human or model. EARS (the Easy Approach to Requirements Syntax) is a small fix for that. It was developed by Alistair Mavin and colleagues at Rolls-Royce while analysing airworthiness regulations for a jet engine's control system, and first published in 2009, according to the official EARS guide.
Every EARS requirement keeps its clauses in the same order, with a few keywords. There are five basic patterns:
| Pattern | Template |
|---|---|
| Ubiquitous (always active) | The <system> shall <response>. |
| State-driven | While <precondition>, the <system> shall <response>. |
| Event-driven | When <trigger>, the <system> shall <response>. |
| Optional feature | Where <feature is included>, the <system> shall <response>. |
| Unwanted behaviour | If <trigger>, then the <system> shall <response>. |
They can also be combined, for example "While ..., when ..., the ... shall ...", which the guide calls complex requirements.
Here's what that looks like on a made-up feature: a CSV export for an orders table.
Before:
Add CSV export to the orders page. Handle errors gracefully and keep it fast.
After:
R1: When the user clicks "Export CSV" on the orders page, the app shall
download a CSV file of the orders matching the current filters.
R2: The CSV export shall include the columns id, date, customer, total, status,
in that order.
R3: If the current filters match zero orders, then the app shall show
"No orders to export" and shall not download a file.
R4: While an export is in progress, the app shall disable the "Export CSV" button.
"Handle errors gracefully" became R3, a specific case with a specific response. "Keep it fast" disappeared, because it wasn't a requirement yet. If speed matters, it goes back in with a number you can check, under Constraints. Each requirement now maps to one acceptance check, which also maps nicely to one test.
Hand the spec to the agent
With the spec written, the conversation with the agent changes. Instead of "add CSV export", you send something like this (an example prompt, adapt it to your agent):
Read specs/orders-csv-export.md and the files listed under Context.
Do not write code yet.
1. Propose a short plan: which files you will change and why.
2. Break it into small tasks, each linked to the requirements it covers.
3. List anything ambiguous under Open questions in the spec.
Stop and wait for my review.
Then work through the loop:
- Review the plan. Check it against Out of scope. A plan that touches a file you didn't expect is the cheapest moment to catch drift.
- Answer the open questions in the spec, not in chat, so the answer survives the session.
- Implement one task at a time. Ask the agent to say which requirement each change satisfies.
- Verify against the acceptance checks, not against "it looks right". Any change that doesn't trace back to a requirement is a candidate for removal.
Keep the spec in the repo next to the code. When the feature changes later, update the spec first, then ask the agent to bring the code in line.
Right-size it
The obvious objection is that this is a lot of ceremony for a typo. It is, so don't do it for a typo. A reasonable split, my own rule of thumb rather than a standard:
- Fast lane: one-line fixes, copy changes, renames. A clear prompt is enough.
- One-page spec: anything that adds behavior a user will notice, touches more than one module, or that you'd struggle to review in a single sitting.
- Full loop with a constitution: new projects or teams where several people and agents work against the same codebase and need shared standing rules.
If you notice you're writing "Out of scope" for every second prompt, that's a sign the work deserves a spec.
Checklist
- Write Goal and Context before you open the agent.
- List what's out of scope, including "no new dependencies".
- Rewrite every vague requirement in one of the five EARS patterns.
- Give every requirement an acceptance check.
- Ask for a plan and tasks first, and review them before any code.
- Keep answers to open questions in the spec file.
- Accept only changes that trace back to a requirement.
Where to go next
If you want the method on a few pages next to your editor, the free Spec-Driven Development Quick-Start Cheat Sheet covers the four-phase loop and its review gates, the anatomy of a spec with a copy-ready skeleton, the five EARS patterns, when to run the full loop and when to take the fast lane, and the three levels of rigor, in 6 pages.
When you're ready for the full reference, Spec-Driven Development: The Complete Guide to the AI Coding Method spends 40 pages and thirteen chapters on every phase in depth, the Constitution, a tool map (GitHub Spec Kit, AWS Kiro, OpenSpec, Tessl, BMAD and more), SDD compared with TDD and plan mode, team adoption, and three worked walkthroughs: a full-rigor feature, a fast-lane fix and a brownfield adoption.
This article was written with the assistance of AI and reviewed by the author.
Top comments (0)