Skip to content

Spec-Driven Development with Coding Agents

Coding agents can produce code faster than a team can review it. That makes the quality of the instructions and checkpoints around the code more important, not less important.

Spec-driven development (SDD) gives the agent a chain of reviewable artifacts instead of one large prompt:

  1. requirements define the behavior and constraints
  2. design explains the technical approach and tradeoffs
  3. tasks divide the design into executable changes
  4. verification connects the implementation back to the requirements

The goal is not to predict every detail before coding. The goal is to make intent, decisions, and evidence visible enough that humans and agents can detect when the work is moving in the wrong direction.

A prompt, plan, and specification answer different questions.

ArtifactMain questionTypical lifetimeCommon failure
PromptWhat should the agent do now?One interactionImportant context remains implicit
PlanHow will this change be implemented?One task or pull requestThe plan optimizes a poorly defined outcome
SpecificationWhat behavior must exist, why, and under which constraints?A feature, change, or product capabilityIt becomes stale or too large to review
Verification evidenceWhat proves the required behavior exists?Test and release historyPassing checks do not cover the actual requirement

A useful specification is not merely a longer prompt. It is a contract that can be questioned before implementation and checked after implementation.

This also distinguishes SDD from casual “vibe coding.” In an unstructured session, the human repeatedly steers the agent by reacting to generated code. In SDD, much of that steering is made explicit before and during implementation through artifacts that can be reviewed, versioned, and tested.

SDD is sometimes presented as a straight sequence, but real development is iterative. Design can expose missing requirements. Implementation can expose an invalid design assumption. Testing can reveal that an acceptance criterion was incomplete.

flowchart TD
    A["Intent and problem"] --> B["Discover unknowns"]
    B --> C["Requirements<br/>behavior and constraints"]
    C --> D["Design<br/>approach and tradeoffs"]
    D --> E["Tasks<br/>executable changes"]
    E --> F["Implementation"]
    F --> G["Verification evidence"]
    G --> H{"Meets the specification?"}
    H -->|Yes| I["Review and release"]
    H -->|No| B
    D -->|Feasibility changes scope| C
    F -->|Implementation discovery| D

The feedback arrows matter. Without them, a generated specification can become a rigid waterfall document that protects an early mistake.

File names vary by tool. requirements.md, design.md, and tasks.md are common, but the names are less important than the separation of concerns.

Requirements describe observable behavior and constraints without prematurely deciding every implementation detail.

They should capture:

  • the user or system outcome
  • acceptance criteria
  • functional behavior
  • non-functional constraints such as latency, availability, privacy, and accessibility
  • error and boundary cases
  • compatibility requirements
  • explicit non-goals
  • unresolved questions

Requirements should not silently embed a preferred implementation.

Weak:

Store sessions in Redis.

Better:

Authenticated sessions must survive an application restart.
Session lookup must remain below the agreed latency target at expected load.
The design must work with the infrastructure already approved for this service.

The second version leaves room to compare Redis with an existing database, a managed session service, or another suitable design. If Redis is mandatory, state why as a constraint rather than disguising it as the outcome.

2. Design: How The Requirements Will Be Met

Section titled “2. Design: How The Requirements Will Be Met”

The design translates behavior into a technical approach. It should be grounded in the current codebase, not generated from generic architecture patterns.

A useful design covers:

  • relevant existing components and ownership boundaries
  • proposed components, interfaces, and data flow
  • alternatives considered and why they were rejected
  • data model and migration impact
  • authentication, authorization, privacy, and abuse cases
  • failure handling, retries, and idempotency
  • observability and operational support
  • rollout and rollback strategy
  • testing strategy
  • requirement IDs addressed by each design decision

For an existing repository, require file paths, symbols, commands, or documentation links that support the design. An agent should not claim that a reusable abstraction exists without pointing to it.

Tasks should be small enough to verify and large enough to deliver a meaningful increment.

Each task should identify:

  • the requirement and design decisions it implements
  • expected files or system boundaries
  • dependencies on earlier tasks
  • the observable completion condition
  • the check that must pass
  • whether the task is required or optional

Weak:

Implement password reset.

Better:

TASK-3 implements REQ-2 and DES-4.
Add one-time reset-token persistence behind the existing account repository.
Do not change the public login response schema.
Complete when:
- token values are stored only as hashes
- expired and consumed tokens are rejected
- focused repository and service tests pass

Prefer vertical slices over layer-only batches when possible. “Add the database schema, service behavior, API response, and focused tests for one requirement” is easier to review than generating every database change, then every service, then every endpoint across a large feature.

4. Evidence: How Completion Is Demonstrated

