Engineering Management ·

Technical Decision Records: How to Make Decisions That Stick

Most engineering teams make important technical decisions in Slack threads or someone's head. When that context disappears, the same debates repeat. This covers what ADRs are, a practical template, when to write one vs when it's overhead, and how they change team dynamics around disagreement and ownership.

Technical Decision Records: How to Make Decisions That Stick

Six months after joining a codebase, you find a pattern that makes no sense. You ask why the team chose this approach. Nobody remembers. The person who made the call left a year ago. There is a comment in the code that says “don’t change this” with no explanation. You either trust it and move on, or you spend a week rediscovering why it exists.

This is not a knowledge management problem. It is a decision hygiene problem.

Technical Decision Records (also called Architecture Decision Records, or ADRs) are a lightweight way to capture not just what you decided, but why. They are not documentation for its own sake. They are a forcing function for better thinking and a gift to your future team.

What an ADR Is (and Is Not)

An ADR is a short document that records a single significant technical decision. It captures the context, the options considered, the decision made, and the consequences.

It is not:

  • A design document (those are for how, not what)
  • A post-mortem (those are for incidents, not decisions)
  • A requirements spec
  • Something that needs to be long or formal

The canonical format was introduced by Michael Nygard in 2011. The original template has five fields: title, status, context, decision, consequences. Most teams extend it slightly to capture options and tradeoffs. That extension is where most of the value is.

The core insight is that decisions without context cannot be challenged productively. If someone new to your team questions a database choice, they need to understand what alternatives were evaluated and why they were rejected. Otherwise the debate starts from scratch. With an ADR, the new engineer can read the original reasoning, identify whether the context has changed, and make a case for revisiting with specifics instead of opinions.

The Template

Here is a practical ADR template that balances completeness with the speed you can actually maintain under production pressure:

# ADR-NNN: [Short Title]

**Date:** YYYY-MM-DD
**Status:** [Proposed | Accepted | Deprecated | Superseded by ADR-NNN]
**Deciders:** [Names or team]

## Context

What is the situation that requires a decision? Include constraints,
the problem being solved, and relevant system state. Write for someone
who joins the team a year from now.

## Decision Drivers

- [Driver 1: e.g., latency requirements under 50ms p99]
- [Driver 2: e.g., team has no Go expertise]
- [Driver 3: e.g., must run on existing Kubernetes cluster]

## Options Considered

### Option A: [Name]

Brief description.

**Pros:**
- Point 1
- Point 2

**Cons:**
- Point 1
- Point 2

### Option B: [Name]

Brief description.

**Pros:**
- Point 1

**Cons:**
- Point 1

## Decision

We chose **Option A** because [specific reasoning tied to the decision
drivers above, not just "it was the best option"].

## Consequences

**Positive:**
- [What gets better]

**Negative:**
- [What we are accepting as a tradeoff]
- [What we are explicitly not solving]

**Risks:**
- [What could go wrong, and how we will detect it]

The status field carries more weight than it looks. Proposed means it is open for comment. Accepted means the decision was made. Deprecated means context changed and the decision no longer applies, but was not replaced. Superseded by ADR-NNN means a later decision replaced it, with a pointer to the new one. This status chain is what makes ADRs a living record instead of archaeology.

When to Write One

Writing an ADR for every decision is overhead that will kill adoption. The question is: what makes a decision worth recording?

A useful rule: write an ADR when any of these are true:

  • The decision is hard to reverse (database engine, authentication mechanism, deployment model)
  • You considered multiple real options and the choice was not obvious
  • The decision affects more than one team or service
  • You expect the decision to be questioned in the next six to eighteen months
  • Someone on the team disagreed and was overruled

That last point is important. An ADR is not just for posterity. It is also a way to make dissent visible and respected. If an engineer argued for a different approach and was not heard, the ADR that records the decision should note what was argued and why it was not chosen. This is not about creating a paper trail for blame. It is about acknowledging that smart people had different views and the team chose a direction anyway.

Compare that against what does not need an ADR:

