Architecture Decision Records (ADRs) and Decision Process for Engineering Teams
Fast recall
Quick revision
Core answer
ADR theater; decision paralysis; no documentation; over-process
Discuss ADR + RFC + C4 + ArchUnit; lightweight async process; scaling decisions across teams; preventing drift
Interview-ready guide
Interview answer
Architecture decisions on fast-moving teams need lightweight, written, versioned records.
The standard pattern: Architecture Decision Records (ADRs)
- Originally proposed by Michael Nygard
- A 1-page Markdown file in your repo with:
- Title
- Status (proposed/accepted/superseded/deprecated)
- Context (forces and constraints)
- Decision (what we're doing)
- Consequences (positive, negative, neutral)
When to write one
- Significant
- Irreversible-ish
- Affects multiple teams
- Not for trivial choices
The process
- Someone drafts
- Async review (1-2 weeks)
- Small synchronous discussion if needed
- Accept or supersede
Avoid two failure modes
- Decision paralysis (every choice becomes 6-week debate; ship nothing)
- Architectural drift (decisions made implicitly in PRs; nobody knows why; engineer rotation loses context)
Senior teams use ADRs
- To record the why so future engineers (including future-you) understand context
Pair ADRs with
- RFCs for larger proposals before deciding
- C4 diagrams for architecture visualization
- Fitness functions / ArchUnit tests to enforce invariants
- Architecture review forums (light, not heavy)
- Principal engineer office hours for guidance
Common mistakes
- Writing ADRs after the fact as documentation theater
- Treating ADRs as design specs (they're decision records)
- Ignoring ADRs in code review
- No ownership for review
Senior move
- Make ADRs part of the engineering rhythm
- Referenced in PRs, revisited annually, used to onboard new hires
Why architecture decisions need process
- Future engineers need to understand why X was chosen (and why not Y)
- New team members need historical context to onboard
- Decisions reveal trade-offs — documenting forces explicit thinking
- Architectural drift happens when decisions are implicit; over years, system becomes incoherent
- Re-litigation waste — same debates every 6 months without record
Most teams either
- Over-process: every decision a 6-week design review; nothing ships
- Under-process: decisions in Slack threads or PR comments; no record; drift
Right answer is lightweight, written, versioned: ADRs
ADR detail
- Proposed by Michael Nygard 2011
- Now standard at Spotify, GitHub, Thoughtworks, AWS, many others
- Example ADR-007: Use Postgres for Transactional Storage
- Status: Accepted (2024-03-15)
- Context: need transactional storage for orders/payments; team has Postgres expertise; ops has playbooks; considered Postgres/MySQL/DynamoDB/CockroachDB; ~1k tx/s year 1, ~10k year 3
- Decision: PostgreSQL 16 hosted on AWS RDS; logical replication for read replicas; consider Citus or sharding at >5k tx/s
- Consequences: positive well-known, mature, rich SQL, ACID, JSON; negative vertical scaling limits, need sharding strategy if exceeds projection; neutral lock-in to RDS, can be migrated but cost
- References: related ADR-003 event sourcing patterns; superseded none
That's it. Versioned in repo (docs/adr/). Numbered sequentially. Status drives lifecycle.
Variants
- Markdown Architectural Decision Records (MADR): more structured template
- Y-statements: 'In the context of X, facing Y, we decided Z to achieve W, accepting V'
- Lightweight ADRs: Title + Decision + Context + Consequences
When to write an ADR
Write for decisions that are
- Significant: shapes architecture, affects multiple teams, hard to change
- Irreversible-ish: can be reversed but at material cost (database choice, framework, language)
- Cross-team: API contract, shared library, platform pattern
- Surprising/contrarian: 'we chose to NOT use X' — future engineers will wonder why
- Foundational: tech stack choice, deployment topology, security posture
Don't write for
- Variable naming, code style (use linters)
- Library version bumps
- Implementation details inside one service
- Trivial choices reversible with one PR
Test: 'Will someone in 2 years ask why we did this?' — if yes, ADR.
Lightweight process
- 1Draft (1-2 hours): write ADR; submit as PR
- 2Async review (1-2 weeks): tag relevant teams + principal eng; comments on PR
- 3Sync discussion if needed (30 min): only if open questions remain
- 4Accept or supersede: merge with status updated
- 5Implement: track in normal sprint work
- 6Revisit annually: still valid? supersede if not
Key: time-box review. If 2 weeks pass without resolution, escalate to principal engineer for tiebreaker. Don't let proposals languish.
Where to keep ADRs
- In relevant repo's docs/adr/ (close to code that implements them)
- For org-wide decisions: central architecture repo
- Some teams use Confluence/Notion; works but harder to version
- Markdown in Git is gold standard: versioned, reviewable, diffable
Tooling
- adr-tools CLI: adr new 'Use Postgres' creates next-numbered file
- MADR template: more structured
- Log4brains: ADR website generator
- ArchUnit: Java library to enforce architectural invariants in tests
- C4 model + Structurizr: diagrams as code; pairs with ADRs
- Backstage (Spotify): catalog services + decisions
ADR vs RFC
- RFC (Request for Comments): longer proposal before decision; explores options
- ADR: shorter record of the decision
- Many teams: RFC for big decisions; ADR captures chosen path
- Some teams use ADR alone for medium decisions
Example RFC → ADR flow
- 'How should we handle async event processing?' → RFC explores Kafka vs SQS vs RabbitMQ vs custom
- After 2 weeks of debate → ADR-012: Use Kafka for high-throughput events
- 6 months later → ADR-018 supersedes if Kafka isn't working
Diagrams pair with ADRs
- C4 model (Brown):
- Level 1 Context — system in environment
- Level 2 Container — services/databases
- Level 3 Component — inside a service
- Level 4 Code — class diagrams (rare)
- Diagrams as code: Structurizr DSL, Mermaid, PlantUML, draw.io with versioned XML
Avoiding drift: fitness functions
- Architectural drift = gap between intended architecture and actual, growing over time
- ADRs document intent; fitness functions enforce it
Fitness functions (Ford/Parsons/Kua)
- Automated tests for architectural invariants
- Run in CI; fail builds when violated
- ArchUnit examples:
- controllers_only_call_services: noClasses().that().resideInAPackage('..controller..').should().dependOnClassesThat().resideInAnyPackage('..repository..')
- services_should_be_annotated: classes().that().resideInAPackage('..service..').should().beAnnotatedWith(Service.class)
- no_cycles: slices().matching('..(*)..').should().beFreeOfCycles()
- Other: 'no service depends on more than 5 others', 'P99 latency below 500ms in load test', 'startup time below 30s'
Architecture reviews (light, not heavy)
- Office hours: principal engineers available 1-2 hours/week; teams bring questions
- Architecture forum: monthly; teams present significant decisions; peer review
- Pre-work consultation: principal engineer reviewer on RFCs before formal review
- Avoid design review committees: slow, hostile, often political
Decision rights
- Trivial: any engineer
- Service-internal: tech lead
- Cross-service/cross-team: ADR + async review
- Foundational/org-wide: ADR + RFC + principal eng + leadership input
- Compliance/security/cost-impacting: include security/finance/SRE
Common pitfalls
- 1ADR theater: writing ADRs after the fact for documentation; loses decision benefit
- 2No follow-through: ADR accepted; nobody implements; nobody enforces
- 3Over-process: every decision is 6-week ADR; teams give up
- 4Under-process: implicit decisions; drift; loss of context
- 5ADRs ignored in PRs: reviewer doesn't check if PR contradicts ADR
- 6No ownership: nobody assigned to drive ADRs to acceptance
- 7Stale ADRs: 5-year-old still 'accepted' when superseded in practice
- 8No annual revisit: decisions outlive their context
- 9ADRs as design specs: 1000-word implementation details (use RFC or design doc)
- 10One person writes all: bus factor; lack of diverse input
- 11No diagrams: text-only hard to grok; pair with C4
- 12Hidden ADRs: in some private repo nobody reads
Cultural factors
- Psychological safety: people must feel safe proposing controversial choices
- Async-first: discussions in PR comments; sync only when needed
- Disagree and commit: once accepted, support; don't relitigate
- Be willing to supersede: ADRs aren't immutable; new info → new ADR
Maturity ladder
- 1Implicit decisions: in Slack, PRs, undocumented; high drift
- 2Some decisions in wikis; inconsistent
- 3ADRs adopted; some teams use; not enforced
- 4ADRs standard; reviewed in PRs; referenced in onboarding
- 5ADRs + fitness functions enforce invariants
- 6ADRs + RFCs + diagrams + reviews integrated
- 7Architecture intentional, evolutionary, measurable
- Most 1-2; mature 4-6
Trade-offs
Lightweight vs heavyweight
- Lightweight: fast; risk of skipping important
- Heavyweight: thorough; slow; bureaucratic
- Right: lightweight default; escalate weight for high-impact
Centralized vs federated
- Central architecture team: consistency; bottleneck
- Federated: autonomy; fragmentation
- Hybrid: principal engineers + standards + ADRs; teams own implementation
Sync vs async
- Sync: faster decisions; harder to schedule; less inclusive
- Async (PR comments): inclusive; can drag; needs time-box
- Right: async first; sync when needed for resolution
Strict vs flexible
- Strict: every PR cited against ADR
- Flexible: ADRs as guidelines
- Right: ADRs are why; code is what; reviewer ensures coherence
Honest critique
- Works in mature orgs; cult-of-process risk if pushed too early
- Process ≠ outcome; bad decisions can be well-documented
- Needs active stewardship; ADRs decay without it
- Best teams don't need many ADRs; instead clear principles + good judgment
- Some decisions too uncertain for ADR; spike + iterate first
When ADRs really pay off
- Multi-team platforms
- Long-lived systems (5+ years)
- High employee turnover (context preservation)
- Cross-functional decisions (security, compliance, cost)
- Technical migrations (database moves, framework upgrades)
Senior engineer moves
- Drive ADR adoption: write first 3-5 ADRs for big decisions; show value
- Reference ADRs in PRs: 'this implements ADR-007'
- Enforce with fitness functions: ArchUnit tests for invariants
- Coach the process: help teams write good ADRs; don't gatekeep
- Run office hours: be available for guidance; reduce review meetings
- Annual ADR review: pick 5 oldest; still right?
- Use ADRs in onboarding: 'read ADR-001 to ADR-020 in your first week'
- Keep ADR list current: index page; status visible
- Pair ADR + diagram: every significant ADR has a C4 diagram
- Time-box debates: if 2 weeks of comments, escalate; don't drift
- Document non-obvious 'no' decisions: 'we considered X and rejected because Y'
- Connect to fitness functions: ADR says 'no cycles between services'; ArchUnit enforces
Strategic take
- ADRs are lightweight, versioned records of significant decisions with context + consequences
- Pair with RFCs (big proposals), C4 diagrams (visualization), ArchUnit (enforcement)
- Lightweight async process: draft, review 1-2 weeks, accept; escalate if stuck
- Avoid theater (post-hoc ADRs) and paralysis (every decision 6-week debate)
- Seniors drive adoption, enforce via fitness functions, mentor on the process
- Goal: intentional architecture that survives team changes and time
Architecture Decision Records (ADRs) and Decision Process for Engineering Teams
Direct Answer
Architecture decisions on fast-moving teams need lightweight, written, versioned records — the standard pattern is Architecture Decision Records (ADRs), originally proposed by Michael Nygard. An ADR is a 1-page Markdown file in your repo with: title, status (proposed/accepted/superseded/deprecated), context (forces and constraints), decision (what we're doing), consequences (positive, negative, neutral). You write one when a decision is significant, irreversible-ish, or affects multiple teams — not for trivial choices. The process: someone drafts, async review (1-2 weeks), small synchronous discussion if needed, accept or supersede. Avoid two failure modes: decision paralysis (every choice becomes a 6-week debate; ship nothing) and architectural drift (decisions made implicitly in PRs; nobody knows why; rotation of engineers loses context). Senior teams use ADRs to record the why so future engineers (including future-you) understand context. Pair ADRs with: RFCs for larger proposals before deciding; C4 diagrams for architecture visualization; fitness functions / ArchUnit tests to enforce invariants; architecture review forums (light, not heavy); principal engineer office hours for guidance. Common mistakes: writing ADRs after the fact as documentation theater, treating ADRs as design specs (they're decision records), ignoring ADRs in code review, no ownership for review. Senior move: make ADRs part of the engineering rhythm — referenced in PRs, revisited annually, used to onboard new hires.
Common Mistakes & Pitfalls
ADR theater; decision paralysis; no documentation; over-process
Check your understanding
Answer these without looking back at the article.
- Walk through writing an ADR for choosing Kafka vs RabbitMQ.
- How do you keep ADRs from becoming stale documentation?
- What's the difference between an ADR and an RFC?
- How do fitness functions in ArchUnit enforce ADR decisions?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.