Prompts vs Specs
A prompt and a spec are the same thing — a statement of intent — with one difference that changes everything: lifespan. A prompt governs one session and evaporates. A committed spec governs every future session, every teammate's agent, every re-run after a failed attempt, and every "wait, why does it work this way?" six months later. Treating them as interchangeable is the single most common process error in agent-assisted teams.
Why the lifespan difference dominates
When intent lives only in a prompt:
- Re-runs drift. The second attempt at a feature gets a from-memory retelling of the requirements — close to the original, but not identical. You now have two slightly different sources of truth, one of which is gone.
- Nobody else can operate. A teammate picking up the feature has the code and a chat transcript they'll never read. The decisions — why per-key rate limiting, why 429 over queuing — exist nowhere.
- There's no arbiter. When the output looks wrong, "wrong relative to what?" has no answer. You review against your recollection, which the agent's fluent output actively reshapes.
- Compaction eats it. On long sessions, Claude Code summarizes older conversation to reclaim context; your precisely-worded constraint from hour one survives as a paraphrase. See Context and Instruction Hierarchy.
A spec file has none of these problems, plus properties chat can never have: it's versioned, diffable, reviewable in a PR, and readable by the agent on demand in any session.
The escalation rule
If you've said it twice in chat, it belongs in a file.
Twice is the signal that the knowledge is durable, not task-scoped. Where it goes depends on its scope:
| You keep repeating… | Escalate to |
|---|---|
| A convention or invariant ("integer cents, never floats") | Project CLAUDE.md — see CLAUDE.md and AGENTS.md |
| Feature requirements or acceptance criteria | A spec under specs/ or docs/specs/, committed |
| How a subsystem works | Repo docs — see Docs as Memory |
| Why an architectural choice was made | An ADR — see Decision Records |
After escalation, prompts shrink to pointers. That's the end state to aim for: files carry truth, prompts carry direction.
Weak vs strong: the same feature, two ways
Weak — the chat-only version:
Add CSV export to the invoices list. Include the usual columns, handle
big exports sensibly, and make it match how the rest of the app does
downloads.Every load-bearing decision is delegated by vagueness: "usual columns" (which? localized headers? which date format — the EU customers filed a bug about this once), "big exports sensibly" (streaming? background job + email? row cap?), "how the rest of the app does downloads" (there are two patterns in the codebase; the agent will pick one). The agent ships something plausible. Three weeks later a teammate re-runs a variant of this prompt to add PDF export and gets different answers to all three questions. This is the six-silent-decisions problem in the wild.
Strong — committed spec plus pointer prompt. First, specs/invoice-csv-export.md (abridged):
# Spec: Invoice CSV export
## Behavior
- Export respects the *currently applied* list filters and sort.
- Columns: invoice_number, customer_name, issued_at (ISO 8601),
due_at, status, total (integer cents), currency.
- Header row uses the user's locale via the existing i18n keys.
## Scale
- ≤ 10k rows: stream synchronously with chunked transfer.
- > 10k rows: enqueue export job (existing `jobs/` queue), email a
signed download link, expire after 72h.
## Non-goals
- No XLSX. No column selection UI. No scheduled exports.
## Acceptance
- Export of a filtered list matches the on-screen rows exactly.
- 50k-row export completes without exceeding 256MB worker memory.Then the prompt:
Implement specs/invoice-csv-export.md. Follow the download pattern in
src/exports/statements.ts, not the legacy one in src/reports/. List
any spec ambiguities before writing code.The delta: every decision the weak prompt left to chance is now written, reviewable, and permanent. The prompt adds only session-scoped direction — which precedent to follow, and an instruction to surface ambiguity instead of resolving it silently. Re-runs, teammates, and next quarter's PDF-export task all inherit the same truth. Full spec-writing technique in Writing Effective Specs; complete worked examples in Spec Examples.
The spec-code feedback loop
A spec is not write-once. It's a control loop with the code:
The two failure edges matter equally. When verification fails because the code is wrong, you re-prompt — cheaply, because the spec didn't change. When it fails because the spec was wrong (you discover mid-build that 10k rows was the wrong threshold), you amend the spec first, then re-run — so the correction is permanent instead of living in one session's chat. Specs that stop receiving these updates go stale and start lying; Keeping Specs Alive covers the maintenance discipline.
When a prompt alone is fine
Not everything deserves a spec. Renames, one-file bug fixes with a reproducing test, mechanical refactors, research questions — the prompt is the whole intent, and it's checkable on sight. The threshold: would a second person, or a second session, ever need these decisions? No → prompt. Yes → file. When in doubt, count the silent decisions the agent would have to make; more than two or three, write the spec.
Related
- Specs over Prompts — the operational version of this argument: adopting spec-driven agent work on a real team.
- Writing Effective Specs — structure, sizing, and the acceptance-criteria discipline.
- Mental Models — prompt-as-temporary-spec and spec-as-source-of-truth in the layered control stack.
- Prompt Anatomy — what still belongs in the prompt once specs carry the truth.
- Context and Instruction Hierarchy — why chat-resident knowledge decays even within a single session.
- Idea to Spec — the workflow for getting from a vague feature idea to a committed spec.