| name | distill |
| description | Use when a system already exists but is not documented — to capture how it actually works, build shared vocabulary around it, and produce a living spec that the team can reason about and discuss. |
Extract a blueprint from an existing system. The system may be partially documented, fully undocumented, or documented in ways that no longer match reality. Capture how things actually work — not how they were intended to work or how someone remembers they work. Code tells you what; tribal knowledge tells you why; a blueprint tells you both.
Code captures what the system does, including bugs and hacks. The blueprint captures what it should do.
Use relationship names and domain concepts, not schema details.
When you find different names in code vs product, pick one and flag the other for cleanup.
If you do not know whether behaviour is intentional, open a question.
If a code path is never reached in production, it does not belong in the blueprint.
Just because code does X does not mean X was intended. Validate with people.
Rephrase at the behaviour level. See core discipline.
Distillation requires both code and people. Code reveals what; people reveal why.
"What part of the system are we documenting?" — full product, one feature, one service?
"What sources do we have?" — code, existing docs, people, Slack history, old tickets
"What is most uncertain or contested?" — start where the blueprint adds most value
"What should the blueprint be used for once it exists?" — onboarding, redesign, debugging?
The "would a stakeholder care?" test — for every detail found in code or conversation:
The "intentional or accidental?" test: designed behaviour → document as requirement. Accidental behaviour → document as open question: "The system currently does X. Was this intentional?"
Before capturing anything, understand the landscape.
From codebase: entry points (API routes, UI actions, webhooks, jobs, consumers), domain models (entities), core flows (services, handlers), external integrations.
From existing docs: product specs, API docs, runbooks, architecture diagrams, old tickets/PRs.
From people: "Walk me through [flow]", "What parts are you least confident about?", "What would a new team member need to know?", "What breaks most often?"
Produce a rough map before writing section files:
Entry points:
- API: /api/orders/*, /api/payments/*
- Webhooks: /webhooks/stripe, /webhooks/shipment-tracker
- Jobs: expire_trials (nightly), send_digests (daily)
Key entities:
- User, Subscription, Order, Payment, Invoice
Core flows:
- Checkout, Subscription renewal, Order cancellation, Refund
External dependencies:
- Stripe (payments), SendGrid (email), Shippo (shipping)
Establish vocabulary before documenting flows. Existing systems often have inconsistencies — codebase uses one name, product uses another, support uses a third.
Signals: classes/tables with different names than the product uses; same concept under multiple names; comments like "same as X".
Resolution: pick the business/product term. Document it; note code names needing cleanup.
Find where data lives and what states it moves through.
In code, look for: enum fields and status columns, state machine libraries, boolean field combinations implying unnamed states, nullable timestamps encoding state.
Extract implicit states — the hardest part. Conditional logic checking field combinations is often an implicit state machine:
if user.is_active and user.verified_at is not None:
elif user.is_active and user.verified_at is None:
elif not user.is_active and user.deactivated_at is not None:
else:
Surface the implicit state machine, give states names, flag: "Is this how this was intended to work?"
Find where state changes happen and trace them.
Look for: status-changing functions, event handlers, webhook receivers, scheduled job bodies, API endpoint handlers (the action, not the endpoint).
For each flow trace: what triggers it, what preconditions must be true, what changes, what is communicated, what happens next.
The scattered logic problem: business logic often spread across multiple layers. Consolidate it:
API handler checks: user.is_active
Model method checks: subscription.status = active
Service checks: payment.status = confirmed
→ The actual preconditions for checkout are all three combined.
Capture once in the blueprint; note that consolidation is needed in code.
Code captures current behaviour including bugs. People carry intent. Walk key scenarios with someone who knows the system, then compare:
- "The code does X, but you described Y. Is one wrong?"
- "This flow has a case you didn't mention. Known edge case?"
- "The code does X here but I couldn't find a reason. Do you know why?"
Distillation reveals:
- Undocumented decisions — things nobody can explain
- Undocumented requirements — behaviour never stated as a requirement
- Missing error handling — happy paths with no failure behaviour
- Stale documentation — docs that no longer match the system
- Behavioural debt — system does something two different ways for historical reasons
Output: blueprint directory — one file per section. Step 2 → terminology.md, Step 3 → domain-model.md, Step 4 → scenarios/, Step 5 → scenario updates + questions.md, Step 6 → questions.md + decisions.md. Update README.md and changelog.md at the end.
No database column types or query syntax
No API endpoint paths or HTTP methods
No framework-specific concepts
No specific library names (unless the integration itself is a domain concern)
Foreign keys replaced with named relationships
Technical status values replaced with meaningful names
Implementation field names replaced with business names
Each concept has exactly one name throughout the blueprint
No "also known as" or "equivalent to" notes (resolve, don't annotate)
Terminology matches what the product and business actually use
Code names that differ from blueprint terms are flagged for cleanup
The behaviour nobody owns — logic nobody can explain. Document as-is, flag as open question.
The workaround that became permanent — "temporary" code that is now core. Document actual behaviour; note history.
The feature no one uses — code paths never triggered in production. Question whether these belong.
The inconsistent state machine — same state reachable via two paths with different outcomes. Document both; flag if intentional.