Introduction
Why Specs Before Code
An e-commerce team's checkout service has a function that stacks discount coupons — a loyalty discount, a seasonal sale, and a referral code can all apply to the same order. Someone, at some point, added a docstring: "Calculates final price by summing all discount percentages and applying the total to the price." It's accurate. It's also completely useless as a safety check, because six months later a customer stacks three 40%-off codes, the function sums them to 120% off, and the checkout page displays a negative price — the store owes the customer money. The docstring didn't catch this, couldn't have caught this, because it was written by looking at the code and describing what it already did. Of course it "matches" the implementation. It's the same bug, described in prose.
This is the specific failure spec-driven development exists to prevent, and it's worth being precise about why, because "write more documentation" is not the fix — that team had documentation. The fix is about when the description of correct behavior gets written and where it comes from.
Two different loops
The traditional loop is think → code → document: an engineer thinks through the problem informally, writes code based on that thinking, and — if there's time, which there often isn't — writes documentation afterward describing what the code does. The documentation, when it exists at all, is derived from the code. It's a mirror, not a check. If the code has a bug, the documentation written by observing the code will describe the buggy behavior as if it were correct, because from the documentation-writer's vantage point, at that moment, it is the behavior.
Spec-driven development inverts the order: specify → generate → validate. The spec — problem statement, acceptance criteria, constraints, test cases — is written first, independent of any implementation, derived from the actual business requirement ("a single discount cannot exceed 100%, and stacked discounts must never produce a negative price") rather than from watching code run. Because the spec exists before the code, it can be used to check the code. It's an independent source of truth, not a mirror of one.
What makes something a spec, not just documentation
A spec is falsifiable against the implementation independent of how the implementation was derived. If a description of behavior was written by reading the code, it can only confirm the code (tautologically) — it was never independent, so it was never in a position to disagree. A spec is written from the requirement, before the code exists, specifically so it's still able to say "the code is wrong" once code exists to compare it against.
The sddbook.com framing is precise here: SDD is not prompt engineering. "Prompts are tactical. SDD is a professional methodology — an architectural pattern that makes specifications executable and enforceable." The point isn't that specs are nicer prose than docstrings. It's that a spec, written first and kept as a durable artifact, is the only thing in this loop that can actually disagree with the code — and disagreement is exactly what catches the discount-stacking bug before a customer does.
This also explains why "we already have good documentation" is not a rebuttal to "you need specs." Documentation and specs can look nearly identical on the page — both might be a markdown file describing how discounts stack — and still play completely different roles depending on which came first and where the description originated. The test isn't how detailed or well-written the artifact is. It's whether it was derived from the requirement (independent, therefore able to disagree with the code) or derived from the implementation (a mirror, therefore unable to). A team that skips this distinction often discovers it the same way the checkout team did: not in a design review, but in a customer support ticket asking why the store just refunded more than the order cost.
A team's onboarding docs say "the retry logic waits 2 seconds between attempts, up to 3 retries." An engineer reads this, assumes it's a spec, and builds a new feature that depends on this exact retry timing. It turns out the docs were written by reading the current code six months ago, and the actual retry logic was since changed to exponential backoff with up to 5 retries — nobody updated the docs. What does this reveal about the difference between documentation and a spec?
Documentation written by observing code is a snapshot, not a contract — nothing enforces that it stays synchronized as the code evolves, because it was never the thing the code answers to. A spec that's actively validated against (the drift gate covered later in this phase) is different: changing the code without updating the spec is either blocked or immediately visible, instead of silently rotting for six months like this onboarding doc did.