What This Template Is For
An Architecture Decision Record (ADR) captures a single architecture decision along with its context, the options considered, and the consequences of the chosen approach. The concept was popularized by Michael Nygard and has become standard practice at engineering organizations that value decision transparency.
Most teams make architecture decisions in meetings, Slack threads, or pull request comments. Six months later, nobody remembers why the team chose Kafka over SQS, or why the API uses REST instead of GraphQL. New engineers question the decision, the original author has left the team, and the decision gets relitigated. ADRs solve this problem by creating a permanent, searchable record of what was decided, why, and what alternatives were rejected.
This template follows the widely adopted ADR format with one addition: a structured options comparison table that forces the author to evaluate alternatives against consistent criteria. For the broader context of how technical decisions fit into product delivery, the Technical PM Handbook covers PM-engineering collaboration patterns. When the decision involves build-versus-buy trade-offs, the AI Build vs Buy tool can help frame the analysis. For recording the implementation plan after the decision, use the technical spec template.
How to Use This Template
- Write one ADR per decision. If a single document covers two decisions, split it into two ADRs. Each record should be independently readable and searchable.
- Number ADRs sequentially (ADR-001, ADR-002, etc.) and store them in a dedicated directory in your repository or wiki. Common locations:
docs/adrs/,decisions/, or a Confluence space. - Write the Context section first. Explain why this decision needs to be made right now. What has changed? What problem needs solving? What constraint has been introduced?
- List all options considered, including the ones you rejected. The value of an ADR is in showing the reasoning process, not just the outcome.
- Fill in the comparison table with consistent evaluation criteria. Common criteria: complexity, performance, cost, team expertise, reversibility, and maintenance burden.
- Write the Decision section in present tense: "We will use X." Not "We decided to use X" (past tense implies the decision is historical, not active).
- Document consequences honestly. Every decision has trade-offs. If you cannot list downsides, you have not thought hard enough.
- Set the status to Proposed, then update to Accepted after team review. If a decision is later reversed, update the status to Superseded and link to the new ADR.
The Template
ADR-[NNN]: [Short Decision Title]
| Field | Details |
|---|---|
| Status | Proposed / Accepted / Deprecated / Superseded by ADR-[NNN] |
| Date | [Date decision was made or proposed] |
| Author | [Name] |
| Reviewers | [Names] |
| Related ADRs | [Links to related decisions, if any] |
Context
[2-3 paragraphs describing the situation that requires a decision. Include:]
- [What system or feature this affects]
- [What problem or requirement triggered the decision]
- [Any constraints (technical, timeline, cost, team expertise) that shape the options]
- [What happens if no decision is made (status quo)]
Decision Drivers
- [Driver 1: e.g., "Must support 10K requests/second at P99 < 200ms"]
- [Driver 2: e.g., "Team has limited experience with technology X"]
- [Driver 3: e.g., "Must integrate with existing authentication system"]
- [Driver 4: e.g., "Solution must be operational within 6 weeks"]
- [Driver 5: e.g., "Long-term maintenance cost is more important than initial build speed"]
Options Considered
Option 1: [Name]
[1-2 paragraphs describing this option. How would it work? What technology or approach does it involve?]
Pros:
- [Pro 1]
- [Pro 2]
- [Pro 3]
Cons:
- [Con 1]
- [Con 2]
Option 2: [Name]
[1-2 paragraphs describing this option.]
Pros:
- [Pro 1]
- [Pro 2]
Cons:
- [Con 1]
- [Con 2]
- [Con 3]
Option 3: [Name]
[1-2 paragraphs describing this option.]
Pros:
- [Pro 1]
- [Pro 2]
Cons:
- [Con 1]
- [Con 2]
Comparison Matrix
| Criterion | Weight | Option 1 | Option 2 | Option 3 |
|---|---|---|---|---|
| [Criterion 1: e.g., Performance] | High | Good / Adequate / Poor | Good / Adequate / Poor | Good / Adequate / Poor |
| [Criterion 2: e.g., Team expertise] | High | Good / Adequate / Poor | Good / Adequate / Poor | Good / Adequate / Poor |
| [Criterion 3: e.g., Implementation time] | Medium | Good / Adequate / Poor | Good / Adequate / Poor | Good / Adequate / Poor |
| [Criterion 4: e.g., Operational cost] | Medium | Good / Adequate / Poor | Good / Adequate / Poor | Good / Adequate / Poor |
| [Criterion 5: e.g., Reversibility] | Low | Good / Adequate / Poor | Good / Adequate / Poor | Good / Adequate / Poor |
Decision
We will use [Option N: Name].
[1-2 paragraphs explaining why this option was chosen. Reference the decision drivers and the comparison matrix. Be specific about which trade-offs were accepted and why.]
Consequences
Positive consequences:
- [Positive outcome 1]
- [Positive outcome 2]
- [Positive outcome 3]
Negative consequences (accepted trade-offs):
- [Trade-off 1 and why it is acceptable]
- [Trade-off 2 and how we will mitigate it]
Risks:
- [Risk 1 and mitigation plan]
- [Risk 2 and monitoring approach]
Follow-Up Actions
- ☐ [Action 1: e.g., "Create technical spec for implementation"]
- ☐ [Action 2: e.g., "Set up monitoring for identified risks"]
- ☐ [Action 3: e.g., "Schedule review of this decision in 6 months"]
- ☐ [Action 4: e.g., "Update team onboarding docs to reference this ADR"]
Filled Example: REST vs GraphQL for Public API
ADR-017: API Architecture for Public Developer Platform
| Field | Details |
|---|---|
| Status | Accepted |
| Date | March 2026 |
| Author | Marcus Chen, Staff Engineer |
| Reviewers | Sarah Kim (PM), Alex Rivera (Senior Backend), Jordan Lee (Frontend Lead) |
| Related ADRs | ADR-003 (Internal API Gateway), ADR-012 (Authentication System) |
Context
ProjectFlow is launching a public API to allow customers to build integrations, automate workflows, and connect with third-party tools. The product team has validated demand (see PRD-2026-024): 340 customers have requested API access in the past 12 months, and 3 of our top 5 enterprise prospects listed "API availability" as a requirement in their procurement evaluation.
The internal API uses REST with JSON responses. The question is whether the public API should also use REST, switch to GraphQL, or offer both. The decision affects the developer experience, documentation approach, client library strategy, and long-term maintenance cost.
Key constraints: the team has 4 backend engineers available for 3 months. The public API must support the 12 most-requested integration use cases (CRUD on projects, tasks, users, and comments; webhooks for real-time events). The API must handle 500 requests/second at launch with a path to 5,000 requests/second within 12 months.
Decision Drivers
- Developer experience for external integrators (must be easy to learn and use without support)
- Team expertise (4 engineers, all experienced with REST, 1 with GraphQL production experience)
- Time to launch (3 months to public beta)
- Performance at scale (500 rps at launch, 5,000 rps within 12 months)
- Maintenance cost (the public API becomes a permanent commitment once shipped)
- Compatibility with existing internal REST architecture
Options Considered
Option 1: REST (OpenAPI 3.1)
Build the public API as a standard REST API with JSON responses, versioned endpoints, and an auto-generated OpenAPI 3.1 specification. Use the OpenAPI spec to auto-generate client libraries in Python, JavaScript, Go, and Ruby. Documentation hosted on a developer portal with interactive "Try It" functionality powered by the spec.
Pros:
- Entire team has deep REST experience. No ramp-up time
- Existing internal API patterns can be reused with minimal adaptation
- OpenAPI tooling is mature: auto-generated docs, client SDKs, validation middleware
- Caching is straightforward with HTTP cache headers and CDN
Cons:
- Over-fetching: clients receive all fields even when they only need a subset
- Multiple round trips for related resources (e.g., fetching a project, then its tasks, then task assignees)
- Versioning complexity grows over time as the API surface expands
Option 2: GraphQL
Build the public API as a GraphQL endpoint. Clients write queries to request exactly the fields and relationships they need. Use Apollo Server or graphql-yoga as the runtime. Documentation auto-generated from the schema.
Pros:
- Clients request exactly the data they need. No over-fetching
- Single round trip for complex queries spanning multiple resources
- Schema is self-documenting. Introspection eliminates the need for separate API docs
- Strong type system catches errors at query validation time
Cons:
- Only 1 of 4 engineers has GraphQL production experience. Estimated 3-4 weeks of ramp-up for the team
- Requires building a new resolver layer on top of existing REST services. Significant net-new code
- Caching is harder (no HTTP caching for POST-based queries without additional infrastructure)
- Rate limiting is more complex (a single query can trigger expensive nested resolves)
- N+1 query problems require careful dataloader implementation
- Security surface area: query depth and complexity attacks require additional safeguards
Option 3: REST with GraphQL Planned for V2
Ship V1 as REST (Option 1). Evaluate GraphQL for V2 based on developer feedback and usage patterns after 6 months. If the top developer complaints are about over-fetching or round trips, invest in GraphQL. If not, continue with REST and add sparse fieldsets (JSON:API style) as a lighter solution.
Pros:
- Ships fastest (team expertise, reusable patterns)
- Defers the most complex architectural decision until real usage data is available
- No risk of building GraphQL infrastructure that developers do not actually need
- REST V1 establishes a baseline for measuring whether GraphQL adds enough value to justify the investment
Cons:
- If GraphQL is ultimately needed, the REST API still needs to be maintained (two APIs to support)
- Some early adopters may build on REST and resist migrating to GraphQL later
- "V2 someday" plans often never materialize
Comparison Matrix
| Criterion | Weight | REST (OpenAPI) | GraphQL | REST Now, GraphQL Later |
|---|---|---|---|---|
| Developer experience | High | Good | Good | Good (V1), Better (V2) |
| Team expertise | High | Good | Poor | Good |
| Time to launch (3 months) | High | Good | Poor | Good |
| Performance at scale | Medium | Good | Adequate | Good |
| Maintenance cost (long-term) | Medium | Good | Adequate | Adequate |
| Query flexibility | Medium | Poor | Good | Poor (V1), Good (V2) |
| Caching | Low | Good | Poor | Good |
Decision
We will use Option 3: REST with GraphQL planned for V2.
The primary decision drivers are team expertise, time to launch, and the principle of deferring irreversible decisions until we have real data. The entire team is proficient with REST, and the existing internal API provides reusable patterns that significantly accelerate development. GraphQL would add 3-4 weeks of ramp-up time and introduce new operational complexity (dataloader tuning, query cost analysis, depth limiting) that the team has not managed before.
The decision to plan for GraphQL in V2 is not a commitment. It is a structured evaluation point. After 6 months of REST API usage, we will analyze developer feedback, support ticket patterns, and API usage data. If over-fetching and round trips are the top pain points, we will build a GraphQL layer on top of the existing REST services. If developers are satisfied with REST, we will invest in sparse fieldsets and compound endpoints instead.
Consequences
Positive consequences:
- Ships 3-4 weeks faster than GraphQL, hitting the 3-month beta target
- Lower operational risk during launch (team is operating within their expertise)
- Auto-generated OpenAPI docs and client SDKs reduce documentation and support burden
- Decision is reversible: adding GraphQL later does not require removing REST
Negative consequences (accepted trade-offs):
- Developers who prefer GraphQL may be disappointed. Mitigation: communicate the V2 evaluation plan in developer docs
- Over-fetching on complex queries. Mitigation: add
?fields=query parameter for sparse fieldsets in the first release - Multiple round trips for related resources. Mitigation: add compound endpoints (e.g.,
/projects/:id?include=tasks,members) for the 3 most common multi-resource queries
Risks:
- Risk: "V2 GraphQL" becomes perpetual vaporware. Mitigation: schedule a formal evaluation at month 6 with a written recommendation. Add it to the technical roadmap as a decision item, not a backlog wish
- Risk: Early adopters build complex REST integrations that make future GraphQL migration painful. Mitigation: use API versioning (v1/) so GraphQL can coexist rather than replace
Follow-Up Actions
- ☑ Create technical spec for REST API (ADR author, this week)
- ☐ Set up OpenAPI spec auto-generation from code annotations
- ☐ Build sparse fieldsets (
?fields=) support for V1 - ☐ Schedule GraphQL evaluation for September 2026
- ☐ Add API design guidelines to engineering wiki
Common Mistakes to Avoid
- Writing ADRs after the fact. An ADR written three months after the decision is a post-hoc rationalization, not a decision record. Write the ADR when the decision is being made, while the context and alternatives are fresh.
- Documenting only the chosen option. The most valuable part of an ADR is the rejected alternatives and the reasoning behind the rejection. Future engineers need to understand why Option B was rejected, not just why Option A was chosen.
- Skipping the consequences section. Every decision has downsides. If you list only positive consequences, reviewers will (correctly) suspect you have not thought deeply enough. Honest trade-off documentation builds trust.
- Making every decision an ADR. ADRs are for significant, hard-to-reverse architecture decisions. Choosing between two logging libraries or deciding whether to use Tailwind or CSS modules does not warrant an ADR. Reserve them for decisions that affect system boundaries, data models, APIs, or infrastructure.
- Never updating ADR status. When a decision is superseded, update the original ADR's status to "Superseded by ADR-NNN." A stale ADR repository erodes trust in the practice.
Key Takeaways
- Write one ADR per decision, at the time the decision is made, not after the fact
- Document all options considered, including rejected ones. The reasoning matters more than the outcome
- Use a consistent comparison matrix to evaluate options against weighted criteria
- Be honest about consequences. Every architecture decision has trade-offs
- Store ADRs in the code repository so they are versioned, searchable, and discoverable
About This Template
Created by: Tim Adair
Last Updated: 3/4/2026
Version: 1.0.0
License: Free for personal and commercial use
