SDD — Designing a Spec That Survives Code Generation — Spec-First — Spec-Driven Development
Five specification types with different lifecycles — and why maintaining everything is the wrong answer to spec-in-the-drawer.

SDD — Designing a Spec That Survives Code Generation — Spec-First — Spec-Driven Development
Five specification types with different lifecycles — and why maintaining everything is the wrong answer to spec-in-the-drawer.
The spec goes in the drawer — and it’s not your fault
Friday, six o’clock. A developer closes the laptop after a sprint with a coding agent. Thirty generated Markdown files sit next to code that passed review and shipped to production.
Two weeks later, he returns to the same feature. Not one of those files reflects reality. The agent added validations, someone introduced tenant scoping in a hot-fix, a library changed its signature. The Markdown from the previous sprint is archaeology.
This isn’t due to a lack of discipline on his part. It’s a built-in feature of the architecture he unknowingly chose.
“I believe the hard part of building software to be the specification, design, and testing of this conceptual construct, not the labor of representing it.” — Frederick Brooks, Computer Scientist, “No Silver Bullet” (1986)
Spec-in-the-drawer isn’t a discipline failure. It’s the consequence of an architectural decision most teams never consciously made. The right answer isn’t maintaining one document — it’s splitting the specification into five document types with different lifecycles. That’s what this article is about.
What you’ll learn
- Why PRDs and SRSs are written for humans — and why LLMs as recipients require a fundamentally different information architecture.
- Seven information layers — cross-cutting requirements that MUST appear in every spec for an LLM, regardless of the document type.
- Five specification types you need — constitution, feature spec, technical plan, task list, and change spec. What each is for, what it MUST contain, and a template to copy.
- The lifecycle of these specifications — which one dies on purpose, which survives, and which gets archived.
- A decision tree — which spec set is right for solo, team, and enterprise.
Why PRDs, SRSs, and LLDs were written for humans — and why LLMs require a different information architecture
When you write “the system should authenticate users”, a person on your team knows what you mean. You’re using Postgres, you switched to JWT, and a Slack thread from March decided on Auth0. The person reads between the lines and asks at the coffee machine when in doubt.
LLMs don’t have coffee or Slack. The agent chooses between OAuth, JWT, and a session cookie based on the statistical distribution in its training data — weighted toward whatever was popular on GitHub in 2023. This isn’t a model defect. It’s a difference in the nature of the recipient.
PRDs, SRSs, and LLDs are a legacy of committees. Ambiguity is a feature in them — it leaves room for negotiation, hallway clarification, and revision after the demo. These documents assume tribal knowledge: the team’s unwritten knowledge that fills in the gaps. A spec for an LLM can assume nothing.

PRD vs Spec for LLM — this isn’t a difference in style, it’s a difference in kind. Every layer on the right closes a gap that on the left is filled by a human by default.
The key insight: this isn’t about a better version of a PRD. It’s about a different category of document entirely. Hallucinations are the logical consequence: the agent received a document designed for a reader who has context the agent doesn’t.
For terminological clarity: vibe coding — a term coined by a prominent AI researcher in 2025, describing software development as a conversation with an agent without a plan. Spec-Driven Development (SDD) is a workflow in which you write a document describing WHAT and WHY before you start instructing the AI.
The conclusion: we must design the spec for LLMs from scratch, without inheriting PRD and SRS conventions.
Seven information layers — cross-cutting requirements
Before I get to the specific document types, one observation: regardless of which document you’re writing, it MUST contain seven information layers. These aren’t sections — they’re qualities. Every SDD document contains them in some proportion.
- Layer 1: Language precision. “The system should encrypt passwords” means everything and nothing. “The system SHALL encrypt all passwords using bcrypt with cost factor ≥ 12” is a contract. EARS (Easy Approach to Requirements Syntax) — a format with five sentence patterns, developed at Rolls-Royce in 2009 — enforces precision. Adopted in aviation and the automotive industry long before the LLM era — and it turns out this was training for agents all along.
- Layer 2: Negative requirements. Without an explicit “the system does NOT do X” section, the agent will add social login because “it’s best practice.” The agent’s default optimism is a bug, not a feature.
- Layer 3: Measurable success criteria. “Implement JWT auth” means twenty lines or two hundred. Without acceptance criteria, the agent decides arbitrarily. This is the only way it knows when to stop.
- Layer 4: Hard technical constraints. What is forbidden — libraries, patterns, and APIs. Without this layer, the agent selects technology by popularity in its training data — sometimes something that’s been deprecated for two years.
- Layer 5: Architectural context — codebase anchoring. A spec anchored in the existing repository: the agent knows specific files and dependencies; it doesn’t write “in a vacuum.” Without anchoring, the agent builds a parallel world — that’s how it creates duplicate classes that it then tries to “unify.”
- Layer 6: Edge cases as explicit scenarios. Without named edge cases, the agent generates the happy path. The pattern appears everywhere: scenarios are named, not implied.
- Layer 7: Verified library specs — anti-hallucination. A verified library specification describes actual API signatures, not statistical guesses. A feature spec should include the specific library version and key API constraints.