Section titled “4. Evidence: How Completion Is Demonstrated”

Completion is not the agent saying “done.” It is evidence connected to the specification.

Evidence can include:

  • unit and integration test output
  • contract or schema validation
  • screenshots and accessibility checks
  • load-test results
  • migration dry runs
  • security scans
  • logs or traces from a controlled environment
  • manual approval for behavior that cannot be automated safely

The evidence artifact does not need to be a separate file. It can be a traceability table in the pull request, test names containing requirement IDs, or links from tasks to CI results.

Not every change should begin with product requirements.

Starting pointBest whenFirst artifact
Requirements-firstDesired user behavior is known but architecture is flexibleRequirements and acceptance criteria
Design-firstArchitecture, migration, or technical constraints drive the changeTechnical note or design
Bug-firstExisting behavior is wrong and regression risk mattersReproduction, root cause, preserved behavior, fix criteria
Research-firstImportant facts or technology choices are unresolvedFindings, sources, experiments, and recommendation
Quick planThe change is understood and low riskShort scope, tasks, and verification list

Current Kiro Feature Specs support both requirements-first and design-first workflows, while Kiro’s web workflow also distinguishes feature, bug, and quick-plan work. GitHub Spec Kit provides a tool-neutral Spec to Plan to Tasks to Implement process for multiple coding agents.

These are implementations of the idea, not the definition of it. A team can use the same method with ordinary Markdown and any capable coding agent.

A full specification has a cost. Use it where reviewable intent reduces more risk than the process adds.

ChangeAppropriate artifact set
Typo or mechanical renamePrompt plus build or lint check
Small, well-understood fixReproduction, scoped plan, regression test
Multi-file featureRequirements, design notes, tasks, verification map
Migration, security, billing, or permissionsFull spec with review gates, rollout, rollback, and audit evidence
Exploratory prototypeHypothesis, time box, learning criteria, throwaway boundary

Use a full spec when:

  • several people must agree on behavior
  • requirements interact or conflict
  • the change crosses components or repositories
  • failure is expensive or difficult to reverse
  • an agent will work autonomously for a long period
  • the resulting decisions must survive the chat session

Do not use a full spec to make a one-line change look important.

IDs make traceability possible.

### REQ-1: Generic reset response
When a visitor submits a password-reset request, the account API shall return
the same public acknowledgement whether or not the email belongs to an account.
Rationale: prevent account enumeration.

Without a stable ID, phrases such as “the reset requirement” become ambiguous as the document changes.

An acceptance criterion should describe something a test or reviewer can observe.

VagueObservable
The page should load quicklyAt the agreed test load, the page’s p95 server response time is below 300 ms
Errors should be handled gracefullyA failed dependency returns the documented error state without losing the user’s draft
Use secure tokensReset tokens contain at least the approved entropy, expire after 15 minutes, are single-use, and are stored only as hashes
The UI should be accessibleThe flow passes the selected automated checks and its keyboard path is manually verified

Do not invent numeric targets just to make a sentence measurable. Targets must come from product, security, compliance, or operational owners.

Separate Functional And Non-Functional Requirements

Section titled “Separate Functional And Non-Functional Requirements”

Functional requirements describe behavior:

When a valid reset token is submitted with an acceptable password,
the account service shall replace the password and invalidate the token.

Non-functional requirements constrain how the behavior is delivered:

The service shall not write raw reset tokens or new passwords to application logs.

Security, latency, availability, data retention, localization, accessibility, and compatibility are often omitted because they are not visible in the happy path. Give them their own review pass.

EARS stands for Easy Approach to Requirements Syntax. It uses a small set of keywords to constrain natural-language requirements without requiring a formal specification language.

PatternUseExample
Always activeA rule that is continuously trueThe account service shall never store a raw reset token
State-drivenBehavior while a state is trueWhile an account is locked, the service shall reject password login
Event-drivenResponse to a triggerWhen five failed logins occur within ten minutes, the service shall lock the account
Optional featureBehavior only when a feature existsWhere enterprise SSO is enabled, the login page shall show configured identity providers
Unwanted behaviorResponse to an error or undesirable eventIf a reset token is expired, then the service shall reject it without changing the password
CombinedState plus event or error conditionsWhile maintenance mode is active, when a login is attempted, the service shall return the maintenance response

EARS can improve consistency and testability, but syntax alone does not make a requirement correct. A perfectly structured sentence can still encode the wrong user need, omit an important boundary, or use an undefined term.

