Code review generates findings. Triage converts findings into decisions: fix this, defer that, dismiss the other. The findings are usually documented. The decisions almost never are.
This is a problem. When a finding resurfaces six months later – in a new scan, during an audit, or when a new team member asks why the code looks this way – nobody can explain why it was dismissed. Was it a deliberate, informed decision? Was it an oversight? Did someone just click “dismiss” to clear their queue?
Without documented decisions, teams relitigate the same findings repeatedly. They lose the reasoning that informed past choices. They cannot demonstrate to auditors or new hires that review decisions were thoughtful rather than careless.
Documenting review decisions is not bureaucracy. It is institutional memory. And teams that do it well spend far less time revisiting old ground.
Why decisions disappear
The reason review decisions go undocumented is not laziness. It is that most review workflows are optimised for the finding, not the decision.
A typical code review tool presents a list of findings. For each finding, the reviewer has a few options: fix, defer, or dismiss. Most tools capture which option was chosen. Almost none capture why.
The “why” lives in a Slack conversation, a standup discussion, or a reviewer's memory. Slack messages are buried within days. Standup discussions are not recorded. Memory is unreliable and leaves the organisation when the person does.
This is how institutional knowledge evaporates. The decision was made for a good reason. The reason was never written down. A year later, a new team member sees the same pattern and has no context for why it exists. They either fix it (potentially reintroducing a problem the original decision was avoiding) or they flag it as a new finding, starting the triage cycle over again.
What to document
A useful review decision record captures four things.
What was found. The specific finding, including where it was found, what category it belongs to, and what severity it was assigned. This provides the context that makes the rest of the record meaningful. Without knowing what was found, the decision is uninterpretable.
What was decided. The action taken: fixed, deferred to a specific date or sprint, accepted as a known risk, or dismissed as a false positive or not applicable. This is the outcome. Most tools capture this much. The value comes from the next two elements.
Why it was decided. The reasoning behind the decision. A finding might be dismissed because the affected module is scheduled for replacement next quarter, making the fix a wasted investment. A finding might be deferred because it requires a coordinated change across three services and the team is mid-release. A finding might be accepted as a known risk because the mitigation cost exceeds the expected impact. Whatever the reason, writing it down transforms an opaque action into an explained choice.
Who decided. The person or group responsible for the decision. This is not about blame. It is about accountability and follow-up. If a deferred finding needs to be revisited, knowing who made the original decision means knowing who has the context to reassess it. If an auditor asks why a security finding was dismissed, knowing who made the call means knowing who can explain the reasoning.
The four audiences for decision documentation
Review decisions serve four distinct audiences, each with different needs.
Future you. Six months from now, you will not remember why you dismissed finding #47. You will remember that you had a good reason. The documentation provides the reason so you do not have to reconstruct it from memory. This is the most immediate and practical audience.
New team members. When someone joins the team and starts reading the codebase, they will have questions. Why does this module use a different error handling pattern? Why is there a known vulnerability in this dependency? Why has this TODO been here for two years? Documented decisions answer these questions without requiring a senior team member to provide oral history.
Auditors and compliance reviewers. In regulated industries, auditors need evidence that code review happens and that findings are addressed. “We reviewed the code and fixed the important stuff” is not evidence. A documented decision log with findings, decisions, reasoning, and responsible parties is evidence. It demonstrates a controlled, repeatable process.
The team itself, over time. Patterns emerge in decision logs. If the same type of finding is dismissed repeatedly across different modules, perhaps the severity calibration is wrong, or perhaps there is an underlying architectural issue that should be addressed at a higher level. Decision logs make these patterns visible.
Formats that work
There is no single right format for documenting review decisions. The best format is the one your team will actually use. That said, three approaches have proven effective across different contexts.
Architectural Decision Records (ADRs). ADRs are a well-established format for documenting significant technical decisions. Each record follows a template: title, status, context, decision, and consequences. ADRs work well for significant review decisions – the finding that led to a major refactoring commitment, the architectural pattern that was validated or rejected, the security risk that was formally accepted. They are too heavyweight for routine triage decisions, but for decisions that affect the project's direction, they provide the right level of structure.
Triage logs. A triage log is a lighter-weight format that captures decisions in a structured table or list. Each entry records the finding ID, the decision, a brief justification, the decision-maker, and the date. Triage logs work well for batch processing of review findings. After a code scan produces 60 findings and the team triages them in a meeting, the triage log captures all 60 decisions in a single document. It is quick to write, easy to reference, and straightforward to search.
Inline comments with context. For decisions that are best understood in the context of the code itself, inline comments work well. A comment explaining why a particular pattern was chosen, why a potential issue was accepted, or why a seemingly simpler approach was rejected provides context exactly where a future reader will need it. The key is including the “why,” not just the “what.” A comment that says ‘accepted risk per review 2024-11-15' is useful. A comment that says ‘this is fine' is not.
Common mistakes in decision documentation
Teams that start documenting review decisions often make a few predictable mistakes.
Documenting everything with equal detail. Not every finding warrants a paragraph of justification. A low-severity style suggestion that was dismissed because the team has no style convention for that pattern needs one sentence: “Dismissed – no team convention, not worth creating one for this case.” A critical security finding that was accepted as a known risk needs a full explanation: what the risk is, why it is being accepted, what mitigations are in place, and when it will be reassessed. Scale the documentation to the significance of the decision.
Writing justifications that do not justify. “Dismissed – not a priority” is not a justification. It restates the decision without explaining it. Why is it not a priority? Because the affected module is rarely used? Because a bigger refactoring is planned that will address this? Because the team is in a critical release window? The justification should provide enough context that someone who was not in the room can understand the reasoning.
Treating documentation as a one-time activity. Decision documentation is only valuable if it is maintained. Deferred findings should have follow-up dates. Accepted risks should be periodically reassessed. A decision log that was last updated eight months ago is not institutional memory – it is a historical artefact.
Structured triage as decision documentation
The most effective way to document review decisions is to make documentation a natural part of the triage workflow rather than a separate step that happens afterwards.
When triage requires a decision and a reason for every finding, documentation happens automatically. There is no separate step to forget or skip. The act of triaging is the act of documenting.
VibeRails builds this into its triage workflow. Every finding has a status (open, fixed, deferred, dismissed) and a notes field for the reasoning. When a team triages findings, the decisions and justifications are captured as part of the triage itself, not as an afterthought. The result is a complete decision record that is produced during the normal workflow rather than requiring extra effort.
This matters because the biggest risk to decision documentation is not that teams refuse to do it. It is that they intend to do it later and never do. By embedding documentation into the triage step, the intention and the action happen simultaneously.
The compound value of decision history
Individual decision records are useful. A history of decision records is high-impact.
Over multiple review cycles, the decision history reveals patterns that no single review can show. If the same finding type is dismissed four times in four consecutive reviews, something is miscalibrated – either the detection rule is too aggressive, or the team is systematically ignoring a real problem. If deferred findings are consistently never addressed, the deferral process is not working.
Decision history also accelerates onboarding. Instead of explaining years of context verbally, a team lead can point a new hire to the decision log: here is what we found, here is what we did about it, and here is why. The new hire gets the institutional context in an hour instead of over six months of osmosis.
Write down why you made the decision. Your future team will thank you for it.
Limits and tradeoffs
- It can miss context. Treat findings as prompts for investigation, not verdicts.
- False positives happen. Plan a quick triage pass before you schedule work.
- Privacy depends on your model setup. If you use a cloud model, relevant code is sent to that provider; local models can keep inference on your own hardware.