Covers src/path/to/module.ts — one sentence describing what this module does and why it exists.
Code tells you how something works. A spec tells you what it is supposed to do — and those two things are not always the same.
Without a spec, the only way to understand intended behaviour is to read the implementation and guess which parts are deliberate and which are accidents. Reviews become slower, regressions are harder to catch, and edge cases live only in the memory of whoever wrote the original code.
A spec solves this by making behaviour an explicit, reviewable artefact:
- It is the source of truth for intent. When implementation and spec disagree, that is a bug or an outdated spec — either way, something needs fixing.
- It makes reviews faster. A reviewer can check behaviour against the spec rather than reconstructing intent from code.
- It protects against regressions. Behaviour that is written down is harder to silently break across refactors.
- It onboards new contributors. Anyone can understand what a module does without reading every branch.
- It surfaces design decisions. Deliberate asymmetries, guard conditions, and non-obvious rules are documented where they belong — next to the behaviour they govern.
- Not a test suite — specs describe intent, tests verify it.
- Not API documentation — specs describe runtime behaviour, not public interfaces.
- Not a changelog — specs reflect the current intended state, not history.
- Not implementation comments — specs live in
spec/, not in the source file.
- Copy this file into the subfolder that matches your domain (
auth/,editor/,data/,ui/,images/,pwa/,features/). - Name it in kebab-case matching the primary concept (e.g.
rename.md,guest-gists.md). - Fill in each section. Delete sections that do not apply — a short precise spec beats a long incomplete one.
- Add an entry for the new file in
spec/README.md. - Submit the spec in the same PR as the behaviour it describes. Keeping them together is what makes specs trustworthy.
| Event / condition | Expected behaviour |
|---|---|
| [trigger A] | [what happens] |
| [trigger B] | [what happens] |
- When
[flag/condition]is true: [behaviour] - When
[flag/condition]is false (default): [behaviour]
1. [Step one]
2. [Step two]
3. [Step three]
- [Condition] is skipped / ignored / a no-op.
- On failure: [what happens — logged, thrown, retried?].
[Symbol]is the single source of truth for [concept]; do not duplicate it.
| Condition | Behaviour |
|---|---|
| [network / API error] | [how the UI or data layer responds] |
| [invalid input] | [validation result] |
Note: [Optional — use this blockquote pattern only for deliberate asymmetries, non-obvious design choices, or important caveats that a reviewer might otherwise flag as a bug.]