-
Notifications
You must be signed in to change notification settings - Fork 2
Hands On Walkthrough
One complete SDD cycle on a toy project, in about 45 minutes. Works in Claude Code and in GitHub Copilot — the commands are identical.
This is a workshop exercise: work in pairs. One person is the Product Owner (PO), one is the Developer (Dev). You sit at the same machine and take turns deliberately. In PO phases only the PO types; in Dev phases only the Dev types. That discipline is the lesson.
Doing it alone? Then switch hats consciously at each phase boundary and notice how different the two roles feel.
A tiny HTTP service that splits a restaurant bill fairly across several people. Inputs: bill amount, tip percentage, number of people. Outputs: the amount per person and the total tip. No database, no frontend, no login — just enough for the whole SDD cycle to run through visibly once.
Create an empty project from the template and open that folder in your tool — not a parent folder, or the slash commands will not appear.
git clone https://github.com/GregorBiswanger/featherspec.git tip-splitter
cd tip-splitter
rm -rf .git
git init(On Windows PowerShell: Remove-Item -Recurse -Force .git. With Node.js installed,
npx degit GregorBiswanger/featherspec tip-splitter replaces the clone and the two cleanup
lines.)
Claude Code: run claude in the folder. Type / and check that the sdd- commands appear.
GitHub Copilot: open the folder in VS Code, open Copilot Chat
(Ctrl/Cmd + Alt + I), switch to Agent mode, pick
the SpecDrivenAgent persona. Type / and check that sdd-setup, sdd-specify, sdd-plan,
sdd-compile and sdd-lifecycle appear. Missing? Restart VS Code completely — not just a
window reload.
Once per project. Creates the Memory Bank and fills the architecture snapshot.
/sdd-setup
Answer the language question with whatever your team documents in — English, Deutsch, … —
and then the short wizard.
Learning point: open
.memory-bank/. That is the long-term memory. Everything decided here survives closing the tool, switching to the other assistant, and next Monday. That is what separates SDD from a throwaway chat.
The PO writes the specification from the user's point of view. No tech stack. Not one word about Node.js.
/sdd-specify I want a small service that splits a restaurant bill fairly across several
people. A user gives three things: the bill amount, a tip percentage and the number of
people. The service returns what each person pays in total (including tip) and how large
the total tip is.
Goal: a group at the table knows within seconds what everyone owes, without doing mental
arithmetic.
Success criteria:
- The per-person result is always rounded to two decimal places.
- The individual amounts add back up to the total (no cents lost to rounding).
- A response arrives in under a second.
Out of scope: no login, no persistence, no currency conversion, no splitting by what each
person ordered.
The assistant now runs its interview — one question per message — about users, roles, edge cases and acceptance criteria. Answer as the PO. Short answers are fine.
Learning point: pause and notice what is not in there. No "build a REST API", no "use Express", no endpoint paths. Only the problem and what success means. The how comes from the Dev in a minute.
As a follow-up message in the same chat:
Add these rules to the spec:
- 0 or fewer people returns a clear error message, not a calculation.
- A negative tip percentage is not allowed and returns an error. 0 percent is allowed.
- A bill amount of 0 is allowed and yields 0 per person.
- Leftover rounding cents are added to the first person, so the sum comes out exactly.
Learning point: the rounding-cents rule is a genuine product decision. The AI cannot guess it and the Dev is not allowed to invent it. That is exactly why the PO is in the room.
/sdd-clarify
This reads the spec as a stranger — no memory of the conversation you just had — and comes back with four lists: contradictions, terms used in two senses, criteria nothing can decide, and failure modes nobody named. It does not fix them. It ends with one question.
Learning point: read the output together, then ask the room: how many of these could we have caught ourselves? Usually one or two. The rest were invisible from inside the conversation that produced them — which is the entire argument for a second pass. The context that created an ambiguity is structurally the worst placed to find it.
Decide per item: fix it in the spec now, or move it to Open points and accept the risk knowingly. Both are legitimate; silently ignoring it is not.
The PO reads the generated spec in .specs/backlog/, checks it, and says out loud:
"Spec approved." Then hand the keyboard to the Dev.
You should now have:
.specs/backlog/0001-tip-splitter.md
with **Status:** Draft and a list of AC-001, AC-002, … criteria.
Now only the Dev types. The PO watches and does not intervene.
/sdd-plan We build this as a minimal HTTP service with Node.js and the built-in http module,
without external frameworks. A single POST endpoint /split takes JSON with the fields amount,
tipPercent and people, and returns JSON with perPerson and totalTip. Input validation happens
in the service. The calculation lives in its own testable module. We write unit tests with
the built-in node:test runner. No database, no build step.
This writes .specs/backlog/0001-tip-splitter.plan.md next to the spec: numbered baby steps
(T-001, T-002, …), each with a Verify: line and an empty Verified: field, plus a research
section, a traceability table and a session-handoff block. Then it stops and hands the plan
back — planning does not touch code.
Now actually read it. Both of you, out loud, for five minutes. This is the exercise most worth doing properly, and the one every team skips in real life.
You are not hunting for bugs. You are checking that you agree on the why and the ordering — does step three really need step two first, is the risky part early enough, is anything missing. A wrong step produces hundreds of wrong lines; a wrong line produces one. Two hundred lines of plan versus two thousand lines of diff is not a close call.
Say which steps look wrong before a single line is written.
Learning point: the Dev just made a dozen decisions the PO never saw — the
httpmodule instead of Express, a separate calculation module,node:test. The PO has no business in any of them. And conversely: the Dev should never have invented the rounding-cents rule.
Implement T-001.
Then T-002, and so on — or ask for the whole plan and review each focused change as it lands.
After every step the assistant runs the Verify: command, writes what came back into
Verified:, then ticks the checkbox, fills in Notes, writes the real file paths and the
deciding test into the traceability table, and moves Current step — all in the same change set
as the code.
Open the plan file and look at a finished step. Verified: should read something like
2026-08-03 · node --test · 7 passed. If it is empty and the box is ticked anyway, you have
caught the failure mode this field exists for: an agent optimises for reporting completion, and
a checkbox costs nothing to tick.
Try this: close the session halfway through and start a new one with /sdd-plan. It reads the
plan, checks it against git status, and tells you in three lines what is done, what is next and
what blocks it.
In the terminal:
node --testnode server.jsAnd in a second terminal:
curl -X POST http://localhost:3000/split -H "Content-Type: application/json" -d "{\"amount\": 100, \"tipPercent\": 10, \"people\": 3}"Expect roughly 36.67 / 36.67 / 36.66 per person — the leftover cent goes to person one — and a
total tip of 10.
/sdd-compile
You get the readiness brief. It opens with a verdict — READY, NOT READY, or
NOT READY — unverified — then every acceptance criterion marked satisfied or pending with
evidence, open plan steps, docs-sync status, and the next three actions.
Look hard at the evidence column, because that word has a narrow definition here: a test name
and its output, or a command and its output. Not a step ID. Not a sentence explaining what the
code does — that is the code describing itself through the thing that wrote it. If a criterion
shows prose instead of an artifact, it should say pending, and if it says satisfied anyway,
you have found something worth discussing in the retro.
The Dev says out loud: "Implementation done, tests green." Keyboard back to the PO.
The PO does not review the code. The PO checks every acceptance criterion they wrote themselves — with exactly the edge cases from Part 2.
Normal case:
curl -X POST http://localhost:3000/split -H "Content-Type: application/json" -d "{\"amount\": 50, \"tipPercent\": 0, \"people\": 2}"Edge case "0 people" — must return an error, not a calculation:
curl -X POST http://localhost:3000/split -H "Content-Type: application/json" -d "{\"amount\": 50, \"tipPercent\": 10, \"people\": 0}"Edge case "rounding adds up" — the three amounts must total exactly 100:
curl -X POST http://localhost:3000/split -H "Content-Type: application/json" -d "{\"amount\": 100, \"tipPercent\": 0, \"people\": 3}"The most important learning point of the day: you check against your own criteria from the spec, not against a gut feeling. If "actually I'd also like X" occurs to you now — that is not the Dev's mistake. It was not in the spec. That is the next iteration, a new
/sdd-specify.
/sdd-lifecycle
Spec and plan move together into .specs/done/, the spec status becomes Implemented, the plan
status becomes Done, and .memory-bank/activeContext.md is updated. The spec stays as a
versioned living document — the basis for the next iteration.
| Phase | Who | Command | What happens |
|---|---|---|---|
| Setup | PO + Dev | /sdd-setup |
Memory Bank and architecture snapshot |
| Specify | PO | /sdd-specify … |
what and why, success criteria, scope |
| Sharpen | PO | follow-up message | edge cases and product rules |
| Clarify | PO + Dev | /sdd-clarify |
a cold second pass finds what the conversation hid |
| Sign off | PO | — | "Spec approved", swap seats |
| Plan | Dev | /sdd-plan … |
tech stack, module structure, baby steps |
| Read the plan | PO + Dev | — | the cheapest review in the cycle — do not skip it |
| Implement | Dev | Implement T-001. |
code step by step, Verified: filled, plan kept current |
| Verify | Dev | node --test |
tests green, app runs |
| Compile | Dev | /sdd-compile |
verdict + evidence per criterion, swap seats |
| Accept | PO |
curl against the criteria |
check against your own acceptance criteria |
| Close | PO | /sdd-lifecycle |
spec → done/, iteration finished |
Two rows are not commands. That is deliberate: the two moments with the highest leverage in this whole flow are a human reading something, not a tool producing something.
The PO now also wants each person's amount rounded up to the next whole euro.
Run /sdd-specify again against the existing spec, then /sdd-plan. Watch what happens in
Mode C: before editing anything, the plan reads its traceability table in reverse and tells
you which steps and which files that one changed rule reaches.
The second lap is noticeably faster — the Memory Bank already knows the context, and the spec is the starting point rather than a blank page. That is the payoff of writing it down.
Adapted from the Spec-Driven Development hands-on workshop for Product Owners by Gregor Biswanger.
Start here
Reference
Design & interop
- Interop Matrix
- Design Decisions
- Specify Method
- Committing to One Tool
- Migrating from the predecessor
- Releasing
Help