Ask the following questions:

  • Who or what receives the value?
  • Is the behavior observable?
  • Are terms such as “fast,” “large,” “recent,” or “secure” defined?
  • Are time, ordering, concurrency, and retry behavior explicit where relevant?
  • What happens at minimum, maximum, empty, expired, duplicate, and interrupted states?
  • Do any requirements conflict?
  • Are external systems and policy assumptions supported by current sources?
  • Which decisions still require a human owner?
  • What must not change?

For a complex requirement set, ask an agent to perform a separate consistency review rather than immediately generating a design. Current Kiro tooling includes an Analyze Requirements pass for inconsistencies, ambiguities, conflicting constraints, missing definitions, and edge cases. The same review can be requested from any agent, but its findings still need human judgment.

Review these requirements without proposing implementation yet.
Find:
- ambiguous or undefined terms
- conflicting requirements
- missing failure, boundary, and concurrency cases
- solution choices disguised as requirements
- non-functional constraints that need an owner or measurable target
For every finding, cite the affected requirement IDs and propose a clarification.
Do not silently rewrite product intent.

Traceability answers a simple question: can we follow each important requirement through design, implementation, and evidence?

flowchart LR
    R1["REQ-1<br/>generic public response"] --> D1["DES-2<br/>uniform API result"]
    D1 --> T1["TASK-2<br/>handler and mail boundary"]
    T1 --> V1["TEST-5<br/>known and unknown email"]
    V1 --> E1["CI evidence"]

    R2["REQ-2<br/>single-use token"] --> D2["DES-4<br/>hashed token record"]
    D2 --> T2["TASK-3<br/>token lifecycle"]
    T2 --> V2["TEST-8<br/>state transition cases"]
    V2 --> E1

A lightweight matrix is often enough:

RequirementDesignTasksVerificationStatus
REQ-1DES-2TASK-2API integration testCovered
REQ-2DES-4TASK-3Token lifecycle testsCovered
NFR-1DES-5TASK-4Log-capture assertionCovered
NFR-2Not decidedNoneNoneBlocked

The uncovered row is valuable. It prevents a polished task list from hiding a missing design decision.

Traceability should remain cheap enough to maintain. Do not build a large requirements database for a small application unless regulation, safety, or organizational scale justifies it.

Acceptance criteria and tests are related, but they are not identical.

  • an acceptance criterion states required behavior
  • a test checks selected examples or properties of that behavior
  • evidence records what actually ran and what happened

One requirement may need several test levels. A password-reset rule might need a pure token-expiry unit test, repository integration tests, an API response test, and a browser-level user-flow check.

Example-based tests check cases selected by a developer:

expired token -> rejected
used token -> rejected
valid token -> accepted

Property-based testing defines a behavior that should remain true across many generated inputs or operation sequences:

For every token lifecycle, no token can successfully reset a password more than once.

Good candidates include:

  • encode then decode returns the original value
  • serialization and deserialization preserve meaning
  • retries do not duplicate an idempotent operation
  • balances are conserved across valid transfers
  • access never increases after a permission is removed
  • a state machine never enters a forbidden state

Property-based tests are powerful, but they are not automatic proof that the implementation satisfies the full specification. Confidence depends on:

  • whether the property expresses the real requirement
  • whether the input strategy covers the meaningful domain
  • whether the test oracle is correct
  • whether integrations and environmental failures are also tested

The Hypothesis documentation explicitly presents property-based testing as a useful addition to ordinary unit testing, not always a replacement.

Also watch for shared-assumption failure: if the same agent writes the requirement, design, implementation, and test, all four can encode the same misunderstanding. Human review, independent examples, production evidence, and separate adversarial review passes reduce that risk.

Use Tools During Specification, Not Only Coding

Section titled “Use Tools During Specification, Not Only Coding”

An agent should not design from its pretrained memory when current evidence is available.

Useful sources include:

  • the current repository and history
  • issue trackers and product decisions
  • API schemas and generated clients
  • architecture records
  • current vendor documentation
  • logs, traces, and incident reports
  • database schemas and sample data
  • design mocks and accessibility requirements
  • MCP servers that expose approved internal systems

Use tools in every phase:

PhaseUseful tool activity
RequirementsRead tickets, interview notes, policies, analytics, and current behavior
DesignInspect code paths, dependencies, schemas, APIs, and current documentation
TasksConfirm file ownership, build commands, test boundaries, and dependency order
ImplementationEdit, execute, inspect failures, and record deviations
VerificationRun checks, capture evidence, compare against requirement IDs

Tool access improves grounding, but it also creates security and freshness concerns. Restrict permissions, identify authoritative sources, record when evidence was retrieved, and do not treat an arbitrary search result as a product requirement.