Seven information layers. These aren’t sections, they’re qualities — each of the five document types (Section 3) contains them in different proportions.
Independent research from 2022 found that over 40% of code in security-critical contexts had vulnerabilities — a significant portion of cases traces directly to a gap in one of these seven layers. These layers are requirements. The five document types (below) are the mechanisms for expressing them.
Five specification types you need
For an agent to generate production code, you need five distinct documents. Each answers a different question, has a different lifecycle, and has a different owner. Together, they form a complete contract with the LLM.
For each type, I’ll cover: purpose, lifecycle, what it MUST contain, and a template to copy.
Project Constitution
Purpose. A permanent agreement between the team and the agent. Non-negotiable rules — security, tech stack, and fundamental architectural patterns. Read by the agent at EVERY session, at every level.
Lifecycle. Years. Edited once per quarter, semantic versioning. Constitution changes are auditable events.
What it MUST contain.
- Tech stack — an ALLOWED list (with specific versions) and a FORBIDDEN list. A single entry saves hundreds of corrections.
- Architectural patterns — required (“database access only through the repository layer”) and forbidden (“no global mutable state”).
- Security — custom rules anchored in established standards (OWASP Top 10, CWE). Mapping to CWE-IDs makes the constitution an auditable compliance artifact.
- Testing gates — coverage thresholds and test categorization (unit/integration/e2e). Minimal, because the agent handles most of it.
- Code style minimum — naming conventions and directory structure. LLMs handle most conventions by default — don’t overcomplicate it.
- Enforcement levels — CRITICAL (blocks merge), SHOULD (warning), and MAY (informational).
Template.
# Project Constitution — vX.Y.Z
## Article I — Tech Stack [CRITICAL]
ALLOWED: TypeScript ≥5.3, React ≥18, PostgreSQL ≥15
FORBIDDEN: jQuery, class components, CommonJS modules
## Article II — Architectural Patterns [CRITICAL]
The system SHALL access database only through the repository layer.
Direct ORM calls in controllers are PROHIBITED.
## Article III — Security [CRITICAL]
The system SHALL parameterize all database queries (CWE-89).
The system SHALL hash passwords using bcrypt with cost ≥12 (OWASP A02).
## Article IV — Testing Gates [SHOULD]
Coverage on changed files ≥80% before merge.
## Article V — Code Style [MAY]
Repository pattern: src/repositories/{Entity}Repository.ts
Nine to twelve articles. No fewer (too abstract), no more (compliance overload — the agent gets lost in the instructions).
Feature Specification
Purpose. Describes WHAT and WHY for a single feature. What the user gets, why, and how you verify it’s done.
Lifecycle. Per-feature branch. After the merge it can die (on purpose) — that’s its right.
What it MUST contain.
- Business goal — one paragraph. Why this feature exists.
- User stories — As a {persona}, I want {action} so that {benefit}.
- Functional requirements — in EARS format (SHALL). Numbered: FR-001 and FR-002.
- Success criteria — measurable, verifiable. SC-001: “Login completes in <2s p95.”
- Non-Goals — what this feature does NOT do. Without this section, the agent will add features.
- Edge cases — explicit, named. “IF login fails 5 times THEN account is locked for 15min.”
- Codebase anchoring — which files, which patterns, and where to hook in.
- Open questions — a “[NEEDS CLARIFICATION]” section for ambiguities. Better to stop the agent than let it guess.
Template.
# Feature Spec: {name}
## Goal
[1 paragraph — why this feature]
## User stories
- US-1: As a registered user, I want to log in with email+password
so that I can access my account.
## Functional requirements
FR-001: The system SHALL authenticate users via email+password.
FR-002: WHEN login fails 5 times, the system SHALL lock account for 15 min.
## Success criteria
SC-001: 95% logins complete in <2s.
SC-002: Failed login does not reveal whether email exists.
## Non-Goals
- Social login (planned v2)
- Password reset flow (separate feature, FR-XXX)
## Edge cases
- IF email exists but is unconfirmed THEN block login + send confirmation.
- IF account locked THEN return 423 + Retry-After header
## Codebase anchoring
- src/auth/AuthService.ts — extend
- src/middleware/rateLimit.ts — reuse pattern
- src/db/schemas/User.ts — no changes
## [NEEDS CLARIFICATION]
- 2FA strategy: TOTP vs SMS vs none [NEEDS CLARIFICATION]
Technical Plan
Purpose. Describes HOW. Architectural decisions, libraries with versions, implementation phases, and risks. This is the document the agent reads before it starts generating — to understand the technical context it’s working in.
Lifecycle. Per-feature, shorter than the feature spec. After execution, it becomes history.
What it MUST contain.
- Stack and versions — specifically. Not “Node ≥18,” but “Node 20.11 LTS.”
- Architectural decisions — key choices with rationale. Links to separate ADRs if the choice is significant.
- Dependencies — which libraries, in which versions, and why these rather than others.
- Phases / milestones — implementation order.
- Performance goals — specifically. “1000 req/s p95 <200ms, <100MB memory per instance.”
- Risks — what can go wrong and how you’ll detect it.
Template.
# Technical Plan: {name}
## Stack
Node 20.11 LTS, TypeScript 5.4, Fastify 4.26, PostgreSQL 16.
## Architectural decisions
- AD-1: JWT (jose) + httpOnly cookie. Refresh token in a separate table.
Rationale: stateless API, but invalidation possible via DB deletion.
- AD-2: bcrypt (cost=12) instead of argon2.
Rationale: constitution Article III, OWASP A02.
## Dependencies
- jose@5.2 (JWT)
- bcrypt@5.1 (hashing)
- @fastify/rate-limit@9.1 (existing pattern)
## Phases
1. Schema migration (users.password_hash, users.locked_until)
2. AuthService (signup/login/lock logic)
3. Routes + middleware
4. Tests (unit + integration)
## Performance goals
- Login p95 <2s, p99 <5s.
- bcrypt cost=12 → ~250ms per hash. Acceptable.
## Risks
- bcrypt blocking event loop → use worker threads if p95 >2s.
- Brute force on endpoint → rate-limit exists, but audit the rules.
Task List
Purpose. Atomic units of work. Each task deterministic, verifiable, and independently committable. The agent executes the list task by task, with a fresh context.
Lifecycle. Per-feature. After execution, it becomes history in git (one commit per task).
What it MUST contain for each task.
- ID + name — T001 and T002. Short, actionable title.
- File paths — which files it touches.
- Action — what specifically to do. Full code if possible.
- Verification — a command or test that confirms the task is done.
- Definition of done — measurable.
- [P] — a marker for parallel-safe tasks (independent ones).
- Dependencies — sequential ordering for dependent tasks.
Template.
# Task List: {name}
## T001: Users schema migration
**Files:** db/migrations/2026_05_15_login.sql
**Action:** ALTER TABLE users ADD COLUMN password_hash TEXT NOT NULL,
ADD COLUMN locked_until TIMESTAMP NULL.
**Verification:** psql -c "\\d users" — shows the new columns.
**Done:** Migration ran on local DB; rollback passed.
## T002 [P]: Test AuthService.hashPassword
**Files:** src/auth/__tests__/AuthService.test.ts
**Action:** [full test code with bcrypt cost=12 + edge case for empty password]
**Verification:** npm test -- AuthService.test
**Done:** Test FAILS (RED phase TDD).
## T003 [depends: T002]: Implement AuthService.hashPassword
...
The [P] markers allow the agent to execute tasks in parallel when they have no dependencies. An atomic commit per task — makes rollback and review easier.
Change Specification — delta and bug fix
Purpose. Describes a change to an existing system. Either an iteration (delta) or a defect repair (bug fix). The format is a semantic diff, not a full re-spec.
Lifecycle. Per-change. Archived with a timestamp (audit trail).
What it MUST contain for delta.
- ADDED — new requirements or features.
- MODIFIED — changed requirements, in “old → new” format.
- REMOVED — deprecated requirements.
- Impact on other components — which parts of the system need review.
Template — delta.
# Delta: adding 2FA to login (2026-05-20)
## ADDED
- FR-010: WHEN user enables 2FA, the system SHALL require TOTP code on every login.
- SC-010: TOTP setup flow completes in <60s end-to-end.
## MODIFIED
- FR-001 (previous version): "system SHALL authenticate users via email+password"
- FR-001 (new): "system SHALL authenticate users via email+password,
followed by TOTP if 2FA is enabled."
## REMOVED
- (none)
## Impact
- AuthService.login — add 2FA challenge step.
- LoginRoute — add step state machine.
What it MUST contain for a bug fix.
- Current behavior — how the system behaves NOW (incorrectly).
- Expected behavior — how it should behave after the fix.
- Unchanged behavior — what MUST remain unchanged. The most important section. Without it, the fix introduces regressions in distant modules.
- Reproduction — steps to reproduce.
- Root cause — the cause, not just the symptom.
Template — bug fix.
# Bug-fix: rate-limiter ignores X-Forwarded-For (BUG-2026-042)
## Current behavior
WHEN request arrives through load balancer THEN rate-limiter uses
the load balancer's IP, not the end client's → all clients share
one IP's limit.
## Expected behavior
WHEN request has X-Forwarded-For header from trusted proxy
THEN rate-limiter SHALL use the original client IP.
## Unchanged behavior
- WHEN request does NOT have X-Forwarded-For THEN rate-limiter uses
socket IP (as now).
- WHEN X-Forwarded-For comes from untrusted source THEN ignore it
(security: spoofing protection).
## Reproduction
curl -H "X-Forwarded-For: 1.2.3.4" localhost/login (in a loop) →
first client blocked after 5 attempts (should be: individual limits).
## Root cause
Rate-limit middleware has no trustedProxies list in configuration.
The Unchanged behavior section is the difference between a bug-fix that fixes and a bug-fix that breaks. Without it, the agent treats the entire module as “to be rewritten.”

