ADR Template
This document provides a standardized template and organizational guidelines for Architecture Decision Records (ADRs). ADRs capture important architectural decisions along with their context and consequences, creating a historical record of technical choices.
Purpose¶
ADRs serve as a living documentation system that:
- Records the reasoning behind significant architectural decisions
- Provides context for future developers who need to understand why decisions were made
- Enables teams to revisit and re-evaluate decisions as requirements evolve
- Creates an audit trail of technical choices for compliance and governance
When to Create an ADR¶
Create an ADR for decisions that:
- Affect system structure: Database selection, service architecture, API design patterns
- Have long-term implications: Technology choices that are difficult to reverse
- Impact multiple teams: Cross-cutting concerns affecting multiple services or teams
- Involve trade-offs: Decisions where alternatives were seriously considered
- Require buy-in: Changes needing stakeholder approval or team consensus
ADR Standards¶
File Naming Convention¶
# Format: NNNN-kebab-case-title.md
# NNNN = Zero-padded sequential number
0001-use-microservices-architecture.md
0002-adopt-kubernetes-for-orchestration.md
0003-use-postgresql-for-primary-database.md
0004-implement-event-driven-communication.md
0005-adopt-trunk-based-development.md
Directory Structure¶
docs/
└── architecture/
└── decisions/
├── README.md # ADR index and guidelines
├── 0001-use-microservices-architecture.md
├── 0002-adopt-kubernetes-for-orchestration.md
├── 0003-use-postgresql-for-primary-database.md
├── 0004-implement-event-driven-communication.md
└── templates/
└── adr-template.md # Blank template for new ADRs
Status Definitions¶
# ADR Status Workflow
statuses:
proposed:
description: "ADR is under discussion, not yet accepted"
next_states: [accepted, rejected, withdrawn]
color: "yellow"
accepted:
description: "Decision has been approved and is in effect"
next_states: [deprecated, superseded]
color: "green"
rejected:
description: "Decision was considered but not adopted"
next_states: []
color: "red"
deprecated:
description: "Decision is no longer recommended but still in use"
next_states: [superseded]
color: "orange"
superseded:
description: "Decision has been replaced by a newer ADR"
requires: "Link to superseding ADR"
next_states: []
color: "gray"
withdrawn:
description: "Proposal was withdrawn before decision"
next_states: []
color: "gray"
stateDiagram-v2
[*] --> Proposed
Proposed --> Accepted : Approved
Proposed --> Rejected : Not adopted
Proposed --> Withdrawn : Cancelled
Accepted --> Deprecated : Outdated
Accepted --> Superseded : Replaced
Deprecated --> Superseded : Replaced
Rejected --> [*]
Withdrawn --> [*]
Superseded --> [*]
Numbering Guidelines¶
# ADR Numbering Best Practices
numbering:
format: "4-digit zero-padded (0001, 0002, ...)"
assignment: "Sequential, never reused"
gaps_allowed: true # Numbers can be skipped if ADR withdrawn before merge
examples:
correct:
- "0001-initial-architecture.md"
- "0042-add-caching-layer.md"
- "0100-migrate-to-cloud.md"
incorrect:
- "1-initial-architecture.md" # Not zero-padded
- "ADR-001-initial-architecture.md" # Redundant prefix
- "initial-architecture.md" # Missing number
ADR Template¶
# ADR-{NNNN}: {Title}
**Status**: {Proposed | Accepted | Deprecated | Superseded by [ADR-XXXX](link)}
**Date**: YYYY-MM-DD
**Deciders**: {List of people involved in the decision}
**Technical Story**: {Link to issue, epic, or RFC}
## Context and Problem Statement
{Describe the context and problem statement in 2-4 sentences. What is the issue
that motivates this decision? What are the forces at play?}
## Decision Drivers
* {Decision driver 1, e.g., "Need to support 10x traffic growth"}
* {Decision driver 2, e.g., "Team expertise in specific technology"}
* {Decision driver 3, e.g., "Budget constraints"}
* {Decision driver 4, e.g., "Time-to-market requirements"}
## Considered Options
* **Option 1**: {Brief description}
* **Option 2**: {Brief description}
* **Option 3**: {Brief description}
## Decision Outcome
**Chosen option**: "{Option X}", because {one-sentence justification that
addresses the key decision drivers}.
### Consequences
**Positive**:
* {Positive consequence 1}
* {Positive consequence 2}
* {Positive consequence 3}
**Negative**:
* {Negative consequence 1, with mitigation if applicable}
* {Negative consequence 2, with mitigation if applicable}
**Neutral**:
* {Neutral observation or trade-off}
## Validation
{How will we know if this decision is successful? Include metrics or criteria.}
* {Validation criterion 1, e.g., "System handles 1000 RPS with p99 < 100ms"}
* {Validation criterion 2, e.g., "Development velocity maintained or improved"}
## Pros and Cons of the Options
### Option 1: {Name}
{Brief description of the option}
* **Good**, because {argument}
* **Good**, because {argument}
* **Bad**, because {argument}
* **Bad**, because {argument}
### Option 2: {Name}
{Brief description of the option}
* **Good**, because {argument}
* **Bad**, because {argument}
### Option 3: {Name}
{Brief description of the option}
* **Good**, because {argument}
* **Bad**, because {argument}
## Links
* {Link to related ADR}: {Relationship description}
* {Link to RFC or design document}
* {Link to relevant documentation}
## Notes
{Optional section for additional context, meeting notes, or implementation details
discovered after the decision was made.}
Example ADR¶
# ADR-0003: Use PostgreSQL for Primary Database
**Status**: Accepted
**Date**: 2025-01-10
**Deciders**: Backend Team, DevOps Team, Data Engineering
**Technical Story**: [PROJ-245](https://jira.example.com/browse/PROJ-245)
## Context and Problem Statement
We need to choose a primary database for our new microservices architecture. The
database must support ACID transactions, handle complex relational queries, and
scale to support our projected growth of 10x over the next 3 years. Our current
MySQL 5.7 instance is reaching its limits and requires a migration regardless.
## Decision Drivers
* Need for strong ACID compliance for financial transactions
* Complex relational data model with many joins
* Team has 5+ years of PostgreSQL expertise
* Requirement for open-source to avoid vendor lock-in
* Strong ecosystem for extensions (PostGIS, timescale)
* Cloud-provider agnostic deployment requirement
## Considered Options
* **Option 1**: PostgreSQL 15+
* **Option 2**: MySQL 8.0
* **Option 3**: CockroachDB
* **Option 4**: Amazon Aurora
## Decision Outcome
**Chosen option**: "PostgreSQL 15+", because it provides the best balance of
ACID compliance, query performance, team expertise, and open-source flexibility
while meeting all our technical requirements.
### Consequences
**Positive**:
* Team already has deep PostgreSQL expertise (no training required)
* Excellent JSON/JSONB support enables flexible schemas where needed
* Rich extension ecosystem (PostGIS for geo, pg_stat_statements for monitoring)
* Strong cloud provider support (AWS RDS, GCP Cloud SQL, Azure Database)
* Active community with regular security updates
* Native support for advanced features (CTEs, window functions, partial indexes)
**Negative**:
* Vertical scaling limits compared to distributed databases (mitigated by read
replicas and Citus extension if needed)
* Requires careful sharding strategy for multi-tenant workloads (mitigated by
planning tenant isolation at application layer)
* Connection pooling required for high-concurrency workloads (mitigated by
PgBouncer deployment)
**Neutral**:
* Similar operational complexity to MySQL
* Comparable managed service costs across cloud providers
## Validation
* System handles 1000 transactions per second with p99 latency < 50ms
* Query performance for reporting dashboards < 2 seconds
* Successful DR failover tested quarterly with RTO < 5 minutes
* Zero data loss during planned maintenance windows
## Pros and Cons of the Options
### Option 1: PostgreSQL 15+
The most advanced open-source relational database with strong ACID compliance
and extensive feature set.
* **Good**, because team has 5+ years of production PostgreSQL experience
* **Good**, because excellent JSON support allows schema flexibility
* **Good**, because rich extension ecosystem (PostGIS, pg_cron, pgvector)
* **Good**, because strong community support and regular releases
* **Good**, because cloud-agnostic with managed options on all major providers
* **Bad**, because single-node write scaling limits (~10K TPS practical max)
* **Bad**, because requires connection pooler for high connection counts
### Option 2: MySQL 8.0
Widely-used open-source database with improved features in version 8.
* **Good**, because familiar to many developers
* **Good**, because strong cloud provider support
* **Good**, because good read scaling with replicas
* **Bad**, because JSON support less mature than PostgreSQL
* **Bad**, because fewer advanced SQL features (limited CTE support)
* **Bad**, because team would need retraining from PostgreSQL
* **Bad**, because optimizer less sophisticated for complex queries
### Option 3: CockroachDB
Distributed SQL database with strong consistency and horizontal scaling.
* **Good**, because native horizontal scaling
* **Good**, because PostgreSQL wire protocol compatible
* **Good**, because built-in multi-region support
* **Bad**, because higher operational complexity
* **Bad**, because higher latency for single-region workloads
* **Bad**, because team has no operational experience
* **Bad**, because commercial license for advanced features
### Option 4: Amazon Aurora
AWS proprietary MySQL/PostgreSQL-compatible database.
* **Good**, because managed service reduces operational burden
* **Good**, because fast failover and read scaling
* **Good**, because integrated with AWS ecosystem
* **Bad**, because vendor lock-in to AWS
* **Bad**, because higher cost than self-managed PostgreSQL
* **Bad**, because some PostgreSQL extensions not supported
* **Bad**, because conflicts with multi-cloud strategy requirement
## Links
* Supersedes [ADR-0001: Use MySQL for Primary Database](0001-use-mysql-for-primary-database.md)
* Related to [ADR-0004: Implement Read Replicas for Scaling](0004-implement-read-replicas.md)
* Related to [ADR-0007: Use PgBouncer for Connection Pooling](0007-use-pgbouncer.md)
* [PostgreSQL 15 Release Notes](https://www.postgresql.org/docs/15/release-15.html)
* [Database Selection RFC](https://confluence.example.com/display/ARCH/RFC-2025-001)
## Notes
**2025-01-15**: Initial migration plan approved. Starting with read replicas in
us-east-1, expanding to eu-west-1 in Q2.
**2025-02-01**: PgBouncer configuration finalized. Using transaction pooling mode
with max 100 connections per pool.
Specialized ADR Templates¶
Lightweight ADR (Y-Statement)¶
For smaller decisions that don't warrant a full ADR:
# ADR-{NNNN}: {Title}
**Status**: Accepted
**Date**: YYYY-MM-DD
In the context of {context},
facing {concern},
we decided for {option},
and against {alternatives},
to achieve {outcome},
accepting {trade-off}.
# Example: Lightweight ADR
# ADR-0015: Use UUID v7 for Primary Keys
**Status**: Accepted
**Date**: 2025-01-20
In the context of designing database schemas for new services,
facing the need for distributed ID generation without coordination,
we decided for UUID v7 (time-ordered),
and against auto-increment integers and UUID v4,
to achieve sortable, collision-free IDs that work across services,
accepting slightly larger storage requirements (16 bytes vs 8 bytes).
Security Decision Record¶
For security-focused architectural decisions:
# ADR-{NNNN}: {Security Decision Title}
**Status**: {Proposed | Accepted | Deprecated | Superseded}
**Date**: YYYY-MM-DD
**Deciders**: {Security Team, Architecture Team}
**Security Classification**: {Public | Internal | Confidential}
**Compliance Requirements**: {SOC2, HIPAA, PCI-DSS, GDPR, etc.}
## Threat Context
{Describe the security threat or risk being addressed}
### Threat Model
* **Asset**: {What are we protecting?}
* **Threat Actor**: {Who might attack?}
* **Attack Vector**: {How might they attack?}
* **Impact**: {What's the potential damage?}
## Security Requirements
* {Requirement 1, e.g., "Data encrypted at rest using AES-256"}
* {Requirement 2, e.g., "All API calls authenticated and authorized"}
* {Requirement 3, e.g., "Audit logs retained for 7 years"}
## Decision Outcome
{Standard decision section}
## Security Validation
* {Penetration testing requirement}
* {Security review sign-off}
* {Compliance audit checklist}
## Residual Risk
{Document any remaining risk after implementing the decision}
| Risk | Likelihood | Impact | Mitigation |
|------|------------|--------|------------|
| {Risk 1} | Low/Med/High | Low/Med/High | {Mitigation strategy} |
Technology Adoption ADR¶
For decisions about adopting new technologies or frameworks:
# ADR-{NNNN}: Adopt {Technology} for {Purpose}
**Status**: {Proposed | Accepted | Deprecated | Superseded}
**Date**: YYYY-MM-DD
**Deciders**: {Team leads involved}
**Evaluation Period**: {Start date} - {End date}
## Technology Overview
* **Name**: {Technology name and version}
* **Category**: {Database, Framework, Library, Tool, etc.}
* **License**: {MIT, Apache 2.0, Commercial, etc.}
* **Maturity**: {Experimental, Stable, Mature, Legacy}
* **Vendor**: {Company or community}
## Evaluation Criteria
| Criterion | Weight | Score (1-5) | Notes |
|-----------|--------|-------------|-------|
| Performance | {%} | {score} | {notes} |
| Security | {%} | {score} | {notes} |
| Maintainability | {%} | {score} | {notes} |
| Community/Support | {%} | {score} | {notes} |
| Learning Curve | {%} | {score} | {notes} |
| Cost | {%} | {score} | {notes} |
| **Total** | 100% | {weighted} | |
## Proof of Concept Results
{Summary of any PoC conducted during evaluation}
```bash
# PoC metrics
Throughput: X requests/second
Latency p99: Y ms
Memory usage: Z MB
```
## Adoption Plan
1. {Phase 1: Pilot with single service}
2. {Phase 2: Expand to team}
3. {Phase 3: Organization-wide adoption}
## Rollback Plan
{How to revert if the adoption doesn't work out}
ADR Index Template¶
Create a README.md in your decisions directory to serve as an index:
# Architecture Decision Records
This directory contains Architecture Decision Records (ADRs) for {Project Name}.
## What is an ADR?
An Architecture Decision Record captures an important architectural decision made
along with its context and consequences. ADRs are immutable once accepted—if a
decision changes, a new ADR supersedes the old one.
## ADR Index
| ID | Title | Status | Date |
|----|-------|--------|------|
| [ADR-0001](0001-use-microservices-architecture.md) | Use Microservices Architecture | Accepted | 2024-06-15 |
| [ADR-0002](0002-adopt-kubernetes-for-orchestration.md) | Adopt Kubernetes for Orchestration | Accepted | 2024-07-01 |
| [ADR-0003](0003-use-postgresql-for-primary-database.md) | Use PostgreSQL for Primary Database | Accepted | 2025-01-10 |
| [ADR-0004](0004-implement-event-driven-communication.md) | Implement Event-Driven Communication | Proposed | 2025-01-20 |
## Status Legend
| Status | Description |
|--------|-------------|
| **Proposed** | Under discussion, not yet accepted |
| **Accepted** | Approved and in effect |
| **Deprecated** | No longer recommended, still in use |
| **Superseded** | Replaced by a newer ADR |
| **Rejected** | Considered but not adopted |
## Creating a New ADR
1. Copy the template: `cp templates/adr-template.md NNNN-title.md`
2. Use the next available number (check the index above)
3. Fill in all required sections
4. Submit a pull request for review
5. Update this index after the ADR is accepted
## Review Process
1. Author creates ADR and opens PR
2. Relevant stakeholders review (minimum 2 approvals)
3. Discussion and refinement in PR comments
4. Final approval from Architecture Board (for significant decisions)
5. Merge and update status to "Accepted"
## Tools
We use [adr-tools](https://github.com/npryce/adr-tools) for managing ADRs:
```bash
# Install adr-tools
brew install adr-tools
# Create new ADR
adr new "Use PostgreSQL for Primary Database"
# List all ADRs
adr list
# Generate table of contents
adr generate toc > README.md
```
## Further Reading
* [ADR GitHub Organization](https://adr.github.io/)
* [Michael Nygard's ADR Article](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions)
* [MADR Template](https://adr.github.io/madr/)
Tooling Integration¶
adr-tools CLI¶
Install and configure adr-tools for streamlined ADR management:
# Installation
# macOS
brew install adr-tools
# Linux (manual installation)
git clone https://github.com/npryce/adr-tools.git
cd adr-tools
make install prefix=/usr/local
# Initialize ADR directory
adr init docs/architecture/decisions
# Create new ADR
adr new "Use PostgreSQL for Primary Database"
# Creates: docs/architecture/decisions/0003-use-postgresql-for-primary-database.md
# Supersede an existing ADR
adr new -s 1 "Use Event Sourcing Instead of CRUD"
# Creates new ADR that supersedes ADR-0001
# Link ADRs
adr link 3 "Implements" 1 "Is implemented by"
# Links ADR-0003 to ADR-0001
# Generate table of contents
adr generate toc > docs/architecture/decisions/README.md
# Generate dependency graph
adr generate graph | dot -Tpng > adr-graph.png
Configuration File¶
Create .adr-dir in your repository root:
docs/architecture/decisions
Custom adr-tools Template¶
Create a custom template at docs/architecture/decisions/templates/template.md:
# ADR-{NUMBER}: {TITLE}
**Status**: Proposed
**Date**: {DATE}
**Deciders**: {List deciders}
**Technical Story**: {Link to ticket}
## Context and Problem Statement
{Describe the context}
## Decision Drivers
* {Driver 1}
* {Driver 2}
## Considered Options
* **Option 1**: {Description}
* **Option 2**: {Description}
## Decision Outcome
**Chosen option**: "{Option}", because {justification}.
### Consequences
**Positive**:
* {Consequence}
**Negative**:
* {Consequence}
## Links
* {Related ADR or document}
GitHub Template for New ADRs¶
Create .github/ISSUE_TEMPLATE/adr-proposal.yml:
name: ADR Proposal
description: Propose a new Architecture Decision Record
title: "[ADR] "
labels: ["adr", "architecture"]
body:
- type: markdown
attributes:
value: |
## Architecture Decision Record Proposal
Use this template to propose a new ADR for team discussion.
- type: input
id: title
attributes:
label: ADR Title
description: Short, descriptive title for the decision
placeholder: "Use PostgreSQL for Primary Database"
validations:
required: true
- type: textarea
id: context
attributes:
label: Context and Problem Statement
description: What is the issue that motivates this decision?
placeholder: |
We need to choose a primary database for our new microservices
architecture. The database must support ACID transactions and
handle complex relational queries.
validations:
required: true
- type: textarea
id: drivers
attributes:
label: Decision Drivers
description: What factors are influencing this decision?
placeholder: |
- Need for ACID compliance
- Team expertise
- Budget constraints
validations:
required: true
- type: textarea
id: options
attributes:
label: Options Considered
description: What alternatives are being evaluated?
placeholder: |
1. PostgreSQL - Open source, strong ACID
2. MySQL - Familiar to team
3. CockroachDB - Distributed SQL
validations:
required: true
- type: input
id: story
attributes:
label: Technical Story
description: Link to related issue, epic, or RFC
placeholder: "PROJ-245"
- type: checkboxes
id: checklist
attributes:
label: Pre-submission Checklist
options:
- label: I have searched existing ADRs for similar decisions
required: true
- label: I have identified the key stakeholders
required: true
- label: I have considered at least 2 alternative options
required: true
Pre-commit Hook for ADR Validation¶
Add to .pre-commit-config.yaml:
repos:
- repo: local
hooks:
- id: validate-adr-format
name: Validate ADR Format
entry: python scripts/validate_adr.py
language: python
files: ^docs/architecture/decisions/\d{4}-.+\.md$
types: [markdown]
Validation script (scripts/validate_adr.py):
#!/usr/bin/env python3
"""Validate ADR format and required sections."""
import re
import sys
from pathlib import Path
REQUIRED_SECTIONS = [
"Context and Problem Statement",
"Decision Drivers",
"Considered Options",
"Decision Outcome",
]
VALID_STATUSES = [
"Proposed",
"Accepted",
"Deprecated",
"Superseded",
"Rejected",
"Withdrawn",
]
def validate_adr(filepath: Path) -> list[str]:
"""Validate an ADR file and return list of errors."""
errors = []
content = filepath.read_text()
# Check filename format
if not re.match(r"^\d{4}-.+\.md$", filepath.name):
errors.append(f"Invalid filename format: {filepath.name}")
# Check for status field
status_match = re.search(r"\*\*Status\*\*:\s*(.+)", content)
if not status_match:
errors.append("Missing Status field")
else:
status = status_match.group(1).strip()
# Handle "Superseded by [ADR-XXXX](link)" format
status_value = status.split()[0]
if status_value not in VALID_STATUSES:
errors.append(f"Invalid status: {status_value}")
# Check for date field
if not re.search(r"\*\*Date\*\*:\s*\d{4}-\d{2}-\d{2}", content):
errors.append("Missing or invalid Date field (expected YYYY-MM-DD)")
# Check for required sections
for section in REQUIRED_SECTIONS:
if f"## {section}" not in content:
errors.append(f"Missing required section: {section}")
# Check for decision outcome
if "**Chosen option**" not in content and "Chosen option" not in content:
errors.append("Missing 'Chosen option' in Decision Outcome section")
return errors
def main() -> int:
"""Validate all ADR files passed as arguments."""
exit_code = 0
for filepath in sys.argv[1:]:
path = Path(filepath)
errors = validate_adr(path)
if errors:
print(f"\n{filepath}:")
for error in errors:
print(f" - {error}")
exit_code = 1
return exit_code
if __name__ == "__main__":
sys.exit(main())
MkDocs Integration¶
Add ADRs to your MkDocs navigation in mkdocs.yml:
nav:
- Architecture:
- Overview: architecture/overview.md
- Decisions:
- Index: architecture/decisions/README.md
- ADR-0001 Microservices: architecture/decisions/0001-use-microservices-architecture.md
- ADR-0002 Kubernetes: architecture/decisions/0002-adopt-kubernetes-for-orchestration.md
- ADR-0003 PostgreSQL: architecture/decisions/0003-use-postgresql-for-primary-database.md
Or use the awesome-pages plugin for automatic navigation:
# mkdocs.yml
plugins:
- awesome-pages
# docs/architecture/decisions/.pages
title: Architecture Decisions
arrange:
- README.md
- ...
Automated ADR Index Generation¶
Script to auto-generate the ADR index (scripts/generate_adr_index.py):
#!/usr/bin/env python3
"""Generate ADR index from decision files."""
import re
from pathlib import Path
def extract_metadata(filepath: Path) -> dict:
"""Extract title, status, and date from ADR file."""
content = filepath.read_text()
title_match = re.search(r"^# ADR-\d+:\s*(.+)$", content, re.MULTILINE)
status_match = re.search(r"\*\*Status\*\*:\s*(.+)", content)
date_match = re.search(r"\*\*Date\*\*:\s*(\d{4}-\d{2}-\d{2})", content)
return {
"number": filepath.stem.split("-")[0],
"filename": filepath.name,
"title": title_match.group(1) if title_match else "Unknown",
"status": status_match.group(1).strip() if status_match else "Unknown",
"date": date_match.group(1) if date_match else "Unknown",
}
def generate_index(decisions_dir: Path) -> str:
"""Generate markdown index table."""
adrs = []
for filepath in sorted(decisions_dir.glob("[0-9][0-9][0-9][0-9]-*.md")):
adrs.append(extract_metadata(filepath))
lines = [
"| ID | Title | Status | Date |",
"|----|-------|--------|------|",
]
for adr in adrs:
lines.append(
f"| [ADR-{adr['number']}]({adr['filename']}) "
f"| {adr['title']} "
f"| {adr['status']} "
f"| {adr['date']} |"
)
return "\n".join(lines)
if __name__ == "__main__":
decisions_dir = Path("docs/architecture/decisions")
print(generate_index(decisions_dir))
Review and Approval Process¶
Review Checklist¶
## ADR Review Checklist
### Content Quality
- [ ] Problem statement is clear and well-defined
- [ ] Decision drivers are relevant and prioritized
- [ ] At least 2-3 alternatives were seriously considered
- [ ] Pros and cons are balanced and honest
- [ ] Consequences (positive and negative) are documented
- [ ] Decision is justified with reference to drivers
### Format Compliance
- [ ] Follows standard ADR template
- [ ] Status is set correctly (Proposed for new ADRs)
- [ ] Date is accurate
- [ ] Deciders are listed
- [ ] Links to related documents/issues included
### Technical Accuracy
- [ ] Technical claims are accurate
- [ ] Performance/scalability assertions are backed by data
- [ ] Security implications are addressed
- [ ] Cost implications are considered
### Stakeholder Input
- [ ] Relevant teams have been consulted
- [ ] Security team reviewed (if applicable)
- [ ] Operations team reviewed (if applicable)
- [ ] Product/business stakeholders aware
Approval Matrix¶
# ADR Approval Requirements by Impact Level
approval_matrix:
low_impact:
description: "Single service, easily reversible"
examples:
- "Library selection within a service"
- "Internal API design choices"
approvers:
required: 2
from: ["team-leads"]
medium_impact:
description: "Multiple services, moderate effort to change"
examples:
- "New database for a domain"
- "Authentication approach"
approvers:
required: 3
from: ["team-leads", "staff-engineers"]
high_impact:
description: "Organization-wide, significant investment"
examples:
- "Cloud provider selection"
- "Primary programming language"
- "Core infrastructure decisions"
approvers:
required: 5
from: ["architecture-board", "engineering-leadership"]
includes: ["security-review", "cost-analysis"]
Best Practices¶
Writing Effective ADRs¶
# ADR Writing Guidelines
do:
- Keep the problem statement focused and specific
- Include concrete data to support claims
- Document the decision drivers upfront
- Be honest about trade-offs and limitations
- Link to related ADRs and external resources
- Update the ADR if significant new information emerges
- Include validation criteria for success
avoid:
- Vague or overly broad problem statements
- Documenting decisions after implementation is complete
- Skipping the alternatives analysis
- Being defensive about the chosen option
- Omitting negative consequences
- Using ADRs for operational procedures (use runbooks instead)
When to Update vs. Supersede¶
# ADR Modification Guidelines
update_existing:
- Fixing typos or clarifying language
- Adding implementation notes after decision
- Updating links to moved documents
- Adding validation results
create_new_superseding:
- Fundamental change to the decision
- Choosing a different option than originally decided
- Significant change in context invalidates original reasoning
- New requirements make original decision inappropriate
Additional Resources¶
- ADR GitHub Organization
- Michael Nygard's Original ADR Article
- MADR - Markdown ADR Template
- adr-tools GitHub Repository
- Log4brains - ADR Management Tool
- Documenting Architecture Decisions (ThoughtWorks)