What is an ADR? (And Why They're Critical for AI-Powered Development)
The lightweight documentation practice that preserves architectural knowledge
The Problem: "Why Did We Build It This Way?"
Every engineering organization has the same problem. A new engineer joins the team, opens the codebase, and asks the question that nobody can fully answer:
"Why did we build it this way?"
The code shows what exists. Git history shows when it changed. But the why? That lives in places you can't grep:
- Slack threads from 2019 that nobody can find
- The head of a senior engineer who left last year
- A whiteboard session that was never photographed
- A wiki page that's three refactors out of date
This isn't just inconvenient. It's expensive. New engineers spend weeks reverse-engineering decisions that took the original team minutes to make. Worse, they often make different decisions because they don't know the constraints that shaped the original choice.
What is an ADR?
An Architecture Decision Record (ADR) is a short document that captures a single architectural decision and the context around it. The practice was formalized by Michael Nygard in 2011, and the canonical resource is adr.github.io.
ADRs are:
- Lightweight: One file, one decision, a few minutes to write
- Version-controlled: Stored in
/docs/adr/alongside your code - Immutable: You don't edit old ADRs — you supersede them with new ones
- Discoverable: Anyone can read the history of architectural choices
The key insight: ADRs document why, not just what.
ADR Examples by Type
Different decisions call for different ADR structures. Here are three common types:
Technology Choice: ADR-001-database-selection.md
# ADR-001: Use PostgreSQL for User Data ## Status Accepted ## Context We need a database for user data. The team evaluated: - PostgreSQL: ACID compliance, strong ecosystem, team expertise - MongoDB: Flexible schema, horizontal scaling - DynamoDB: Serverless, pay-per-request pricing Key requirements: - Strong consistency for financial transactions - Complex queries across related entities - Team has 5+ years PostgreSQL experience ## Decision Use PostgreSQL. ## Consequences - Need to manage schema migrations (using Flyway) - Strong consistency guarantees simplify application logic - Can leverage team's existing expertise - Will need to plan for read replicas if scale requires
Pattern Selection: ADR-007-api-versioning.md
# ADR-007: URL Path Versioning for REST APIs ## Status Accepted ## Context We need a versioning strategy for our public API. Options: - URL path: /api/v1/users (explicit, cacheable) - Header: Accept: application/vnd.api+json;version=1 - Query param: /api/users?version=1 Our API consumers are primarily mobile apps with long update cycles. Breaking changes need clear migration paths. ## Decision Use URL path versioning: /api/v1/, /api/v2/ ## Consequences - URLs are self-documenting and easy to test - Can run multiple versions simultaneously during migration - Adds URL complexity - Must maintain routing for deprecated versions
Deprecation: ADR-012-remove-legacy-auth.md
# ADR-012: Deprecate Legacy Session-Based Authentication ## Status Accepted (supersedes ADR-003) ## Context ADR-003 established session-based auth in 2019. Since then: - Mobile apps require token-based auth - Microservices need stateless authentication - Session storage became a scaling bottleneck We've run JWT auth alongside sessions for 18 months. 95% of traffic now uses JWT. ## Decision Remove session-based authentication by Q2 2025. ## Migration Plan 1. Identify remaining session-auth consumers (3 internal tools) 2. Migrate internal tools to JWT (ADR-013) 3. Remove session middleware and Redis session store 4. Archive session-related code ## Consequences - Eliminates Redis session store ($400/mo infrastructure) - Simplifies auth middleware - Breaking change for any undocumented session consumers
ADR Best Practices
Folder Structure
Keep ADRs in a consistent location. The standard convention:
your-repo/ ├── docs/ │ └── adr/ │ ├── 0001-use-postgresql.md │ ├── 0002-adopt-typescript.md │ ├── 0003-session-based-auth.md │ ├── 0007-api-versioning.md │ ├── 0012-remove-legacy-auth.md │ └── template.md ├── src/ └── ...
Number ADRs sequentially. Gaps are fine — they indicate superseded or rejected decisions.
When to Write an ADR
- Write an ADR when:
- • Choosing between technologies (databases, frameworks, languages)
- • Establishing patterns the team should follow
- • Making decisions that would be expensive to reverse
- • Deprecating or replacing existing approaches
- • Someone asks "why did we..." more than once
- Skip the ADR when:
- • The decision is easily reversible
- • It's a one-off implementation detail
- • The choice is obvious given existing ADRs
Status Values
- Proposed: Under discussion, not yet decided
- Accepted: Decision made, team should follow
- Deprecated: No longer recommended, see superseding ADR
- Superseded: Replaced by a newer ADR (link to it)
Common ADR Mistakes
Mistake: Writing ADRs after the fact
ADRs written months later miss crucial context. Write them during the decision, not after. Even rough notes are better than reconstructed history.
Mistake: Documenting WHAT instead of WHY
"We use PostgreSQL" isn't useful — the code already shows that. "We chose PostgreSQL because our team has expertise and we need ACID transactions for payments" explains the reasoning future engineers need.
Mistake: Making ADRs too long
An ADR should be readable in 2-3 minutes. If it's longer, you're probably documenting multiple decisions. Split it up.
Mistake: Editing accepted ADRs
ADRs are immutable records. If a decision changes, write a new ADR that supersedes the old one. The history of decisions is valuable — don't rewrite it.
Mistake: Only documenting "big" decisions
Small decisions compound. "We always put interfaces in a separate package" seems minor until a new engineer puts them inline and creates inconsistency across 50 services.
What If You Have No ADRs?
Most organizations have years of code and zero ADRs. The decisions are buried in the implementation — you just can't see them.
Bootstrapping ADRs for a legacy codebase is hard:
- • Who remembers why we chose RabbitMQ over Kafka?
- • Why do some services use the repository pattern and others don't?
- • What's the intended relationship between these 47 microservices?
This is where AI can help. Modern LLMs like Claude can read code, identify patterns, and help you articulate decisions that are implicit in your implementation.
Using OutcomeOps to Bootstrap ADRs
The OutcomeOps CLI builds a queryable knowledge base from your repositories. Once your code is indexed, you can ask it to generate ADRs by combining your existing ADR template with code maps that describe how your services actually work.
Step 1: Ingest your documentation
Pull ADRs, READMEs, and docs from your repositories into the knowledge base:
$ outcome-ops-assist ingest-docs Ingesting documentation from all configured repositories... ✓ payment-service: 3 ADRs, 1 README, 2 docs ✓ order-service: 2 ADRs, 1 README, 4 docs ✓ user-service: 5 ADRs, 1 README, 1 doc Done. 18 documents indexed.
Step 2: Generate code maps
Create architectural summaries that describe how each service is structured:
$ outcome-ops-assist generate-code-maps Generating code maps for all repositories... ✓ payment-service: analyzing structure... ✓ order-service: analyzing structure... ✓ user-service: analyzing structure... Done. Code maps generated and indexed.
Step 3: Query to generate ADRs
Now ask natural language questions that combine your ADR template with the code maps. The --advanced flag uses Claude Sonnet for complex reasoning:
$ outcome-ops-assist "What is the eventing pattern between \ payment-service and order-service? Generate an ADR using \ our ADR template." --advanced --topK 20
The knowledge base retrieves your ADR template and the code maps for both services. Sonnet synthesizes them into a draft ADR that documents the actual integration pattern — with the context already captured in your codebase.
The AI reads your actual code (via code maps), identifies patterns that are already implemented but undocumented, and drafts ADRs using your organization's template. You review and refine — the AI does the archaeological work.
Example Queries for ADR Generation
Once your knowledge base is populated, you can ask questions like:
# Document a pattern you've identified $ outcome-ops-assist "How do we handle authentication across \ services? Generate an ADR documenting this pattern." --advanced # Understand cross-service dependencies $ outcome-ops-assist "What are all the services that publish \ events to the order queue? Create an ADR for our eventing \ conventions." --advanced --topK 30 # Standardize existing patterns $ outcome-ops-assist "Compare how payment-service and \ user-service implement retry logic. Generate an ADR that \ standardizes the approach." --advanced
The power isn't in special commands — it's in combining your existing documentation, code structure, and AI reasoning to surface decisions that are implicit in your implementation.
Why ADRs Matter for AI-Assisted Development
There's a secondary benefit to having good ADRs: they make AI coding assistants dramatically more effective.
Without ADRs, AI tools generate generic code. They don't know your conventions, your constraints, or your architectural decisions. With ADRs, they have context.
I ran an experiment with Spring PetClinic where I added just 3 ADRs documenting the project's patterns. The AI-generated code went from "generic Spring Boot" to "code a maintainer would actually merge."
The full case study shows the before/after comparison with links to the actual branches:
Read: How 3 ADRs Changed EverythingGetting Started
You can start writing ADRs today without any tooling:
- Create a
/docs/adr/folder in your repository - Copy a template from adr.github.io
- Document your next architectural decision
- Make it a habit: no significant decision without an ADR
For legacy codebases with no existing ADRs, the OutcomeOps CLI can help you bootstrap by analyzing your code and generating draft ADRs for review.
The best time to write an ADR was when the decision was made. The second best time is now.
Enterprise Implementation
The Context Engineering methodology described in this post is open source. The production platform with autonomous agents, air-gapped deployment, and compliance features is available via enterprise engagements.
Learn MoreFor ADR standards, templates, and tooling, visit adr.github.io.