Decision TypeADR Needed?Why
Choosing a database engineYesHard to reverse, significant tradeoffs
Choosing a CSS utility libraryProbably notEasy to swap, low coupling
API versioning strategyYesAffects multiple consumers, hard to change
Picking a linter rulesetProbably notLow stakes, fully reversible
Service communication pattern (REST vs gRPC)YesArchitectural, affects team interfaces
File naming conventionNoTooling enforces it, not a decision record
Moving from monolith to servicesYesMajor, long-lived consequence
Adding a new npm packageNoUnless it represents a pattern shift

The pattern: write when reversal is costly or context will be genuinely lost.

Store Them in the Repo

ADRs belong in your version control system, not in Confluence, Notion, or a shared drive. The reason is simple: Confluence will be replaced. Notion will be replaced. The repo will not. Engineers who join the team will clone the repo. The ADRs will be there.

A standard directory structure:

docs/
  adr/
    0001-use-postgres-as-primary-database.md
    0002-adopt-event-sourcing-for-order-service.md
    0003-supersede-rest-with-grpc-for-internal-apis.md
    README.md

The README.md in the adr/ directory should explain what ADRs are, link to your template, and document how to propose a new one. This makes the process self-documenting for new engineers.

Number them sequentially and never renumber. If an ADR is superseded, do not delete the old one. Update its status to Superseded by ADR-NNNN and add a line pointing to the replacement. The historical chain matters. Understanding why the team moved from REST to gRPC requires understanding why they chose REST in the first place.

For monorepos with multiple services, consider per-service subdirectories:

docs/
  adr/
    global/
      0001-typescript-as-primary-language.md
    services/
      order-service/
        0001-use-postgres.md
      notification-service/
        0001-use-redis-pubsub.md

Global ADRs apply across the codebase. Service-level ADRs apply within a bounded context. This prevents a database choice in one service from appearing to mandate it everywhere.

Writing the Options Section Well

The options section is where most ADRs fail. Teams either list options as a formality (two bad alternatives and the one they already wanted), or they write so much that nobody reads it.

The test for a good options section: could a senior engineer who was not in the room have reached the same conclusion from what you wrote? If yes, you have captured the real tradeoffs. If no, you have written a justification, not a record.

Specific things that make options sections useful:

Name the real alternatives. If you evaluated four databases, name all four. Not naming them implies they were not seriously considered, which either means they were not (and you might be wrong) or they were and you are hiding something.

Quantify where possible. “Postgres is slower for this workload” is weak. “Postgres p99 latency at our expected write volume is 40ms vs CockroachDB at 12ms, based on our benchmark” is useful. You do not always have benchmarks, but if you have numbers, use them.

State the constraints honestly. If you chose a technology partly because the team knows it, say that. Team familiarity is a legitimate decision driver. Hiding it produces an ADR that looks like a purely technical analysis but was actually a team capability decision. Future engineers will notice the mismatch.

Record the option you did not take and why. The most common reason a decision gets revisited is that someone new to the team independently discovers the option you already rejected. If you write down why it was rejected, that engineer can evaluate whether the reason still applies.

Anti-Patterns

Writing ADRs after the fact. The most common failure mode. The decision was made in a meeting, someone remembered that ADRs exist, and now they are writing justification for something that is already in production. The problem is that post-hoc ADRs are rationalizations. The options section will not reflect what was actually considered. The decision drivers will be retrofitted to match the outcome. The document will technically exist but will carry none of the value.

The fix: make writing the ADR part of the decision process, not the deployment process. Before a significant technical decision is finalized, the person proposing it writes a draft ADR. The team reviews the ADR. The decision is made. Then the status changes from Proposed to Accepted. This is three extra hours for decisions that will matter for years.

Making them too formal. Some teams adopt ADR processes that require sign-off from an architecture committee, a specific template with mandatory fields, and a two-week review period. This produces two outcomes: important decisions get made informally to avoid the process, and the ADRs that do get written are polish jobs rather than honest records.

Keep the process lightweight. The template above is a ceiling, not a floor. A two-paragraph ADR that captures context, the decision, and why is better than no ADR. A well-formatted document that does not reflect real thinking is worse than nothing.

