Decision Records (RFC / ADR)
Purpose
Make technical decisions auditable. The "why we chose X" should not require asking the engineer who chose it — it should be retrievable from the repo.
Universal — RFC/ADR format is framework-agnostic; only the types of decisions worth recording vary slightly per stack.
Procedure
Pick the format: RFC vs ADR
- RFC (Request for Comments): proposes change BEFORE implementation, opens discussion
- ADR (Architecture Decision Record): records decision AFTER it's made
- Both can coexist; ADR often follows from accepted RFC
- Decision trigger — weight the process by reversibility (Bezos's one-way vs two-way doors): a hard-to-reverse / costly-to-undo "one-way door" decision, or one 2+ engineers must weigh in on, warrants an RFC before commitment. A cheap-to-reverse "two-way door" needs at most a short ADR.
- Don't write an ADR for everything — over-documentation rots the same way an unread debt registry does (see
tech-debt-management). Reserve records for significant, contested, or hard-to-reverse decisions; trivial/local choices need only a code comment or the PR description. Keep each to ~1 page. - RFC workflow: draft → circulate → a fixed comment period → decision (with a deadline, or it stalls forever) → the accepted RFC becomes or produces an ADR.
Use the standard ADR template (Nygard) for simple, decided-by-conversation calls
# ADR-NNN: <decision title> **Status**: Proposed / Accepted / Rejected / Deprecated / Superseded by ADR-XXX **Date**: YYYY-MM-DD **Deciders**: <who made the call> ## Context What problem prompts this decision? Constraints, requirements, who's affected. ## Decision We chose X. ## Consequences - Positive: <gains> - Negative: <costs / risks>For decisions with 3+ real alternatives, prefer MADR (Markdown Architectural Decision Records) — adds explicit decision drivers and per-option pros/cons evaluation:
# ADR-NNN: <decision title> ## Status: Accepted | Date: YYYY-MM-DD ## Context and Problem Statement ## Decision Drivers - Driver 1, Driver 2, ... ## Considered Options - Option A: pros / cons - Option B: pros / cons - Option C: pros / cons ## Decision Outcome Chosen option: "B", because <evaluation summary>. ## ConsequencesFor frontend specifically, the high-leverage ADR types (universal across React/Vue/Svelte/Angular)
- State management strategy — see
state-management-decisions - Render strategy per route family (server-rendered vs static vs client-only) — see
render-strategy-decision - Styling system (utility-first like Tailwind, CSS-in-JS, CSS Modules)
- Form approach (server-driven vs client-validated) — see
form-ux - Auth strategy (cookies vs JWT vs platform auth like Supabase/Auth0/Clerk)
- State management strategy — see
Link decisions back to code
- Reference ADR number in code comments:
// See ADR-007 for why we use Zustand here, not Context - In commit messages:
feat(state): migrate user profile to Zustand [ADR-007]
- Reference ADR number in code comments:
Treat ADRs as living documents
- When context shifts: append a dated note rather than rewriting
- When decision is superseded: mark Status as "Superseded by ADR-XXX" and keep the file (history matters)
- Full status set: Proposed / Accepted / Rejected / Deprecated / Superseded — and record rejected decisions too: "we considered X and rejected it because Y" is what stops the team re-litigating the same debate in six months
- Never silently delete an ADR — the history of "why we no longer do this" is itself valuable
Make ADRs accessible
- Store in
docs/adr/directory in the repo - Index file (
docs/adr/README.md) listing all ADRs with one-line summaries - Numbered sequentially (ADR-001, ADR-002, ...)
- Store in
Write for the absent reader
- Assume reader has zero context
- Avoid jargon without definition
- State trade-offs honestly — list the cost of the chosen option, not just benefits
- The highest-value content is why the alternatives were rejected, not just why the winner won — that's what prevents re-debate. Keep it short (~1 page); long ADRs don't get written or read
Completion Criteria
- ADR follows Context / Options / Decision / Consequences format
- Process weighted to reversibility — no ADR ceremony for trivial/easily-reversible choices
- Trade-offs documented honestly (cost of chosen path stated)
- Rejected alternatives and their rationale recorded (not just the chosen option)
- Related code references the ADR number
- Indexed in
docs/adr/README.md - Reviewers (or future-self) can understand without external context
Output
- ADR file:
docs/adr/ADR-NNN-<kebab-case-topic>.mdfollowing Nygard (simple) or MADR (3+ alternatives) template - Index entry: append one line to
docs/adr/README.md—- [ADR-NNN: <title>](ADR-NNN-<topic>.md) — <status> — <one-line summary> - Inline code reference:
// See ADR-NNN for rationaleat affected decision points - Commit format:
docs(adr): ADR-NNN <title>— keep ADR additions in their own commit, separate from code changes that reference them - Status updates: when superseded, edit Status field and append
## Notessubsection with date + reason (don't delete old ADRs)
Implementation
React + Next.js (default)
- Store ADRs in
docs/adr/(ordocs/architecture/decisions/) - Number sequentially (
ADR-001,ADR-002, ...) - Link from code via comment:
// See ADR-007 for rationale - Tooling (optional):
adr-toolsorlog4brainsautomate numbering + index generation; otherwise keep it manual - High-leverage ADR types for this stack: Zustand vs Context vs TanStack Query; SSR vs SSG vs ISR per route; Tailwind vs CSS-in-JS
Other stacks
- Vue / Nuxt: same ADR location and numbering; high-leverage types: Pinia vs provide/inject vs TanStack Query; Nitro server presets; useFetch caching strategy
- SvelteKit: same; high-leverage types: Svelte stores vs context vs +server load patterns; prerender vs ssr per +page.ts
- Angular: same; high-leverage types: signals vs RxJS vs NgRx; standalone vs NgModule; lazy-loading boundaries
- All stacks: ADR format itself is framework-agnostic — only the examples of decision types differ. The Nygard and MADR templates work for any technical decision
Related skills
new-tech-evaluation— every adoption/rejection of a library produces an ADRtech-debt-management— large debt migrations need an ADR for the chosen approach
Reference
- Key insight encoded: Treat ADRs as living documents — append dated notes when context shifts rather than rewriting. For frontend, the high-leverage ADR types are state-management strategy, render strategy (SSR/SSG/ISR/CSR), and styling-system choice — these decisions reverberate for years and benefit most from explicit rationale. Weight the process by reversibility (one-way vs two-way doors) and don't over-document — an unread pile of trivial ADRs rots like a stale debt registry. The most valuable content is why alternatives were rejected, which is what prevents the same debate resurfacing.