Five document types. Each answers a different question, has a different lifecycle, and has a different owner. Together they form a complete contract with the LLM.
Five documents look like overhead. They aren’t — because each has one job and one lifecycle. Trying to fit all of this into a single file produces exactly the chaos that frustrates developers dealing with drift.
Lifecycle — what dies, what survives
In every country, the constitution changes rarely, while ordinary legislation changes weekly. Your project needs that same separation. The constitution: years, rare changes, and semantic versioning. The per-feature triad (feature spec, plan, tasks): weeks, dies on purpose after the merge. Change spec: timestamped archive and audit trail.
Birgitta Böckeler of Thoughtworks (October 2025) introduced the canonical taxonomy of what happens to specs after the merge: spec-first, spec-anchored, and spec-as-source. Most developers choose the first path unknowingly — through tool choice, not a deliberate decision. This article is about spec-first.
In the spec-first model, the per-feature triad (spec + plan + tasks) dies on purpose after the merge — that’s its right. The constitution survives. Change specs are archived. For clarity: the other two paths are spec-anchored (spec lives alongside code) and spec-as-source (code is the compiler’s binary). Both are niche and outside the scope of this article.

Lifecycle of the five specification types. The constitution survives everything. The per-feature triad dies on purpose. Change specs are archived — available for audit, not bloating the conversation context.
Yegge, in 2025, spent a month drowning in hundreds of Markdown files. None got read again. That wasn’t due to his lack of discipline — it was the spec-first path without a dedicated persistent layer. A constitution that survives.
The mechanism is straightforward. The constitution is READ by the agent at the start of every session — a file-based handoff, independent of the conversation context. It survives context rot (the degradation in generation quality as the context window fills up) because it isn’t in the conversation history. The file reads just as well in the second session as in the twentieth.
Decision tree — which spec set for your team
Five types is the complete set. Most teams don’t need all five from the start.
- Solo / small team / greenfield: Constitution (lightweight, 5–7 rules) + feature spec (combined with technical plan in one section) + tasks. Change spec as a bug-fix template, delta only when the project is older than 6 months. Most cases.
- Solo / brownfield / hobby: Minimal constitution (3–5 rules — mainly the tech stack) + ad-hoc feature spec. Rewrite more often than maintain. Skip the formal task list.
- Team / long-term project: All five types. Expanded constitution (9 to 12 rules), formal phases in the technical plan, explicit task list with
[P]markers, and delta spec for each iteration. Edge cases are explicitly named, because no agent will forgive you for regressions in distant modules. - Enterprise / regulated (fintech, healthcare, defense): All five types + a heavy constitution mapped to established standards (OWASP, CWE) as a hard gate at every generation. Here, spec-first as the only mode stops being sufficient — but that’s a topic for another episode. For the purposes of this article, the constitution is what holds the entire regime together regardless of the other choices.

