Review this decision log. It holds 140 records over four years in the repository. 31 are marked superseded and 9 of those name no successor. Two records state conflicting positions on the same message bus and both read "accepted". The template has a Consequences heading that is empty in 96 records. A new engineer asked what the current position on async messaging is and spent half a day finding out. What would you change, what would you remove, and what would you leave alone?
Show the full answer Hide the answer
What is actually required
A decision log has one job that justifies its cost: a reader must be able to answer "what is our current position on X and why" in about a minute. Half a day means the log has failed at that job while looking healthy by every process metric, because 140 records over four years is a good writing rate.
The defect is not diligence. It is that currency is stored as a word in a field rather than as a link, so nothing can compute what is current.
What I would change
- Supersession becomes a link, not a label. Every new record names the id of the record it replaces, and the replaced record gets one line pointing forward. The nine orphans get a closing record each that states what replaced them, or that the position was abandoned and why.
- Generate the index. A short script reads the front matter and emits a page listing one current record per subject, with the superseded chain collapsed beneath it. Generated from the files, so it cannot drift from them. This is what turns half a day into a minute.
- The two conflicting accepted records get resolved in public. Write a third record that states the position now, names both, and says which constraint changed. Do not edit either of them.
- Replace the empty heading. "Consequences" written at decision time is speculation, which is why 96 records skip it. Make it "Consequences observed since" with a six-month review that either fills it in or marks the record stale. A heading nobody can fill is a template bug.
What I would remove
None of the records. Michael Nygard's format, published in 2011, makes immutability the point: the value of a wrong record is that it explains why the code looks the way it does. Deleting the 31 superseded records would leave the current ones unexplained.
What I would remove is process: the requirement that a record exists before merge, if that is what produced 96 empty Consequences sections. Volume is not the goal.
What I would leave alone, though it looks odd
The 140 records including the duplicated and contradictory ones, and the rate at which they are written. A log with two conflicting positions recorded honestly is more useful than a tidy log that hid one of them. The failure mode to fear is not contradiction, it is an accepted record that quietly stopped being true and has no successor — which is exactly what the nine orphans are.
How I would argue this in review
Time the lookup, in front of people. Ask someone to find the current position on async messaging and put a timer on it. Then show the generated index and repeat the exercise. The argument is not about ADR hygiene, it is that the log is a lookup structure and it currently has no index, which costs every new engineer half a day and costs every reviewer the confidence to say "we decided this already".
When this is the wrong call
A five-person team with 11 records does not need supersession links or a generated index: the whole log fits on one screen and the person who wrote it is in the room. Introduce the machinery when the log outgrows one reader's memory, which in practice is somewhere around 30 records or the second engineer who joined after they were written. Below that, one living page per subject with a dated change list beats a formal record set, because the failure being solved here is navigation at volume and there is no volume yet.