spec-before-code
verified8d4f2414-0820-4b5d-9e89-4afbf3a38430
Write a short spec (Objective, Acceptance Criteria, edge cases, non-goals) before touching code. Use when asked to build a feature and the requirements are fuzzy or underspecified.
Metadata
Skill file
# Spec Before Code
Use when you are asked to build something and the requirements are fuzzy,
underspecified, or could mean several different things. Writing a one-page spec
first converts a vague ask into a checkable contract — and surfaces disagreements
*before* any code is written, when they are cheapest to fix.
## 1. The 4-block spec template
Write exactly four sections, in this order, in a single markdown file:
```markdown
# <Feature> — Spec
## Objective
One to two sentences. What problem does this solve, for whom, and why now.
No implementation details.
## Acceptance Criteria
A bulleted list of testable, Given/When/Then statements.
Each must be checkable by a machine (test, curl, SQL, CLI).
## Edge Cases
Explicit bullets for: empty input, huge input, malformed input, missing
permissions, concurrent access, and what "does nothing" means.
## Non-goals
What we are deliberately NOT doing in this change, so scope does not creep.
```
Keep it to **one page**. If it does not fit on one page, it is under-specified
(too many blanks) or over-specified (too much implementation) — both are bugs.
## 2. Clarifying questions — the fixed list
Before (or while) writing the spec, ask these six questions if they are not
already answered. They are the minimum set that blocks a correct implementation:
| # | Question | Why it matters |
|---|---|---|
| 1 | What are the **inputs** (types, formats, sources)? | You can't validate what you can't name. |
| 2 | What are the **outputs** and side effects? | "Done" must be observable. |
| 3 | What happens on **failure**? | Defines error contract, retries, rollback. |
| 4 | What is the expected **scale** (data size, QPS, concurrency)? | Separates a script from a service. |
| 5 | What **backward compatibility** must be kept? | Prevents breaking callers/DB/API. |
| 6 | What is **out of scope**? | The non-goals section writes itself. |
## 3. Worked example: "add a delete-user endpoint"
```markdown
# Delete User — Spec
## Objective
Allow an admin to permanently remove a user account and all owned data, with
an audit record, without allowing self-deletion or double-deletion.
## Acceptance Criteria
- Given an admin token and an existing user id,
When DELETE /users/{id} is called,
Then the user row is removed, owned rows are cascade-removed, and 204 is returned.
- Given a non-admin token, When the same call is made, Then 403 and no data changes.
- Given a user id that does not exist, Then 404 and no error is logged as a crash.
## Edge Cases
- Empty id / malformed id -> 400.
- Self-deletion (admin deletes their own id) -> 400.
- Concurrent double-delete -> second call returns 404, not 500.
- Deletion mid-transaction -> all-or-nothing rollback.
## Non-goals
- No soft-delete / tombstone mechanism (separate feature).
- No bulk delete endpoint.
- No email notification to the deleted user.
```
## 4. Confirm before building
Once the spec is drafted, **get explicit sign-off** from the requester before
writing code. State the ambiguities you resolved and the non-goals you assumed.
A 5-minute spec correction now beats a full rework after implementation.
## 5. The bug-fix spec (shorter variant)
For a bug fix, a full 4-block spec is overkill. Use the compact variant:
```markdown
# Fix: <bug title> — Spec
## Objective
The observed behavior, the expected behavior, and a link to the bug report.
## Acceptance
- Given <the repro input>, When <triggered>, Then <correct output>, and no
regression in <adjacent behavior>.
## Non-goals
Not fixing <related-but-separate issues>.
```
A bug-fix spec still answers "what does done mean" — it just does not need an
edge-case inventory, because the bug report already defines the trigger.
## 6. When the spec must change mid-build
If you discover mid-implementation that the spec is wrong (a new edge case, or a
non-goal that is actually required):
1. **Stop** and update the spec file first.
2. **Re-confirm** the changed section with the requester.
3. **Record the change** in the spec (a "Changes" note at the bottom with date +
reason).
Do not silently diverge from the spec. A spec you stopped following is worse than
no spec — it misleads the next reader about what was actually agreed.
## Guardrails
- Do **not** put implementation detail (function names, class hierarchy, SQL) into
the spec. That is gold-plating and locks in a design prematurely.
- Do **not** build before the spec is confirmed. Unconfirmed specs are guesses.
- Do **not** write acceptance criteria that require human judgment — every one must
be machine-checkable.
- Keep it one page. Resist the urge to "just add more detail."
## Pitfalls
- **Gold-plating** — writing a design doc instead of a spec; the spec stops being
about *what* and becomes *how*.
- **Never confirming** — building against your own interpretation, then discovering
the user meant something else.
- **Vague acceptance criteria** — "works correctly", "fast enough", "good UX" cannot
be checked by any command.
- **Missing non-goals** — without an explicit out-of-scope list, scope creeps silently.
- **Skipping edge cases** — the happy path spec is the one that explodes in prod.
## Verify / Checklist
- [ ] Spec has exactly four sections: Objective, Acceptance Criteria, Edge Cases, Non-goals.
- [ ] Every acceptance criterion is Given/When/Then and machine-checkable.
- [ ] Edge cases cover empty, malformed, large, permission, and concurrent inputs.
- [ ] Non-goals list at least one thing explicitly excluded.
- [ ] Spec fits on one page.
- [ ] The six clarifying questions are answered (or explicitly N/A).
- [ ] Requester has confirmed the spec before code is written.
Attached files
No attached files.