Lesson 3 of 4
Architecture Decision Records in Practice
The cheapest high-value habit in the discipline: one short document per significant decision, capturing the forces and the alternatives you rejected.
Six months after a decision, the code shows what you chose. Nothing shows why, what else you considered, or what you knew at the time. That missing context is why teams re-litigate settled questions, and why a new joiner "fixes" something that was deliberate. An Architecture Decision Record is a one-page fix for a problem that otherwise costs a team weeks a year.
The format
# ADR-014: Store monetary amounts as integer minor units
Status: Accepted
Date: 2026-03-11
Deciders: Payments team, platform architect
## Context
We handle GBP, EUR and JPY. Two rounding defects reached production last
quarter, both caused by floating-point arithmetic in discount calculation.
JPY has no minor unit, so a fixed two-decimal assumption is also wrong.
Finance reconciliation requires exact totals to the smallest unit.
## Decision
All monetary amounts are stored and transported as integers in the currency's
minor unit, together with an ISO 4217 currency code. No float or double may
represent money anywhere in the system. Currency-aware exponents come from the
ISO table, not from a constant of 2.
## Alternatives considered
- Decimal/NUMERIC columns: exact, but our message transport serialises via
JSON, and every consumer would need a decimal library to avoid silently
parsing into a float. Rejected on the transport boundary, not the storage.
- A money library on top of floats: hides the problem without removing it.
- Keeping floats with rounding at display time: this is what caused both
defects; the error accumulates before display.
## Consequences
- Every API contract changes: amounts become integers plus a currency code.
- A migration is required for 4.1M existing rows, with a reconciliation report.
- JPY is handled correctly for the first time.
- New engineers will get this wrong; a lint rule rejects float-typed fields
whose name matches /amount|price|total|fee/.What to write one for
| Decision | ADR? | Why |
|---|---|---|
| Splitting billing into its own service | Yes | Expensive to reverse, affects several teams, invites re-litigation |
| Adopting a specific date library | No | One adapter, reversible in a day |
| How tenants are isolated | Yes | Determines the data layer permanently and has compliance consequences |
| The layering rules inside one service | Sometimes | Worth one if it is unusual or if teams keep questioning it |
| Choosing Postgres over MySQL | Yes | Reversal is a migration, and someone will ask why every year |
| Renaming an internal module | No | A pull request |
Asked why the system works this way
As a developer
Explains what the code does, and reconstructs a plausible reason. The reconstruction is often wrong in ways nobody can detect, and it hardens into folklore.
As an architect
Links to the ADR. The answer includes what was rejected and under what conditions the decision should be revisited — so the person asking can tell whether their new information actually invalidates it, or whether it was already considered and rejected in 2026.
In practice
The ADR that saved a quarter
A new principal engineer joined a company running a modular monolith and proposed splitting it into microservices, citing scaling concerns. The proposal was credible, senior, and had leadership attention.
Constraints
- New principal with a strong track record
- Leadership sympathetic to the proposal
- Existing team unable to articulate why the monolith was chosen
- A rewrite would consume roughly two quarters
Decision
Before approving, read ADR-003, written two years earlier, which had chosen the modular monolith explicitly for a team of nine and named the trigger for revisiting: "when two or more teams are blocked on each other's releases more than twice a month".
Why
The ADR turned an argument about preferences into a factual question with a documented threshold. The team measured: cross-team release blocking was happening roughly once a quarter, not twice a month. The condition the original decision had named had not been met.
What it cost
The principal was, reasonably, frustrated to have a proposal blocked by a two-year-old document. The organisation kept two quarters of delivery capacity. The ADR was updated — by superseding it with a new one recording that the trigger had been re-examined and confirmed, along with a scheduled review date. Writing the trigger into the original decision is what made this possible; without it the argument would have been decided by seniority.
Your team made a significant decision in a meeting last week and nobody wrote it down. Is it too late?RevealHide
No, and a late ADR is dramatically better than none. Write it now, date it with the actual decision date, and note that it was recorded retrospectively. You will find that reconstructing the "alternatives considered" section is already harder than it would have been a week ago — which is the argument for writing them promptly, and also a small demonstration of exactly what the document exists to preserve.
Key takeaways
- An ADR records why a decision was made and what was rejected, not what the system looks like.
- The "alternatives considered" section carries most of the value and is the one most often skipped.
- Never edit an accepted ADR; supersede it, because the history is the point.
- Write one when the reversibility test says the decision is architectural.
- Naming the condition that should trigger a revisit turns future arguments into measurements.