Prompts anchor agents. If the first sentence says “store sessions in S3,” the resulting requirements and design may rationalize S3 instead of evaluating the actual persistence need.

Before approving the design, ask:

Identify solution choices embedded in my request.
Restate the underlying outcomes and constraints without those choices.
Research the current codebase and authoritative documentation.
Compare at least two viable approaches, including the proposed one.
Explain the operational, security, migration, and maintenance tradeoffs.

This does not mean the agent must challenge every decision. It means important decisions should be intentional rather than accidental prompt wording.

Greenfield examples are easy because the agent can invent a coherent architecture. Brownfield work is where discipline matters most.

Start with the delta, not a specification for the entire existing system.

  1. Map the current code path and ownership boundaries.
  2. Record current observable behavior, including behavior that must remain unchanged.
  3. Reproduce the problem or establish characterization tests.
  4. Define the required change and non-goals.
  5. Identify architecture and migration constraints from repository evidence.
  6. Design the smallest safe transition.
  7. Break implementation into reversible, verifiable slices.

Agent performance in a large repository depends on the same qualities that help humans:

  • clear module boundaries
  • cohesive responsibilities
  • reliable tests
  • discoverable build and run commands
  • current local documentation
  • manageable dependency graphs

A specification does not repair a tangled codebase. It can, however, make the risk visible and stop the agent from pretending a broad rewrite is a routine feature task.

For unfamiliar repositories, use Codebase Onboarding with Coding Agents and Finding Unknowns Before Agentic Work before finalizing the spec.

Specs become harmful when nobody knows whether they are temporary plans, historical records, or current contracts.

GitHub Spec Kit’s persistence guidance distinguishes several useful choices.

ModelMeaningMain risk
Spec-firstUse the spec to guide implementation, then allow it to become historical or be removedFuture work cannot rely on it as current truth
Spec-anchoredKeep the spec and use it as context for later changesIt can drift unless ownership is clear
Spec-as-sourceTreat the spec as the primary human-edited contract and derive other artifacts from itRegeneration can lose implementation rationale
ModelChange ruleGood fitWatch for
Flow-backAny artifact can change, then the set is reconciledFast, collaborative iterationSilent divergence between requirements, design, tasks, and code
Flow-forwardCreate a new change spec instead of mutating the completed oneAudit trails and historical clarityFragmented decisions across many specs
Living specUpdate the requirement contract, then revise or regenerate derived artifactsStable product contractsOld rationale disappearing during regeneration

Choose one convention explicitly. Add status and ownership to the artifact:

status: active
owner: identity-platform
last_validated: 2026-07-18
implementation:
- src/account/reset-service.ts
verification:
- tests/account/reset-flow.test.ts

The date does not prove correctness. It tells a future reader when somebody last checked the relationship.

Allow account owners to reset a forgotten password without exposing whether an
email address has an account and without allowing a reset token to be reused.
## REQ-1: Public acknowledgement
When a visitor submits a syntactically valid email address to the reset endpoint,
the account API shall return the same public acknowledgement regardless of
whether the email belongs to an account.
## REQ-2: Reset-token lifecycle
Where the email belongs to an active local account, the account service shall
create a cryptographically suitable, single-use reset token that expires after
the security team's approved duration.
If a reset token is expired, consumed, or invalid, then the account service
shall reject the password change without modifying the account.
## NFR-1: Secret handling
The system shall not persist or log raw reset tokens or submitted passwords.
## Non-goals
- changing enterprise SSO recovery
- changing login-session duration
- redesigning the email-delivery service

The duration is deliberately not invented. It remains an explicit decision for the security owner.

## DES-1: Preserve the public API shape
Known and unknown emails return the existing generic response. Mail dispatch
occurs behind the service boundary and does not alter the public result.
Addresses: REQ-1.
## DES-2: Store only a token hash
Generate the raw token once, send it through the existing mail abstraction,
and persist only its hash, account ID, expiry, and consumed timestamp.
Addresses: REQ-2 and NFR-1.
## DES-3: Consume atomically
Validate and mark the token consumed in one transaction so concurrent requests
cannot both succeed.
Addresses: REQ-2.
TaskRequirementsCompletion evidence
Add hashed token record and repository operationsREQ-2, NFR-1Repository tests cover expiry and atomic consumption
Implement uniform reset-request responseREQ-1API test compares known and unknown email responses
Implement password replacement transactionREQ-2Integration tests cover valid, expired, invalid, used, and concurrent requests
Add secret-handling assertionsNFR-1Captured logs contain neither raw tokens nor passwords

This is enough structure to make the work reviewable without specifying every class name before the agent inspects the repository.

