Three months into a project, a developer asks why you chose PostgreSQL over MongoDB. The tech lead explains from memory: document flexibility sounded appealing at first, but the data relationships were too complex, and the client's hosting contract included a managed Postgres instance anyway. Good answer. Relevant context. Completely undocumented.
Six months later, the tech lead has rolled off the project. A new developer joins and makes the same evaluation. Same conclusion, same reasoning, but this time it takes three days of investigation. On the next project, someone else on the team defaults to MongoDB because they heard it was more flexible. No one can articulate why the previous team went a different way.
This is the problem that Architecture Decision Records exist to solve.
What an ADR actually is
An ADR is not a design document. It is not a technical specification. It is a record of a specific decision, captured at the moment it was made, with enough context to be understood by someone who was not in the room.
The key word is decision. Not approach, not preference, not vague direction. A decision has a moment. Before it, multiple options were live. After it, one was chosen. An ADR captures that moment: what the options were, what mattered most, which one won, and why.
Most technical documentation describes how a system works. An ADR describes why it works that way. That is a different kind of knowledge, and it does not fall out naturally from reading the code. I have spent more time than I would like reverse-engineering architectural choices from commit histories and Slack threads, on projects where the original reasoning was perfectly defensible but nobody had written it down.
Walking through a real one
Here is what an ADR looks like in practice. A team is building a multi-tenant SaaS application and needs to decide how to handle database-level access control.
Title: Use Row Level Security for tenant isolation
Status: Accepted
Context: The application serves multiple organisations with strict data isolation requirements. We need to ensure that database queries never return data belonging to a different organisation. This needs to hold for both application queries and any direct database access, including admin tooling and future integrations.
Decision: Use Supabase's Row Level Security policies on every table. Each policy checks that the organisation identifier on the row matches the value extracted from the authenticated JWT. This enforces isolation at the database level rather than the application layer.
Alternatives considered:
Application-level filtering only. Every query could include an explicit WHERE clause enforced in the repository layer. This works but relies entirely on developer discipline. A single missing clause leaks cross-tenant data. RLS makes the filtering impossible to bypass accidentally.
Separate schemas per tenant. Isolating each organisation into its own Postgres schema gives stronger separation but complicates migrations significantly. Schema-per-tenant does not suit a product where organisations are created frequently and the schema evolves rapidly.
Consequences: Every table requires RLS policies to be defined and kept up to date. Developers need to understand that standard auth UID helpers cannot be used with string-based user IDs from the auth provider, and must instead extract the identifier directly from the JWT. Future contributors will encounter this at the start of any database work.
That is a complete ADR. It takes about fifteen minutes to write and remains accurate indefinitely, because it describes the moment of decision rather than the current state of the system. The codebase shows you that RLS is in use. The ADR explains why it was chosen over the alternatives.
The three fields most teams skip
Teams that do adopt ADRs tend to write the decision and the context but leave out the parts that make them genuinely useful.
Alternatives considered is the most important section. The decision itself is visible in the codebase. What is not visible is what you ruled out and why. Without the alternatives, future developers cannot tell whether you considered their idea or never thought of it. They end up re-evaluating options that were already evaluated, sometimes reaching the wrong conclusion because they missed the constraint that eliminated the alternative the first time.
Consequences forces honesty. Writing down the real downsides of a decision, the things developers will encounter because of it, is what separates an ADR from a justification document. The example above has real consequences: extra work, a gotcha for new contributors, a pattern that needs maintaining. When someone reads it six months later and the consequences match their experience, they know the record was written by someone who understood the tradeoffs. That builds trust in the document as a whole.
Status turns ADRs into a living record rather than a historical archive. Decisions get superseded. Technology changes. The constraint that drove a particular choice might disappear. Marking a decision as superseded, and linking to the record that replaced it, means the history reflects current reality rather than accumulating outdated guidance that nobody knows whether to trust.
The five statuses worth using are: proposed, accepted, rejected, superseded, and deprecated. Proposed covers decisions under active discussion. Accepted is the normal state. Rejected is for options that were put forward and turned down, worth keeping as a record so the team does not revisit the same idea every quarter. Superseded means a newer decision replaced this one, with a reference. Deprecated means the decision no longer applies but was not formally replaced by anything.
Where ADRs pay off
The value of an ADR is not immediate. For the first few months of a project, the team knows why decisions were made because the people who made them are still there. Writing it down seems redundant.
Then someone leaves. A new developer joins and spends a week puzzling over a pattern that took an hour to decide. A stakeholder asks why the system works a particular way during a client review and nobody can reconstruct the rationale clearly enough to explain it with confidence. A developer proposes revisiting a decision and the team realises they cannot remember the original constraints without digging through six months of messages. I have sat through that exact conversation more than once.
There is a newer benefit that did not exist a few years ago. AI coding agents working on your codebase have the same knowledge gap as a new developer, but they fill it with guesses rather than questions. When decisions exist as structured, queryable records, the agent can pull the relevant ones before writing code. The output fits the architecture rather than technically working while quietly violating three decisions the team made last year.
This is what the decision tracking in SpecSource is built around. ADRs live alongside the features and solution designs that reference them, with explicit links so you can trace from a business requirement to the decision that shaped how it was built. When the AI has access to the full decision history, the code it generates actually reflects the choices your team has made, rather than the choices an average developer might make on a project with no context.
Getting started
The highest-value moment to write an ADR is immediately after a significant decision is made, while the context is fresh and the alternatives are clearly in mind. The second best time is now, for the five or ten most consequential decisions in your current project.
Start with the decisions that confuse new team members most often. The ones that require a senior developer to explain. The ones where "why does this work this way?" takes a paragraph to answer rather than a sentence.
Write the context first. Then the alternatives. Then the decision and its consequences. The title and status can go in last.
The format matters far less than the habit. An ADR in a text file in your repository is infinitely more useful than an undocumented decision sitting in someone's memory, waiting for them to leave the project.