Every codebase I have worked on past the six month mark has had the same problem. A piece of code looks wrong. Nobody remembers why it is the way it is. Somebody changes it, two weeks later production breaks in a way the original author would have predicted in a single sentence, and by that point the original author has moved teams, the Slack thread has scrolled into the abyss, and the reasoning is not written down anywhere.
A decision log fixes this for almost no effort. One file in the repo, one paragraph per decision, that is it.
The format
Permalink to “The format”docs/decisions.md. Append-only. New entries at the top.
## 2026-04-28: Items table is append-only
We will not update or delete rows in `items`. Updates create a new row
with the same `external_id` and a new `version`. Reads filter by latest.
Why: the consumer side needs an audit log, and we already pay for the
write throughput. The alternatives (CDC, separate audit table) would
double the storage cost and add a service.
Reversible: yes, but expensive. Six months of data would need rewriting.
Author: krisztian-gajdarWhy this format
Permalink to “Why this format”Date first, because when you read this in a year you want to know when the constraints applied. A decision can be right at the time and not right now, and you want to be able to tell which is which without going on a Slack archaeology trip.
The one-sentence summary is a forcing function. If the summary needs more than a sentence, the decision is usually still an argument and not a decision.
The “why” is the whole point. Without it the entry is documentation of the current state, which the code already provides. The why is what tells the next person whether the conditions still hold.
Alternatives matter, but only briefly. This is not an RFC. One sentence per real alternative is enough so the reader knows you considered them and can ask follow-ups if they care.
Reversibility is the field that has saved me the most time. “Reversible: yes, cheap” tells the future me to just go ahead and change it. “Reversible: no” tells me to stop and think for a minute. I almost never wrote this field on my first attempt at a decision log and I missed it every time.
Author initials, not full names. The log is for the team, not for performance review. Initials keep it skimmable, full names make it feel like a courtroom transcript.
What goes in it
Permalink to “What goes in it”Anything where the code’s shape is not obvious from the requirements. A few examples from real logs I have kept:
- We chose Postgres over the document store the team had used previously.
- We are using a sync HTTP client instead of async because the workload is CPU-bound.
- We are not validating field X on ingest because the upstream system already does.
- We dropped support for batches over 1000 because the GPU cannot fit it.
If the answer to “why is this code like this” is “because we decided,” the decision belongs in the log.
What does not go in it
Permalink to “What does not go in it”Implementation details. Naming conventions. Style choices. The log is for decisions where the alternatives were real and we picked one. “We named the function process_event instead of handle_event” is not a decision, it is a coin flip.
How it pays off
Permalink to “How it pays off”The most frequent payoff is the unglamorous one: I read my own entry from a year ago and remember why I did the thing I did. This happens to me probably once a week. The second payoff is onboarding. New team members read the file once and walk away with six months of context they would otherwise have to reconstruct from PR comments. And the third one shows up in code review, where “see decision 2026-03-12” replaces a five-comment thread, because the decision is already written down and you do not re-litigate it on every pull request.
Why it lives in the repo
Permalink to “Why it lives in the repo”Because the alternative is a wiki, and wikis drift out of sync with the code they describe. Always. The repo is where the code is, and so the decision log goes next to it. A pull request that changes the assumption updates the log in the same PR, a decision that gets rendered obsolete gets superseded by a new entry on top, and old entries become history. You do not edit them, you write a new one.
The whole thing takes maybe ten minutes a week. After a year it is the document I reach for the most, and I do not really know why I went so long without it.