Design Doc Authoring
You are writing a living design doc. It has one body and two lives:
- While the work is in flight it is a working artifact: mutable, edited freely, expected to track the code as the design emerges.
- When the work is done it is reader documentation: the accurate description of the shape that was built, with pointers to why.
One rule makes a single document serve both lives: describe the system,
never the work. "The relay claims rows by setting claimed_at" stays true
no matter what order the work happens in. "First we add the column, then we
build the relay" is stale the moment reality reorders it. Present tense, end
state, no sequencing.
What a design doc is - and isn't
A design doc is:
- A description of the solution shape: components, responsibilities, boundaries, data flow, and the contracts between parts.
- The staging ground for decisions that are not yet stable enough to be ADRs.
- Mutable while
draftoractive. Editing it is normal, not ceremony.
A design doc is not:
- A plan. No phases, no task lists, no checkboxes, no rollout sequences, no "Steps". Ordering of work lives in tickets or in chat, and dies there.
- A decision record. A decision that is architectural, non-obvious, and has stabilized graduates to an ADR. The doc links to it; it never restates the rationale.
- A devlog or status report. No dated session entries, no done/remaining tallies.
- A tutorial. Onboarding material belongs in
README.mdor a docs site.
If the document grows past roughly 1500 rendered words, or acquires headers like "Phase 1", "Tasks", "Migration steps", stop. Either the shape is really several docs (split by subsystem) or plan content is creeping in (evict it to a ticket or chat).
When to write one
Write a design doc when both are true:
- The shape spans multiple components, files, or services, so a fresh-context reader (human or agent) needs the picture to work on it.
- The work spans multiple sessions, people, or agents - someone will arrive later without today's context.
Route elsewhere when:
- The outcome is a single settled decision → write an ADR (
adrskill). - The work is small and you hold full context until it ships → no doc; the thinking lives in chat and dies there.
- What needs recording is order or ownership of work → a ticket, with the design doc (or ADR) as its context link.
One doc per body of work. Do not grow a repo-wide monolith, and do not shard one feature's shape across five files.
Where the doc lives
This skill does not decide path. Resolve the destination in this order:
- The user's explicit instruction.
- Where the repo (or the effort) already keeps design docs - e.g. a
docs/design/directory, or an effort-scoped directory that already holds related design material. - The convention documented in
README.md,CONTRIBUTING.md, or an agent-instruction file (AGENTS.md,CLAUDE.md).
If none of the above resolves the path, stop and ask. Do not invent a location.
File naming
- Pattern:
kebab-case-solution-name.md, e.g.partial-releases.md,outbox-relay.md. - Name the solution, not the activity:
partial-releases.md, notimplement-partial-releases.mdorpartial-releases-plan.md. - No numbering. Design docs do not form an ordered series the way ADRs do; if the directory already numbers its docs, match its convention.
Template
Use templates/design-doc-template.md.
Copy the file verbatim, then fill it in. Sections marked optional may be
removed when empty; do not invent sections that are not in the template -
in particular, no plan-shaped sections.
Frontmatter conventions
status and date are required; scope is optional. Use these formats so
design-doc directories stay mechanically consistent:
| Field | Required | Format |
|---|---|---|
status |
Yes | One of: draft, active, stable, superseded by <file> |
date |
Yes | ISO YYYY-MM-DD. Last meaningful change. |
scope |
No | Short phrase naming subsystem or service (management partial releases). |
Do not invent new frontmatter fields.
Status lifecycle
draft --> active --> stable --> superseded by <file>
^ |
\------------/ (work on the shape resumes)
- draft: the shape is being explored; implementation has not meaningfully started. Everything may change.
- active: implementation is underway. The doc is expected to change and
is reconciled against the code when the user calls for it (below). An
activedoc that contradicts the code is a bug in the doc. - stable: the overall shape shipped and the doc was reconciled from the
actual code. It is now reader documentation. Small in-place edits -
typos, clarifications, accuracy fixes - are fine and need no ceremony.
Changes to the shape itself mean either work has resumed (flip back to
active) or the design is being replaced (supersede). This is not ADR-style immutability. - superseded by : replaced by a newer design doc. Update the old doc's status; do not delete it.
The contrast with ADRs is deliberate: an accepted ADR's body is immutable and changes only by supersession; a design doc stays editable for as long as it lives. That is why decisions ripen here and are recorded there.
Reconciling: the doc follows the code
Reconciling is deliberate, not continuous. It happens when the user calls for it, typically after a significant coding session - not after every change. Do not reconcile on your own initiative without a really good reason; instead, when a substantial slice of work has landed, nudge once: "the design doc may have drifted - want to reconcile it?" A doc updated after every edit is a devlog with extra steps.
A reconcile does the following:
- Re-read the doc against the actual code. Code that is not yet built is fine - the doc describes the end state. Code that was built differently than described is a doc bug: fold the change into the doc. List each fold for the user so they can veto ones that are actually code bugs; never leave doc and code silently disagreeing.
- Prune answered open questions. The answer moves into Shape or Decisions; the question disappears.
- Review the Decisions section: graduate entries that now meet the ADR bar (below).
- If the work is complete, do a final full pass from the code and flip the
status to
stable.
Reconciling never produces plan content. "What remains to be built" is visible from the doc-code delta; do not write it down as steps.
Graduating decisions to ADRs
The default route: decisions ripen in the design doc and are recorded as ADRs after they have survived contact with the code, typically at a reconcile. An accepted ADR written before the design stabilizes gets superseded within days or, worse, illegally edited.
The second route: when a decision is meant to constrain the
implementation up front - an architectural guardrail the feature must
follow - open the ADR as proposed alongside the design doc and flesh
both out during implementation. The doc links to it like any graduated
decision; the ADR's own status field carries its maturity. Promote to
accepted when it has held, on the user's word (per the adr skill).
Either way, two things stay true: no accepted ADR for a decision still
in motion, and no rationale duplicated between doc and ADR.
Each entry in the doc's Decisions section carries a state:
open- may still change; lives here and nowhere else.settled- holding, but fails the ADR bar (not architectural, or obvious); it stays a one-liner here permanently.ADR-NNNN- has an ADR (proposed or accepted); the entry's rationale is replaced by the link.
If implementation contradicts an accepted ADR, that is a supersession conversation, not a design-doc edit.
Writing rules
Describe the system, not the work
Present tense, end state. Any sentence about ordering, effort, or who does what is plan content - evict it. This single rule is what lets the doc survive implementation churn and still read as documentation afterward.
Be ruthlessly concrete
Name the real modules, tables, endpoints, and event types
(BulkVoucherOperation.selection, POST /v1/batch/{id}/release), not
roles ("the persistence layer"). ASCII diagrams are welcome where prose
gets clumsy.
Keep it concise enough to actually be read
The doc is re-read at the start of every session that touches the work, by humans and agents both - and humans skip or skim long docs. A skimmed design doc is a failed design doc. Target a five-minute read. Say each thing once: no restating, no second emphasizing sentence, no filler ("importantly", "note that", "as mentioned above"). Every sentence earns its place. When the doc outgrows the target, split by subsystem or evict creeping content - do not summarize the shape into vagueness.
Open questions are first-class
While a doc is draft or active, the Open questions section is where
uncertainty lives honestly. An active doc with no open questions is either
finished or lying. Prune at reconcile.
Cap code at interfaces
Schemas, signatures, endpoint shapes, enums: yes - that is shape. Implementations: no. Once real code exists, prefer linking to it over inlining it.
Anti-patterns
Reject these whether writing or reviewing a design doc:
- Plan creep. "Phase 1", "Step 3", task checkboxes, rollout sequences, effort estimates. Plans are chat-ephemeral or tickets; a design doc that contains a plan starts rotting the day implementation starts.
- Devlog creep. Dated entries narrating what happened. Git history already does this. Reconciling after every small change is the same disease in slow motion.
- Rationale essays. Multi-paragraph why-we-chose-X belongs in an ADR (if it meets the bar) or gets cut to one line (if it does not).
- Restating ADRs. Link, never duplicate. Duplicated rationale forks.
- Plan fidelity. Treating an
activedoc as a contract and calling code changes "deviations" to be resisted or ceremonially approved. The doc follows the code; divergence is information, not disobedience. - Premature acceptance. Accepting ADRs for decisions still in motion.
A
proposedADR evolving alongside the doc is fine; anacceptedone superseded a week later is churn. - Zombie stable. A
stabledoc contradicting the code it describes. Reopen it (active) or supersede it; never leave it lying. - Speculative shape. Documenting components nobody is building ("later this could also..."). Out of scope exists for exactly this.
Workflow
- Confirm a design doc is the right artifact (see "When to write one"). If the request is really an ADR or a ticket, say so.
- Resolve the destination directory (see "Where the doc lives").
- Read any sibling design docs for tone, length, and conventions.
- Copy
templates/design-doc-template.mdto the new file. - Draft: Shape in present tense, seed Decisions (
open) and Open questions with the genuinely undecided items. Default status:draft;activeonce implementation starts. - During implementation: edit freely as the shape moves; after a significant slice lands, nudge the user about reconciling (see "Reconciling").
- Graduate stabilized decisions via the
adrskill; open a guardrail ADR asproposedalongside the doc when the user wants one up front. - When the work ships: final reconcile from the code, prune, flip to
stable.
Stop conditions
Stop and ask the user if:
- The destination directory is unclear and the routing rules do not resolve it.
- The doc you are about to write fails the "When to write one" bar - say chat or an ADR suffices, and ask before proceeding.
- The shape you are documenting contradicts an accepted ADR. Resolve the supersession direction first; never encode the contradiction silently.
- The user asks to embed phases, task lists, or schedules. Push back once, offering a ticket as the home; comply only on explicit insistence.
- You are asked to change the shape described by a
stabledoc and it is unclear whether work is resuming (flip toactive) or the doc should be superseded.