8. Spec-Driven Development
Spec-driven development reverses the usual order: you write what the software must do
before you write any code, then hand those documents to an AI to drive implementation.
This chapter covers three spec files — Constitution.md, Structure.md, and Spec.md
— and the workflow that uses them.
Why specs before prompts?
- A prompt without a spec is ambiguous. A spec makes the constraints
explicit and auditable before the AI writes a line of code.
- The spec becomes the acceptance criterion: generated code that violates
the spec is wrong by definition, regardless of whether it compiles.
- Separating “what must be true” (Constitution) from
“how it is organized” (Structure) from “what each piece
does” (Spec) keeps each document small and focused.
- Specs are reusable — the same Constitution can govern implementations
in multiple languages.
8.1 Constitution.md
States values and hard constraints that apply to all generated code. Written in
plain imperative sentences; one page maximum. Examples:
- Never use
unwrap() in library code.
- All public functions must have a doc comment.
- No external dependencies beyond the standard library.
- Functions must not exceed 30 lines.
8.2 Structure.md
Defines the package layout, file names, module boundaries, and dependency rules.
The AI reads this before generating any file so it knows where each piece belongs.
## Package Layout
src/
lib.rs -- public API re-exports only
config.rs -- configuration types; no I/O
scanner.rs -- directory traversal; depends on config only
matcher.rs -- regex matching; depends on config only
reporter.rs -- output formatting; depends on scanner and matcher
main.rs -- entry point; depends on all others
## Rules
- scanner.rs must not import from matcher.rs
- reporter.rs must not perform I/O beyond writing to a provided Writer
8.3 Spec.md
Documents each public function or type: signature, preconditions, postconditions,
and one example. Written before implementation; updated when requirements change.
## fn scan(root: &Path, config: &Config) -> Result<Vec<Match>>
- root must exist and be a directory; returns Err otherwise
- traverses root recursively, following symlinks if config.follow_symlinks
- returns all Match records where config.pattern matches file content
- excludes files whose extension is not in config.extensions
- example: scan(Path::new("src"), &cfg) -> Ok(vec![Match{path: ..., line: 3}])
8.4 The Workflow
- Write Constitution.md — values and constraints, no code yet.
- Write Structure.md — package layout and dependency rules.
- Write Spec.md — signatures and contracts for each public item.
- Start a CLI session or API call; load all three files as context.
- Ask the AI to implement one file at a time, citing the spec for each function.
- Run tests after each file; feed failures back into the session.
- After all files pass, ask the AI to verify the implementation against
Constitution.md and report any violations.
8.5 Epilogue — Connecting to the SWDev Track
Spec-driven development is the Code Track’s answer to the SWDev track’s
design chapter: Constitution maps to architectural constraints, Structure maps to
package design, and Spec maps to specification. The difference is the AI is now
the implementer. For a deeper treatment of the design concepts behind these
documents, see the
SWDev Story: Software Design
chapter.
8.6 References