# <Change Name>
status: draft
owner: <team or person>
reviewers: <product, engineering, security, operations as needed>
## Problem And Outcome
What problem exists? Who experiences it? What observable outcome should change?
## Current Evidence
- repository paths:
- logs or incidents:
- product or policy sources:
- assumptions not yet verified:
## Requirements
### REQ-1: <short name>
<Observable behavior and rationale>
Acceptance criteria:
- <criterion>
- <criterion>
### NFR-1: <quality or operational constraint>
<Measurable target or named owner who must define it>
## Edge And Failure Cases
- empty and boundary values
- retries, duplicates, and concurrent actions
- dependency failure and timeout
- authorization and privacy failure
- migration and rollback behavior
## Non-Goals
- <explicitly excluded work>
## Open Questions
- <decision, owner, and blocking status>
## Design
### Existing System
<Relevant files, components, interfaces, and constraints>
### Proposed Approach
<Components, data flow, interfaces, security, operations, rollout, rollback>
### Alternatives Considered
| Option | Advantages | Costs and risks | Decision |
|---|---|---|---|
### Requirement Mapping
| Requirement | Design decision | Verification approach |
|---|---|---|
## Tasks
- [ ] TASK-1: <change, requirement IDs, dependencies, and completion check>
- [ ] TASK-2: <change, requirement IDs, dependencies, and completion check>
## Verification Evidence
| Requirement | Check | Result or link |
|---|---|---|
## Deviations
<Implementation discoveries and the artifacts updated because of them>
Inspect the current repository and the supplied product evidence.
Draft requirements only. Do not design or implement yet.
Include stable IDs, acceptance criteria, non-functional constraints,
edge cases, non-goals, and open questions. Cite repository paths or current
sources for claims about existing behavior. Do not invent numeric targets.
Design an implementation for the approved requirements.
Map every design decision to requirement IDs. Ground the proposal in files and
interfaces you inspected. Compare meaningful alternatives. Cover data,
security, failure handling, observability, migration, rollout, rollback, and
verification. Stop if a requirement is infeasible or contradictory.
Break the approved design into dependency-aware, verifiable tasks.
Each task must reference requirement and design IDs, state its expected scope,
and name the check that proves completion. Prefer small vertical slices. Do not
include unrelated cleanup.
Implement only the approved tasks.
After each meaningful task, run its focused check and record evidence. If the
codebase contradicts the spec, stop and update the affected requirement,
design, or task through review instead of silently improvising.
Audit the implementation against every requirement ID.
Show the requirement-to-design-to-task-to-test mapping, command output, and any
manual evidence. List uncovered requirements, deviations, stale artifacts, and
remaining risk. Do not mark complete while a required row is uncovered.
FailureWhy it happensBetter approach
Giant generated documentsThe agent equates detail with qualityLimit each artifact to decisions that affect implementation or verification
Requirements describe technologyThe initial prompt anchors on a solutionRestate outcomes and compare alternatives before design
One-way workflowTeams treat approved text as immutableFeed design, implementation, and test discoveries back through review
Tasks lose requirement contextThe task list becomes the only artifact the agent seesCarry IDs and acceptance criteria into each task
Tests mirror the same misunderstandingOne agent authors every artifact from one assumptionAdd independent examples, adversarial review, and human validation
Specs drift after mergeNo persistence model or owner existsDeclare whether the spec is temporary, historical, anchored, or authoritative
Full ceremony for trivial workProcess is applied mechanicallyScale the artifact set to complexity and risk
Tools provide untrusted contextThe agent treats every retrieved source as authoritativeRestrict access and identify approved sources explicitly

What Spec-Driven Development Does Not Solve

Section titled “What Spec-Driven Development Does Not Solve”

SDD does not automatically provide:

  • correct product strategy
  • complete requirements
  • trustworthy source data
  • sound architecture
  • secure permissions
  • reliable tests
  • human accountability

It makes these concerns easier to inspect. It cannot replace the people responsible for them.

  • A specification defines behavior, constraints, and evidence; a plan mainly defines implementation steps.
  • Requirements, design, tasks, code, and tests should form a traceable chain.
  • The workflow must allow discoveries to flow backward, or it becomes rigid waterfall.
  • Structured language such as EARS helps testability but does not guarantee correct intent.
  • Property-based testing is useful for invariants, not automatic proof of the whole specification.
  • Tool use belongs in requirements and design as well as implementation.
  • Brownfield success still depends on repository structure, tests, and current evidence.
  • Decide whether specs are temporary, historical, anchored, or authoritative before they drift.
  • Use the smallest amount of ceremony that matches the cost of being wrong.

Diagram viewer