ADRs: Write Decisions Down Before They're Folklore
An undocumented decision doesn't disappear — it becomes folklore, and folklore costs engineer-weeks when re-litigated and agent-hours when re-refactored.
The Decision That Nobody Wrote Down
Six months into a project, a new engineer joins the team. They open the codebase, see SQLite as the storage layer, and immediately know what this means: a placeholder. Someone started quickly, used the embedded option, and meant to migrate later. The new engineer opens a pull request to migrate to a hosted Postgres database. It is a perfectly reasonable inference.
The PR takes two days to write. It gets reviewed by three engineers. It triggers a three-hour debate that reconstructs, imperfectly, the conversation from six months ago. The PR is closed. The correct decision — SQLite, intentional, for specific reasons — was the right call then and it is the right call now.
Two engineer-days and three hours of debate, for a decision that was already made. The only thing missing was the document that recorded it.
This is how decisions become folklore. Nobody writes the decision down. The original engineer knows why. The context is clear at the time. Six months later, the original engineer is on a different project or off-boarded entirely, and the context walks out the door with them.
What an ADR Is
An Architecture Decision Record is a short document that captures one architectural decision: what was decided, the context that forced the decision, and the consequences of that choice. It is not a design document. It is not a specification. It is a record.
The canonical ADR format has three sections:
Context. The situation that created the decision. What problem were you solving? What were the constraints? What alternatives were considered and why were they not chosen? This section is the most important — it is the one that will prevent the re-litigation six months later.
Decision. One sentence: what was decided. "We will use SQLite for all trade and state persistence." Unambiguous. No hedging.
Consequences. What does this decision mean going forward? What are the benefits? What are the trade-offs honestly accepted? Under what conditions should this decision be revisited?
A complete ADR for the SQLite decision might be 20 lines. It takes 20 minutes to write. It prevents the two-day PR and the three-hour debate — and more importantly, it prevents a coding agent from "helpfully" migrating the database on the grounds that SQLite doesn't scale to multi-host deployments.
The Status Is Not Optional
ADRs have a lifecycle. The lifecycle has exactly four states: proposed, accepted, superseded, and deprecated.
The lifecycle rule that matters most: accepted ADRs are immutable. You do not edit an accepted ADR. You do not update it to reflect new understanding. If the decision was wrong, or the context has changed, you write a new ADR that supersedes the old one. The old ADR gets a note: "Superseded by ADR-009." The new ADR references the old one.
This is not bureaucratic overhead. It is an audit trail. The full history of a decision — including why a previous decision was overturned — is one of the most valuable pieces of documentation a codebase can have. When a coding agent reads ADR-003 and sees "Superseded by ADR-009," it knows to read ADR-009 for the current constraint. When it reads ADR-009, it can also read ADR-003 to understand what was tried before.
The immutability rule also solves a subtle problem: if accepted ADRs can be edited, they become unreliable. An agent loading the ADR cache cannot know if the document reflects the original decision or a revised version. Immutability makes accepted ADRs trustworthy.
The Agent Case for ADRs
Human engineers who were present for the original decision can at least ask a question before opening a migration PR. Coding agents cannot. They operate on what is in the codebase, the context files, and whatever documentation is accessible at session start.
When Claude Code is dispatched on a refactoring task in the Foresight codebase, it sees SQLite and infers from its training data that SQLite is typically a placeholder for development workloads. Without an ADR explaining why SQLite is intentional for a single-host trading bot with no network latency tolerance, the agent's inference is reasonable. With the ADR, the inference is corrected before any code is written.
This is the compound value of ADRs in agent-driven development: they do not just prevent a human from re-litigating a decision. They prevent an agent from re-litigating it on every dispatch. An ADR loaded into the agent's context at session start is an active constraint, not a historical artifact.
Mission Control's context file system references relevant ADRs in the key-files table. When Knox dispatches a coding agent against a service that has accepted ADRs, those ADRs are part of the operating context. The agent reads them. The constraints are active.
What Belongs in the Context Section
The Context section is the hardest to write well and the most valuable to write well. The temptation is to be brief: "We needed a database." That is not context. That is a premise.
Real context:
## Context
The trading bot runs continuously on a single dedicated machine. It needs persistent
storage for active trades, order history, and bot state. Two options were evaluated:
1. **SQLite** — embedded, file-based, zero network dependency, ships with Python.
2. **Hosted database** — managed service, accessible from any machine, adds a monthly
cost and a network round-trip on every query.
The bot operates on one machine only. There is no current or planned multi-instance
deployment. Network latency on every order-state read would add jitter to
latency-sensitive operations. Budget is a factor — adding a monthly database service
cost for a workload a flat file handles cleanly is not justified.
This context section does five things: it names the system, it names the options considered, it explains why each option was evaluated on specific criteria, it names the constraints that eliminated the hosted option, and it establishes the scope condition under which this decision applies ("one machine only — no multi-instance deployment"). A future engineer or agent reading this knows exactly when the decision should be revisited: when the scope condition changes.
What to Do When the Decision Was Wrong
The immutability rule raises a question: what if the decision was genuinely wrong? What if the consequences section listed trade-offs that turned out to be worse than expected?
Write a new ADR. State the new context — what changed? What trade-offs became untenable? What new constraint emerged? State the new decision. Reference the superseded ADR.
The original ADR does not become false. It remains a true record of what was decided and why, with the honest acknowledgment that it was superseded. The new ADR is the current constraint. Both are visible.
This is better than the alternative — editing the accepted ADR to retrofit the new reasoning — because it preserves the full audit trail. "We tried X, it worked for six months, then Y happened, so we moved to Z" is a richer knowledge record than "we use Z."
When Knox reads the ADR history for a Tesseract Intelligence service, the sequence of decisions is more informative than any single decision in isolation. The constraints that were active six months ago, the context that changed, the reasoning for each shift — that is institutional memory that compounds over time.
One ADR Per Decision
The only scope rule for ADRs: one decision, one ADR. Do not combine decisions. Do not write "ADR-005: Infrastructure choices." Write "ADR-005: SQLite for trading state" and "ADR-006: pm2 for process management." Each document covers exactly one decision so it can be superseded independently when that specific decision changes.
Now apply this: take the scenario below and write the ADR that would have prevented the two-day migration PR.
What's Next
The next lesson covers runbooks — the operational counterpart to ADRs. Where ADRs record why a decision was made, runbooks record how an operation is performed. Both exist for the same reason: to remove dependency on the memory of whoever was present when the knowledge was created.