SDD decision tree. Most teams land on solo/greenfield → lightweight constitution + feature spec combo + tasks. Regulated domains and brownfield require all five types with a heavy constitution.
Going through the tree takes two minutes. The decision saves thirty hours of fighting spec-drift over the next two years. Teams that skip this decision get the tool’s default set — and then wonder why the auditor won’t accept a Slack thread as a compliance document.
Summary
Let’s return to the developer from Friday at six. Two weeks later, his repository has five document types: a constitution with nine rules read by the agent at every session, a per-feature triad (feature spec, technical plan, and task list) dying on purpose after the merge, and a delta archive preserving the history of changes. The Markdown from the previous sprint is still archaeology — but in the archive, not in the root directory. Spec-in-the-drawer stops being a problem once you accept that the per-feature spec is MEANT to die — and you separate out what MUST survive.
Five things to take away:
- PRDs, SRSs, and LLDs are a legacy of committees — design the information architecture from scratch.
- Seven information layers are cross-cutting requirements — they appear across all five document types, in different proportions.
- Five spec types you need: constitution, feature spec, technical plan, task list, and change spec (delta + bug fix). Each has one job and one lifecycle.
- The constitution survives everything else — and that is the empirical answer to spec-in-the-drawer.
- Hot-fix-friendly SDD doesn’t exist, and that’s a fact, not a bug. The constitution is your hot-fix gate.

Anatomy of Spec-First in one image. Five document types with different lifecycles. The constitution survives — everything else can die on purpose.
Start with the constitution. Nine to twelve rules, edited once per quarter. Then the per-feature triad for the first real feature. Add change specs when the second merge arrives. Thirty minutes of decision-making saves thirty hours of fighting drift over the next two years.
Thanks for making it to the end. If this article changed the way you think about specifications for AI, share it with a colleague who complains about “agent hallucinations.” Leave a comment: which of the five specification types do you already write, and which is missing?
메타데이터
- post_id
- b61fdc234493
- slug
- sdd-designing-a-spec-that-survives-code-generation-spec-first-spec-driven-development-b61fdc234493
- url
- https://medium.com/@wasowski.jarek/sdd-designing-a-spec-that-survives-code-generation-spec-first-spec-driven-development-b61fdc234493
- canonical_url
- https://medium.com/@wasowski.jarek/sdd-designing-a-spec-that-survives-code-generation-spec-first-spec-driven-development-b61fdc234493
- author_url
- https://medium.com/@wasowski.jarek
- status
- ok
- fetched_at
- 2026-06-09 15:37:30