Learning Session Summary

SDD (Spec-Driven Development)

What a spec is, four ways teams keep them alive, and how the lifecycle plays out across a multi-repo project

What SDD Is

The one-line definition

Spec-Driven Development is the practice of writing a structured specification — describing intent, scope, and constraints — before or alongside the code that implements it. The spec is the contract; tests and code are how you keep your end of it.

Why bother

A spec separates what we agreed to build from what's currently in main. Without that separation, the only source of truth is the code — and "what we meant to do" gets lost the moment a contributor leaves or a feature evolves.

Who writes it
In small teams: usually the engineer building the feature drafts the spec; PM/lead reviews it. In larger orgs: PMs write the What and Why; engineers fill in Constraints, Tasks, and Validation. Either way, it's a collaborative artifact, not a one-person decision.
Spec-first vs. code-first
Spec-first: write the spec before any code. Catches scope and edge cases early; slower to start. Code-first (a.k.a. "spec the existing thing"): reverse-engineer a spec from working code so it can be maintained going forward. Most real teams blend the two — spec-first for new features, code-first when adopting SDD on a legacy codebase.

The Anatomy of a Spec

A good spec template has fixed sections so readers always know where to find a given piece of information. The seven below are the ones that show up across most SDD templates.

SectionWhat goes here
Why1–2 sentences. The problem this solves and why now.
WhatThe concrete deliverable, specific enough that you'd know when it's done.
ConstraintsThree subsections: Must (required patterns/libraries), Must Not (forbidden — e.g., no new deps), Out of Scope (adjacent work explicitly not included).
Current StateWhat already exists. Saves the next contributor from re-exploring the codebase.
TasksThe work broken into discrete units. Each task lists What, Files, and Verify (the command or check that proves it's done).
ValidationEnd-to-end verification once all tasks are complete — automated suites, manual UI checks, cross-feature dependencies.
The "Verify per task" idea

Each task gets a one-line proof that it works — usually a single test command. The aggregate of all Verify lines tells you, at a glance, which parts of the feature have automated coverage and which don't. The Validation section is the cumulative version: "if all tasks are green, here's how I check the whole feature still hangs together."

Four Patterns for Spec Lifecycle

Once a spec exists, what happens to it as the code changes? Real teams pick (or drift into) one of these four patterns.

Living spec

The spec is kept in sync with the code. PRs that change behavior also update the spec.

  • Pro: the spec stays trustworthy — you can rely on it to answer "what does this do today?"
  • Con: requires discipline; without enforcement during code review, drift sneaks in fast (especially in Verify and Current State).
  • Common in: product engineering teams that treat docs as part of the definition of done.
Frozen spec + amendments

The original spec is preserved as a record of intent at time of build. New scope means a new spec, or an "Amendment N" appended at the bottom of the existing one.

  • Pro: history is preserved — you can see what was decided and when.
  • Con: to know the current truth you have to read the original plus every amendment.
  • Common in: regulated environments (medical, finance, aerospace) where audit trails matter.
ADRs (Architecture Decision Records)

Append-only log of decisions. Each new decision gets its own short document; if it supersedes a previous one, the old ADR is marked "Superseded by ADR-N" but never edited.

  • Pro: small, focused units; easy to grep for "why does this work this way?"
  • Con: doesn't describe the current state — only the chain of decisions that produced it.
  • Common in: mature engineering orgs; pairs well with a separate "living overview" doc.
Spec dies after implementation

The spec is a planning artifact, used once during implementation and then ignored. Code, tests, and READMEs become the source of truth.

  • Pro: zero maintenance burden.
  • Con: intent is lost — six months later, no one knows whether a behavior is intentional or accidental.
  • Common in: fast-moving startups, prototype work, hackathons.

Verify & Validation — The Living-Spec Mechanics

The parts of a spec that rot fastest are the test-coverage claims. Two conventions keep them honest.

Per-task Verify — pick one of four
WordingWhen to use
<command to run>There's a dedicated test for this task.
No dedicated test; indirectly covered by <command>.The behavior is exercised under a related test (e.g., a repository tested through its service).
No tests cover this task yet.A test is missing but warranted.
No tests — type-only, intentionally not covered.For type definitions or trivially presentational components.
The Validation section — three sub-blocks
  • Automated checks — full suites per layer (npm test, ./mvnw test) plus the feature-specific test files.
  • Manual checks (UI) — numbered user flows: happy path, edge cases pulled from Constraints and Out of Scope, auth/routing/empty-state checks.
  • Cross-feature dependencies — other specs whose tests or seed data this feature relies on; tests referenced by more than one spec.
Why these two specifically

Constraints rarely lie — they're upstream of the work. The What rarely lies — it's broad. But "this is covered by test X" is the kind of claim that becomes wrong the moment a test is renamed, removed, or never written. Forcing every task to declare its verification status, in one of four standard wordings, makes the lying visible.

Worked Example — A Multi-Repo Project

Three repos, one spec library. Each repo has a different test runner and CI; the specs live separately so they describe both layers without belonging to either.

Docs repo
yoga-path-docs
One markdown file per feature: jwt-auth-spec.md, profile-spec.md, practice-log-spec.md, etc. Plus a spec.example.md template. No code.
Client repo
yoga-path-client
React + TypeScript. Tests via Jest + React Testing Library (npm test). Lint and tests run in GitHub Actions; deploys to Vercel on merge to main.
Server repo
yoga-path-server
Spring Boot + JPA. Tests via JUnit (./mvnw test). Test classes target services and controllers; commands like ./mvnw test -Dtest=AuthServiceTest run a single suite.
Why three repos
The docs need to outlive any single layer. If the client gets rewritten in a different framework or the server gets split, the spec library survives intact and the new implementation has something to be measured against.
Workflow — feature, bug, or test work

Whatever kind of change goes through the same loop: create a feature branch, work in the relevant repo, open a PR, then update the spec after the PR merges to main.

1. Branch
feature / fix / tests
off main
2. Build
code +
tests in repo
3. PR
CI green,
review, merge
4. Update spec
in yoga-path-docs
after merge
What gets touched in step 4
Change typeWhat to update in the spec
New featureEither a new spec file or a new task block in an existing one. Move related items out of Out of Scope. Add Verify + extend Validation.
Bug fixUsually no change — the bug means code disagreed with spec. If the spec was ambiguous about the case, tighten Constraints. Add a Verify if a regression test was added.
New testsUpdate Verify for any task whose status changes from "no tests yet" to a real command. Add the new test file to the Validation section.
Behavior changeUpdate What and Constraints. Add an entry to Current State if the change leaves an intentional gap (e.g., "service still calls findAll() instead of the sorted query").
Why update the spec after the PR merges

Updating the spec inside the same PR sounds tidier, but in practice the spec change ends up reviewed alongside code review, where reviewers focus on the diff. Keeping it as a follow-up — in the docs repo — lets the spec change get its own focused review and avoids holding code merges hostage to documentation nits.

The takeaway

A spec is only useful if you can trust it. The four lifecycle patterns are really four answers to the question "who keeps this honest, and how?" — and for any non-trivial project, picking that answer up-front matters more than which template you use. Living specs need Verify and Validation as the discipline mechanism; ADRs need supersession rules; frozen specs need a clear amendment process. No mechanism, no trust.