Technical Decision Records: How to Make Architecture Decisions That Stick
Most architecture decisions get lost in Slack, reversed without context, or silently abandoned. This guide covers the full ADR lifecycle: format, lightweight templates, when to write one (and when not to), how to keep them discoverable, anti-patterns that kill adoption, and how to wire ADRs into code review so they actually change behavior.
Six months into a codebase, you find a service that deserves a question. Why is this synchronous when everything else is async? Why does this microservice own three unrelated domains? Why is there a bespoke retry library when a standard one was available? You ask around. Nobody remembers. The person who made the call left 14 months ago. There is a TODO comment with no issue link. There is a Slack thread from 2023 that nobody can find.
You have two options: trust the decision and move on, or rediscover why it exists by spending time you do not have.
This repeats every week in engineering teams that do not record their decisions. The same debates surface. New engineers make the same mistakes. The same meeting happens twice. And teams that do try to fix this often create documentation graveyards, wiki pages that are out of date the day they are written, never consulted during the actual work.
Architecture Decision Records (ADRs) are a different approach. They are short, structured, living documents that capture why a decision was made, not just what was decided. When done right, they are low overhead, high leverage, and they actually change how teams think about technical choices.
This guide is for the tech lead or startup CTO who wants an ADR practice that survives contact with a real engineering team.
Why Architecture Decisions Disappear
Before fixing the problem, it helps to understand why decisions get lost in the first place.
The most common cause is medium: Slack, Zoom calls, and linear comment threads are terrible containers for decisions. They are streams optimized for recency. A great three-hour architecture discussion in Slack is invisible six months later, even to the person who had it. The decision lands in the code, the conversation evaporates.
The second cause is implicit agreement. Many architecture choices are never formally made. They emerge from the path of least resistance, from whoever wrote the first service, from a framework default, from a copy-paste from a previous codebase. Nobody decided to use eventual consistency in that subsystem. It happened. Later, someone asks why it works that way and there is no answer, because there was never a decision, just an accumulation.
The third cause is that engineering culture typically rewards building, not reflecting. Writing documentation after a decision is made feels like overhead. The feature is done. The next sprint is starting. The retrospective on why you chose PostgreSQL over MySQL can wait. It waits indefinitely.
ADRs work because they sit at the decision point, not after it. Writing a short ADR forces the decision to be explicit. That is most of the value.
The ADR Format
The original ADR format, proposed by Michael Nygard in 2011, has five sections. It has been iterated on by dozens of teams since, but the core structure holds up.
# ADR-001: Use PostgreSQL as the primary relational database
## Status
Accepted
## Context
We are building a SaaS product with structured data and complex query requirements.
The team has strong SQL experience. We need ACID transactions for billing and user
account data. The infrastructure team already manages Postgres RDS instances for
two other internal products, which reduces operational burden.
## Decision
We will use PostgreSQL as our primary relational database.
## Consequences
- Positive: familiar to the team, strong ecosystem, excellent JSON support for
semi-structured data, managed RDS reduces ops overhead.
- Negative: vertical scaling has a ceiling. If we reach 100k+ concurrent
connections, we will need PgBouncer or a move to Aurora. This is not a
near-term concern.
- Neutral: we forgo the flexibility of MySQL compatibility, but we have no
requirement for that.
## Alternatives Considered
- MySQL: ruled out because the team has no familiarity, and the operational
benefits of Postgres RDS familiarity outweigh MySQL's performance characteristics
at our current scale.
- DynamoDB: ruled out for primary storage because our data model is highly
relational and we need complex queries. Will reconsider for specific high-volume
write paths if needed.
That is a complete ADR. It takes 10 to 20 minutes to write. It pays back that time every time a new engineer joins, every time a debate resurfaces, every time you are evaluating a migration.
The key fields, and what actually matters in each:
Status distinguishes a proposal from a settled decision. Valid values: Proposed, Accepted, Deprecated, Superseded by ADR-042. Status is load-bearing. A wall of Proposed ADRs that never transition to Accepted is a process smell, not a documentation system.
Context captures constraints that existed at the time. This is the most important field because constraints change. An ADR written when you had two engineers looks different from one written when you have 20. Context explains why the decision made sense then, even if it does not make sense now.
Decision should be a single sentence if possible. “We will use X for Y.” Brevity here forces clarity. If you cannot write the decision in one sentence, the decision is not clear yet.
Consequences is where honest ADRs separate from dishonest ones. Every decision has tradeoffs. If your consequences section is all positives, you are writing a justification, not a record. Include what you are giving up, what new problems you are accepting, what future decisions this constrains.
Alternatives Considered closes the loop on “why didn’t you just use X?” Without this, the same question gets asked at every onboarding, every architecture review, every incident postmortem. Writing it down once is cheaper than answering it ten times.
When to Write an ADR
Not every technical choice needs an ADR. The test is whether the decision:
- Is hard to reverse, or expensive to revisit
- Affects more than one service or team
- Involves a significant tradeoff
- Will likely be questioned by the next engineer who touches the code
- Establishes a pattern that others will follow
ADRs that are worth writing:
- Choosing a database, queue, or cache technology
- Deciding on an authentication strategy (JWT vs sessions, OAuth provider)
- Picking a communication pattern between services (REST vs gRPC vs events)
- Settling on a deployment strategy (containers, serverless, VMs)
- Choosing a frontend framework or state management approach
- Setting a policy (all new services use TypeScript, logs go to Datadog)
- Deciding not to do something significant (choosing to defer pagination to v2, deferring multi-tenancy)
ADRs that are not worth writing:
- Library version bumps
- Minor refactors that do not change architecture
- Coding conventions already handled by a linter config
- Decisions that can be trivially reversed
A rough heuristic: if reversing the decision would require more than a day of engineering effort, write an ADR.
When Not to Write an ADR
There is also a failure mode where teams write too many ADRs, turning the practice into a bureaucratic tax. If every PR requires an ADR, people start writing hollow ones to get through the gate. Quality collapses and the system loses trust.
Protect the signal-to-noise ratio. An ADR for “we added a new npm dependency” is noise. An ADR for “we are moving from npm to pnpm across all repos” is signal.
A Lighter Template for Fast-Moving Teams
The Nygard format is good but can feel heavy for a two-person startup. Here is a stripped-down version that covers the essentials:
# ADR-012: Centralize feature flags in LaunchDarkly
**Status:** Accepted
**Date:** 2026-02-14
**Deciders:** @alice, @bob
## What and Why
We are centralizing all feature flags in LaunchDarkly instead of maintaining
per-service environment variable hacks. The trigger was a production incident
where a flag buried in a `.env.production` file on one service was not included
in the deployment runbook, causing a silent regression.
## Decision
All feature flags go through LaunchDarkly SDK. No feature flags as raw env vars.
## Tradeoffs
- Adds a third-party dependency with SDK latency (~2ms per evaluation, cached).
- LaunchDarkly has a free tier limit; at current scale, estimated cost is $0.
- Reduces risk of env-var flag drift across services.
## What This Closes Off
Per-service feature flag utilities. If you are building one, write a new ADR first.
Shorter. Still captures the why, the constraints, the tradeoffs, and the consequences. For most engineering teams, this template is sufficient.
Numbering and Discoverability
ADRs are useless if nobody can find them. The structural choice matters more than the specific tool.
The standard approach is to store ADRs in the repository they describe, in a directory called docs/adr/ or docs/decisions/. Files are numbered sequentially: 001-use-postgres.md, 002-event-driven-notifications.md. Monotonically increasing numbers that never get reused mean an ADR’s number is stable and citable.
For cross-cutting decisions that span multiple services, a dedicated decisions repository (or a decisions/ directory in a monorepo root) works better than duplicating the ADR across repos.
The index is critical. An index.md or README.md in the ADR directory that lists every ADR by number, title, status, and date makes the difference between “these exist” and “these are useful.”
# Architecture Decision Records
| # | Title | Status | Date |
|---|-------|--------|------|
| 001 | Use PostgreSQL as primary database | Accepted | 2025-03-01 |
| 002 | Event-driven notifications via SNS | Accepted | 2025-04-10 |
| 003 | Migrate from REST to tRPC for internal APIs | Proposed | 2026-01-22 |
| 004 | Centralize feature flags in LaunchDarkly | Accepted | 2026-02-14 |
Keep this index updated as part of the PR that adds the ADR. Do not let it drift.
Integrating ADRs into Code Review
The biggest lever for ADR adoption is making them part of the PR workflow, not a separate process that happens “when there is time.”
A practical approach: in your PR template, add a single checkbox:
## Checklist
- [ ] Tests pass
- [ ] Linting clean
- [ ] ADR written or linked if this changes architecture
That checkbox does two things. It prompts the author to think about whether the change warrants an ADR. And it prompts reviewers to ask “does this need one?” when the box is unchecked.
The goal is not to require an ADR on every PR. The goal is to make “did we document this decision?” a reflex, not an afterthought.
For changes that touch foundational patterns, reviewers should be empowered to say: “This looks right, but before we merge, I’d like an ADR for the authentication approach you chose. The PR can wait 30 minutes.” That friction is the whole point.
Tooling Options
The tooling landscape for ADRs ranges from zero infrastructure to dedicated platforms.
Markdown files in the repo is the lowest-friction option. Works with any editor, no dependencies, diffs cleanly in PRs, gets co-located with the code it describes. This is the right starting point for almost every team.
adr-tools (command line) generates numbered ADR files and manages the index. Useful if you want to automate boilerplate. Install with Homebrew, run adr new "Use Redis for session storage" and you get a pre-numbered file with the template filled in.
Notion or Confluence works for teams that already live in those tools, but ADRs in wikis drift from the code. Linking a Notion ADR to a PR is an extra click that most engineers skip. The discoverability benefit of keeping ADRs in the repo usually outweighs the editor comfort of a wiki.
GitHub Discussions or Issues are sometimes used for ADR proposals before they are accepted. The issue captures discussion; the merged PR adds the final ADR file. This works well because the proposal lives where engineers already spend time. The downside is that a rejected proposal disappears into closed issues rather than leaving a Superseded or Rejected record.
Backstage has an ADR plugin if you are already running a Backstage developer portal. Probably not worth adopting Backstage for ADRs alone.
For most teams: start with markdown files in the repo, adr-tools for automation, and a clean index. Add tooling only when you feel a specific friction.
Common Anti-Patterns
Writing ADRs after the fact. The most common failure mode. The team makes a decision, ships it, and writes the ADR three weeks later as a paper trail. This produces ADRs that read like justifications rather than records. The “alternatives considered” section is thin because nobody actually evaluated the alternatives at that point. The “context” section omits the pressures that shaped the choice. Retroactive ADRs have some value, but they are a pale shadow of ADRs written at decision time.
Fix: when you start a design conversation or an architecture spike, open an ADR in draft status. Write the context and alternatives as you explore. Close it with the decision when you land.
Treating ADRs as proposals that need approval. ADRs are records, not RFCs. An ADR labeled Proposed that sits in a PR waiting for three senior engineers to approve it is not an ADR practice, it is a committee process with extra steps. If a decision genuinely requires broad alignment, use an RFC or a design doc with a comment period. ADRs are for recording decisions that have been made.
Fix: the person with decision authority writes the ADR and moves it to Accepted. If the decision is contested, surface that disagreement explicitly in the Status field or with a note: “Accepted with dissent from @carol, see linked discussion.”
Status field neglect. ADRs where every entry is Accepted and nothing is ever Superseded or Deprecated signal a practice that is not being maintained. Technology changes. Yesterday’s accepted decision is sometimes today’s tech debt. If ADRs never get superseded, they are accumulating rather than reflecting reality.
Fix: when a new ADR overturns an old one, update the old ADR’s status to Superseded by ADR-042 and add a line at the top linking to the new one. This is a five-minute task that keeps the record honest.
ADR sprawl without an index. A docs/adr/ directory with 80 files and no index is a documentation graveyard. Discoverability collapses. Engineers stop consulting ADRs because finding the relevant one requires grepping through 80 files.
Fix: maintain the index. Automate it if you have to, with a script that generates the index from frontmatter in each ADR file.
Consensus theater. Writing an ADR and adding a “comment period” that never results in any actual feedback, then closing it with Accepted. If the comment period exists to provide cover for a decision that was already made, skip it. Either make the decision openly or invite real input.
What a Good ADR Looks Like vs. a Bad One
Here is the same decision recorded well and recorded poorly.
The bad version:
# ADR-019: Use Redis for caching
## Status
Accepted
## Decision
We will use Redis for caching.
## Reasons
Redis is fast, widely used, and supports many data structures.
This is not an ADR. It is a one-liner dressed up in a template. It captures zero context about why Redis over Memcached, no information about what is being cached, no tradeoffs, no alternatives.
The good version:
# ADR-019: Use Redis for caching session tokens and rate-limit counters
## Status
Accepted
## Context
The API currently stores session tokens in a single PostgreSQL table with a
token_expires_at index. At 5k RPS, token lookups have become a measurable
source of DB read load (8% of queries in slow query log). We also need to
implement IP-based rate limiting and need a shared counter that is accessible
across multiple API instances.
## Decision
We will use Redis (ElastiCache) for:
1. Session token storage with TTL-based expiry.
2. Rate-limit counters using Redis INCR with EXPIRE.
PostgreSQL remains the source of truth for user data.
## Consequences
- Positive: token lookups drop from DB query to ~0.5ms Redis GET. Rate-limit
counters become trivial to implement.
- Negative: adds operational complexity. A Redis failure now affects API
authentication. We accept this by deploying with ElastiCache Multi-AZ and
treating Redis unavailability as a graceful degradation (fall back to DB for
session validation with a 2x timeout).
- Neutral: Redis data is ephemeral. Session tokens and rate-limit state are
not durability concerns.
## Alternatives Considered
- Memcached: supports TTL but lacks atomic INCR with EXPIRE for rate limiting.
Ruled out because Redis solves both use cases with one service.
- Keeping in PostgreSQL with read replica: read replicas add cost and
complexity without addressing the write path for rate-limit counters.
The difference is specificity. The good ADR tells you what problem existed, what constraints existed, what was ruled out and why, and what new risks were accepted. Six months from now, the engineer doing the incident postmortem when Redis goes down will know exactly what was designed for and what the fallback is supposed to be.
Getting Buy-In From the Team
ADR adoption fails when it is imposed from the top as a documentation requirement. It succeeds when engineers see it as protecting themselves from future pain.
The pitch to the team is not “we need better documentation.” It is: “Have you ever spent a week rebuilding context that someone already had? Have you ever reversed a decision and then wished you had the original reasoning? ADRs are cheap insurance against that.”
Start with a few exemplary ADRs written by respected engineers. Make them genuinely useful, not performative. When a new engineer joins and says “this ADR saved me three days of investigation,” the practice sells itself.
Keep the barrier to entry low. One template. One directory. No approval process. The ADR is done when the decision is made and the PR is merged.
The Real Value
ADRs are not primarily about documentation. Documentation is a side effect.
The real value is that writing an ADR forces you to be explicit. It forces you to name the alternatives you considered. It forces you to articulate the tradeoffs you accepted. That process improves decisions, not just records of them. Teams that write ADRs tend to make better architecture choices, because the discipline of “I am going to write this down” raises the quality of the thinking.
The second value is team continuity. Not continuity in the sense of “this will survive a bus factor event,” though it will. Continuity in the sense that the team six months from now can make new decisions with full context about old ones. That is what distinguishes a codebase that is coherent from one that is a collection of accidental choices layered over each other.
Start with five decisions you wish you had on record. Write them this week. See if the next design conversation feels different when you open an ADR draft at the start of it.
More in Engineering Management
The AI Productivity Paradox: Why Your Team Ships More Code but Delivers Less
AI coding tools create an illusion of velocity at the individual level while degrading team-level delivery, quality, and maintainability. The core mechanism is a 5x+ senior/junior productivity split that aggregate metrics hide entirely.
The AI Productivity Paradox: Why Your Team Ships More Code but Delivers Less Value
93% of developers use AI coding tools, yet DORA metrics haven't improved proportionally. Individual output rises while bug rates, review times, and deployment instability climb. Here is why individual AI productivity gains create organizational drag, and how to fix it with architecture-level guardrails.
Why Your Engineering Team Is Shipping Slower Than 6 Months Ago
Engineering velocity declines at seed-to-Series-A startups for predictable, diagnosable reasons. Process debt, unclear ownership, hiring mistakes, burnout, and architectural bottlenecks all compound. Here is a diagnostic framework you can run in one afternoon, plus a tradeoffs table for each intervention.
The AI Ratchet Effect: Why Giving Your Engineering Team AI Tools Made Them Work Harder, Not Smarter
67% of engineers who adopted AI tools in 2025 worked more hours by year-end, not fewer. This is the AI ratchet effect: management converts every productivity gain into a permanently higher baseline. Here is how it happens, why it is worse at startups, and what a sustainable AI adoption cadence actually looks like.