Not linking from code. An ADR that nobody finds is nearly useless. When you make a non-obvious choice in code, leave a comment that points to the ADR:

// We use optimistic locking here instead of pessimistic.
// See docs/adr/0007-optimistic-locking-for-order-updates.md
async function updateOrderStatus(
  orderId: string,
  newStatus: OrderStatus,
  expectedVersion: number
): Promise<Order> {
  const result = await db.query(
    `UPDATE orders
     SET status = $1, version = version + 1
     WHERE id = $2 AND version = $3
     RETURNING *`,
    [newStatus, orderId, expectedVersion]
  );

  if (result.rowCount === 0) {
    throw new ConflictError(`Order ${orderId} was modified concurrently`);
  }

  return result.rows[0];
}

The comment connects the code to the reasoning. Someone reading this code at 2am during an incident can follow the link and understand the constraint before touching it.

Writing vague consequences. “This approach has some performance implications” is not a consequence. It is a hedge. Consequences should be specific enough that you can detect when they become problems. “Write throughput will be bounded by single-master replication; if we exceed 5,000 writes/second we will need to shard or move to a distributed database” is a consequence. It sets a threshold. It names the follow-on work.

How ADRs Change Team Dynamics

The biggest effect of a consistent ADR practice is not documentation quality. It is how the team handles disagreement.

Without ADRs, when someone disagrees with a technical direction, they have two options: argue in the moment (which often means the loudest voice wins), or stay quiet and move on (which breeds resentment and revisiting). Neither is good.

With ADRs, disagreement gets a third option: write it into the record. If an engineer argued for a different approach and was overruled, the ADR can note: “Option B was argued for by [engineer]. Primary objection was X. The team chose Option A because Y. If Y stops being true, Option B should be reconsidered.” This is not a consolation prize. It is genuine acknowledgment that the dissenting view was heard and the specific condition under which it should be revisited has been documented.

This changes the conversation from “I was overruled” to “my reasoning is on record and the team agreed on when to revisit it.” That is a significant shift in ownership.

It also changes how revisiting decisions works. When a new engineer questions an old choice, the answer is not “that’s how we do it” or a 30-minute meeting to re-explain context that everyone half-remembers. The answer is: read ADR-0014. If the context has changed, propose a superseding ADR. The process for challenging a decision is as lightweight as the process for making one.

Production Considerations

A few things that matter at scale:

Automate the numbering. For teams writing ADRs frequently, manual numbering creates conflicts. A simple script or a tool like adr-tools handles sequential numbering and template generation:

# adr-tools: https://github.com/npryce/adr-tools
adr new "Use Redis for session storage"
# Creates: docs/adr/0012-use-redis-for-session-storage.md

Review ADRs in PR review. When a PR introduces a significant architectural choice, the reviewer should ask whether an ADR exists. Making this part of code review culture is more effective than any process document. It only takes one question from a senior engineer (“where is the ADR for this?”) before the pattern becomes expected.

Revisit ADRs during architectural audits. Once or twice a year, read through your ADR index. Look for decisions that are Accepted but whose context has clearly changed. These are candidates for deprecation or superseding. An ADR directory full of outdated decisions that everyone knows are wrong is worse than no ADRs: it teaches new engineers that the records cannot be trusted.

Surface ADRs during onboarding. New engineers should read the ADR index in their first week. Not every document in depth, but the index. It gives them a map of where the hard choices were made and why the codebase looks the way it does. This single step reduces the “why does this work this way” questions that drain senior engineers’ time.


The teams that maintain this practice consistently report the same thing: they stop having the same debates twice. Not because everyone agrees, but because disagreements get resolved once, get documented with the reasoning, and the conditions for revisiting are explicit. That is the actual value. Not the documents themselves, but the discipline of making decisions in a way that survives the people who made them.

More in Engineering Management

The AI Productivity Paradox: Why Your Team Ships More Code but Delivers Less
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
Engineering Management ·

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 Management ·

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
Engineering Management ·

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.