AI Tools, Vibe Coding
Spec-Driven Development: How to Make an AI Coding Agent Build the Right Thing

Spec-driven development is a workflow where you write a precise specification first, then have an AI coding agent generate the plan, the tasks, and the code from that spec — instead of prompting the agent freehand and correcting it turn by turn. The spec becomes the source of truth the agent keeps returning to, so its output is judged against something concrete rather than against whatever you meant in the moment. This guide covers what spec-driven development actually is, why freeform prompting breaks down on real features, and how to run the spec → plan → tasks → implement loop on a Next.js / npm project without it collapsing into reinvented waterfall.
Join the Vibe Coding Community
Swap builds, trade feedback, and ship faster alongside other people vibe coding.
The One-Line Answer
Spec-driven development (SDD) is a process where a written specification — the "what" and "why" of a feature — drives everything downstream: the AI agent turns the spec into a technical plan, the plan into ordered tasks, and the tasks into code, checking each step back against the spec. The spec is the artifact you version and review; the code is a build output of it.
The shift is where the intent lives. In freeform prompting, intent lives in a scroll of chat messages that no one can review and the agent half-remembers. In spec-driven development, intent lives in a file — one you can read, diff, and correct before a single line of code gets written.
The shift is where the intent lives. In freeform prompting, intent lives in a scroll of chat messages that no one can review and the agent half-remembers. In spec-driven development, intent lives in a file — one you can read, diff, and correct before a single line of code gets written.
The Problem SDD Solves
Hand an AI coding agent a one-line prompt — "add team invitations" — and it will confidently build a thing. Whether it's your thing is a coin flip. It guesses at the states you didn't mention (pending invites, revoked invites, re-invites), picks an email flow you never specified, and invents acceptance criteria you'll only discover are wrong in review.
The failure isn't the model. It's that the agent optimized against an underspecified target. You compensate by steering: a correction, another correction, a "no, not like that," each one buried in chat history where the next session can't see it. On a small change this is tolerable. On a real feature it produces code that technically runs and quietly does the wrong thing — the class of bug where nothing throws an exception but the behavior doesn't match what anyone intended.
Spec-driven development moves the argument earlier, to a point where changing your mind is cheap. You fight about the spec — in prose, in a file — before the agent writes code, when a fix is a one-line edit instead of a refactor.
The failure isn't the model. It's that the agent optimized against an underspecified target. You compensate by steering: a correction, another correction, a "no, not like that," each one buried in chat history where the next session can't see it. On a small change this is tolerable. On a real feature it produces code that technically runs and quietly does the wrong thing — the class of bug where nothing throws an exception but the behavior doesn't match what anyone intended.
Spec-driven development moves the argument earlier, to a point where changing your mind is cheap. You fight about the spec — in prose, in a file — before the agent writes code, when a fix is a one-line edit instead of a refactor.
The Core Loop: Spec → Plan → Tasks → Implement
The workflow that has crystallized around SDD — and what GitHub's open-source Spec Kit formalizes for agents like Claude Code, Copilot, and Gemini CLI — runs in four stages, each producing a durable artifact instead of a disposable chat turn.
1. Specify — capture the what and why
Write what the feature does and who it's for, deliberately leaving out the tech stack. For team invitations: who can invite, what states an invitation moves through, what the invitee sees, what happens on expiry or revocation, and the acceptance criteria that make it "done." This is where you do the thinking the agent otherwise papers over. A good spec names the edge cases explicitly — because an edge case you don't write down is an edge case the agent will invent an answer to.
2. Plan — turn the spec into a technical approach
Now introduce the stack. Given the spec, the agent proposes how: the routes, the data model, the Next.js server actions or API routes, the migration, the packages. In a Next.js / npm project this is where "invitations" becomes a concrete shape — a
3. Tasks — break the plan into an ordered checklist
The plan becomes a dependency-ordered task list: schema before the mutation, mutation before the UI, tests before the implementation they cover. The ordering is what keeps an agent from doing steps three and five and leaving two half-built. A well-formed task list gives the agent an unambiguous "what's next" and gives you a place to see progress that isn't a wall of diffs.
4. Implement — let the agent build against the checklist
Only now does the agent write code, one task at a time, checking each against the spec's acceptance criteria. Because the criteria are written down, "done" is a fact the agent can verify rather than an opinion it asserts. When it finishes a task, you review a small, scoped change that traces directly back to a line in the spec — not a 600-line PR you have to reverse-engineer.
1. Specify — capture the what and why
Write what the feature does and who it's for, deliberately leaving out the tech stack. For team invitations: who can invite, what states an invitation moves through, what the invitee sees, what happens on expiry or revocation, and the acceptance criteria that make it "done." This is where you do the thinking the agent otherwise papers over. A good spec names the edge cases explicitly — because an edge case you don't write down is an edge case the agent will invent an answer to.
2. Plan — turn the spec into a technical approach
Now introduce the stack. Given the spec, the agent proposes how: the routes, the data model, the Next.js server actions or API routes, the migration, the packages. In a Next.js / npm project this is where "invitations" becomes a concrete shape — a
team_invitations table, a /invite/[token] route, an email send in a server action. You review the plan against the spec before any code exists. If the plan contradicts the spec, you caught a bug for the price of reading a page of prose.3. Tasks — break the plan into an ordered checklist
The plan becomes a dependency-ordered task list: schema before the mutation, mutation before the UI, tests before the implementation they cover. The ordering is what keeps an agent from doing steps three and five and leaving two half-built. A well-formed task list gives the agent an unambiguous "what's next" and gives you a place to see progress that isn't a wall of diffs.
4. Implement — let the agent build against the checklist
Only now does the agent write code, one task at a time, checking each against the spec's acceptance criteria. Because the criteria are written down, "done" is a fact the agent can verify rather than an opinion it asserts. When it finishes a task, you review a small, scoped change that traces directly back to a line in the spec — not a 600-line PR you have to reverse-engineer.
How a Spec Is Different from a PRD
A spec and a PRD overlap enough that people use the words interchangeably, but they answer different questions for different readers. A product requirements document is written for humans — product, design, engineering, stakeholders — to align on why you're building something, who it's for, and what success looks like. It lives at the level of user problems, business goals, and scope. A spec in spec-driven development is written to be executed by an agent: it's precise enough that a plan, a task list, and code can be generated from it without a human filling in the gaps.
The practical differences:
• Audience. A PRD persuades and aligns people. A spec instructs a machine. A PRD can get away with "the flow should feel fast"; a spec has to say "route responds in under 200ms," because the agent can't act on a feeling.
• Precision. A PRD tolerates open questions and "TBD." A spec treats an unspecified case as a defect, because the agent will resolve the ambiguity itself — usually wrong.
• Scope per document. A PRD often spans a whole product or a major initiative. A spec is typically one feature or change, sized so an agent can implement it in one bounded pass.
• Lifecycle. A PRD is mostly authored up front and referenced later. A spec is a living artifact you edit as you build, then regenerate the plan and tasks from.
The two aren't rivals — they stack. The PRD sets the direction; the spec is how you hand a slice of that direction to an agent in a form it can actually build. If you're writing the upstream document, our in-depth guide to writing a PRD for vibe coders covers that layer; the spec is the step that turns a section of it into executable work.
The practical differences:
• Audience. A PRD persuades and aligns people. A spec instructs a machine. A PRD can get away with "the flow should feel fast"; a spec has to say "route responds in under 200ms," because the agent can't act on a feeling.
• Precision. A PRD tolerates open questions and "TBD." A spec treats an unspecified case as a defect, because the agent will resolve the ambiguity itself — usually wrong.
• Scope per document. A PRD often spans a whole product or a major initiative. A spec is typically one feature or change, sized so an agent can implement it in one bounded pass.
• Lifecycle. A PRD is mostly authored up front and referenced later. A spec is a living artifact you edit as you build, then regenerate the plan and tasks from.
The two aren't rivals — they stack. The PRD sets the direction; the spec is how you hand a slice of that direction to an agent in a form it can actually build. If you're writing the upstream document, our in-depth guide to writing a PRD for vibe coders covers that layer; the spec is the step that turns a section of it into executable work.
Spec-Driven Development on a Next.js / npm Project
Concretely, an SDD pass on a Next.js repo produces a set of files that live in the repo, next to the code they govern:
A few things make this work in practice rather than in theory:
• Keep the spec stack-free until the plan. Mixing "users can revoke an invite" with "use a Postgres trigger" in the same document means you can't change your implementation without re-litigating the requirement. Separate the what from the how and each stays reviewable on its own.
• Write acceptance criteria as things you can check. "Invitation expires after 7 days" should map to a test, not a vibe. Criteria the agent can execute against are criteria that close the loop by themselves — the same principle behind test-first agent tasks.
• Give the agent your house rules once. Framework version, folder conventions, the patterns you already use — put them where the agent reads them (an
• Version the spec, review it like code. A spec in a chat window is gone next session. A
specs/
team-invitations/
spec.md # what & why, acceptance criteria, edge cases
plan.md # routes, data model, packages, migration
tasks.md # ordered, checkable task listA few things make this work in practice rather than in theory:
• Keep the spec stack-free until the plan. Mixing "users can revoke an invite" with "use a Postgres trigger" in the same document means you can't change your implementation without re-litigating the requirement. Separate the what from the how and each stays reviewable on its own.
• Write acceptance criteria as things you can check. "Invitation expires after 7 days" should map to a test, not a vibe. Criteria the agent can execute against are criteria that close the loop by themselves — the same principle behind test-first agent tasks.
• Give the agent your house rules once. Framework version, folder conventions, the patterns you already use — put them where the agent reads them (an
AGENTS.md or the spec's constitution) so it doesn't re-derive your conventions wrong on every feature.• Version the spec, review it like code. A spec in a chat window is gone next session. A
spec.md in the repo gets diffed in a PR, so a change in intent is as visible as a change in code.The Honest Failure Mode: Reinvented Waterfall
The fair objection to spec-driven development is that "write the full spec, then build it" is just waterfall wearing new clothes — and waterfall failed because requirements are never fully knowable up front. That critique lands if you treat the spec as a one-time gate you write once and freeze.
The version that works treats the spec as living. You specify enough to start, implement a slice, learn something, and edit the spec — then let the plan and tasks regenerate from the change. The spec is a document you keep in sync with reality, not a contract you sign in blood at the start. Used that way, SDD isn't "plan everything first"; it's "keep intent written down and current so the agent — and the next human — always has something concrete to build against." Skip the living part and you do get reinvented waterfall, with an AI agent faithfully implementing a stale spec.
The version that works treats the spec as living. You specify enough to start, implement a slice, learn something, and edit the spec — then let the plan and tasks regenerate from the change. The spec is a document you keep in sync with reality, not a contract you sign in blood at the start. Used that way, SDD isn't "plan everything first"; it's "keep intent written down and current so the agent — and the next human — always has something concrete to build against." Skip the living part and you do get reinvented waterfall, with an AI agent faithfully implementing a stale spec.
Where This Is Heading
Spec-driven development is the near-term, human-in-the-loop shape of a bigger shift: as agents take on more of the implementation, the specification — not the code — becomes the primary artifact engineers author and maintain. Code turns into a build output you regenerate; the spec is the thing you version, review, and argue about. If you want the fuller argument for where autonomous software delivery goes when the spec is the runtime and the "dark factory" builds the rest, that's the subject of the Adaptive Software Substack.
Before the Spec, Know What the Agent Is Building On
A clean spec-driven loop controls what the agent adds. It does nothing about what's already in the repo — an exposed environment variable, an outdated package with a known exploit, a performance problem baked into the existing routes. Point an agent at that codebase and your carefully specified feature ships on top of problems the spec never mentioned, because the spec only knows about the feature, not the foundation.
Connect your GitHub repo to Differ and get a free report on your first import — no live traffic required. Differ's code scan surfaces the security misconfigurations, leaked secrets, performance issues, and outdated dependencies already in your repo on the first pass, so you know the state of the foundation before you write your first spec against it. It's the fastest way to see what your Next.js app exposes today, on the first import, in a single scan — the baseline your spec should be built on.
Connect your GitHub repo to Differ and get a free report on your first import — no live traffic required. Differ's code scan surfaces the security misconfigurations, leaked secrets, performance issues, and outdated dependencies already in your repo on the first pass, so you know the state of the foundation before you write your first spec against it. It's the fastest way to see what your Next.js app exposes today, on the first import, in a single scan — the baseline your spec should be built on.