Event-Driven Capture
Your documentation problem isn’t what you think it is
An engineer needs to change the retry ceiling on the payments service. The code comment says “exponential backoff with jitter, 5-second ceiling.” Solid documentation by any reasonable standard. She checks the PR that introduced it: “tuned retry config,” one LGTM, no discussion. She shrugs, raises the ceiling to 10 seconds, and ships it.
Six weeks earlier, a load test had shown retry clustering under sustained load. The 5-second ceiling was a deliberate trade-off between recovery speed and downstream pressure. That reasoning lived in one person’s head and was never written down. Not because the team was careless. The code comment captured what the ceiling is. It just didn’t capture why, because nobody has time to document the reasoning behind every line of code.
When reasoning is never attached to a value, engineers learn to treat every value as arbitrary. That’s rational behavior given what they can see. A deliberate decision just got overwritten by someone who had no way to know it was deliberate, and this happens multiple times per sprint on every team. The system accumulates implicit risk: careful trade-offs silently undone by people who treated them as safe to change. Nobody tracks it because it doesn’t look like a failure. It looks like engineering.
For twenty years, the response to this inefficiency has been the same: make the documentation better. Three approaches to documentation have coexisted for years, each solving something real.
Auto-generation (Swagger, OpenAPI, JavaDoc) kept specs current by deriving them directly from code, which was genuine progress on the freshness problem. The catch is that auto-generated specs only surface what was in the code annotations, and engineers annotate structure, not reasoning. The spec tells you a parameter exists with a default value. It doesn’t say why that default and not another, because that reasoning was never in the code to begin with. This is a limitation of the practice rather than the tool: OpenAPI description fields can hold any rationale you want to write, and almost nobody writes it.
Living documentation, which Cyrille Martraire’s 2019 Addison-Wesley book covers through Specification by Example, docs-as-code, and executable specifications that break when behavior drifts, solved the coupling between docs and behavior in a way auto-generation couldn’t. The failure shows up one layer down: tests verify behavior, not the reasoning behind it. A test confirms the retry ceiling is 5 seconds, and tells you nothing about why 5 and not 10.
Knowledge bases (Confluence, Notion, GitBook) made documentation searchable, linkable, and organized, which was a real accessibility win. GitHub’s 2017 Open Source Survey of 5,500 developers across 3,800 projects found 93% said documentation is incomplete or outdated. Accessible outdated documentation is arguably worse than no documentation, because it creates false confidence. The problem was never that wikis don’t exist.
AI-powered search tools like Glean made it even easier to find what already exists across Slack, docs, and code. But they can only surface knowledge that was externalized somewhere. If the reasoning behind the retry ceiling was never written down, no search index will return it.
All three approaches improved the artifact, and none of them touched the reasoning. The “why” behind decisions, the trade-offs considered, the constraints at the moment of choice, doesn’t exist anywhere in these systems and never did. That reasoning lives in the engineer’s head, and it operates on a fundamentally different decay timescale than the code. No tooling innovation can fix this as long as documentation happens on a schedule, because the schedule itself is the problem.
Not all knowledge decays at the same rate.
Research on memory type decay consistently shows that contextual intent, the “why” behind a decision, is the most vulnerable category. If the engineer had Slacked the original author six weeks later, the most likely response is “I don’t really remember, it was a couple months ago, let me check my notes.” And if the notes exist at all, they capture what was decided, not why. In the less common case where someone does answer confidently, what you get is a reconstruction, not a retrieval.
That distinction matters more than it sounds. Memory reconstruction is schema-driven: when you ask an expert to explain a past decision, they reconstruct from their current mental model rather than retrieving a stored record. The reconstruction tends to make the decision look more coherent than it was in the moment. It smooths over the incidental anomalies and the uncertainty that actually drove the choice, the parts that are hardest to compress. The harder the original reasoning was to articulate, the worse the reconstruction tends to be, and it arrives confident about all of it. It’s just not always what happened.
The meeting notes from an architecture discussion capture what was decided, maybe a recording link in Confluence. They don’t capture why option B was chosen over option A, what constraints made the team uncomfortable with option A, or which edge case nobody had a good answer for. The reasoning that drove the decision is the first thing that evaporates, and it’s the thing the next engineer will need most.
The timing mismatch is structural. Documentation practices operate on weekly, monthly, or quarterly cadences. The most valuable knowledge, the contextual intent behind consequential decisions, decays in days. A team that’s rigorous about documentation, that consistently follows through on the calendar reminder to write up the decision, is still documenting the reconstruction.
There is no documentation cadence fast enough to catch contextual intent before it fades. The answer isn’t a faster cadence but a different capture trigger entirely.
“Capture reasoning at the moment of decision” sounds right until you think about how many commits a team ships per week. At 40 PRs a week, you cannot capture everything, so you need a filter.
Most commits are mechanical consequences of earlier decisions: a rename, a dependency bump, a formatting fix, a test that failed because of an upstream change. The reasoning behind those changes is either obvious from the diff or irrelevant to future decisions. What matters is the much smaller set of changes where an engineer chose between alternatives under real constraints. That’s where reasoning lives, and lines of code is a poor proxy for finding it. Incidentally, this is also what separates AI-assisted engineering from vibecoding: a vibecoder makes user-level choices (”make this column wider”), not trade-offs with downstream consequences. The decision weight lives in the human judgment, not the code.
A 3-line config change can be the most consequential PR of the quarter. A 500-line refactor can be purely mechanical. The retry ceiling is 3 lines, and it got overwritten six weeks later by someone who didn’t know it was deliberate.
I’ve been calling this “decision weight” for lack of a better term. You can usually spot it. The change affected how the system behaves, not just how the code is organized, or it touched something on the critical path. There was substantive back-and-forth in the PR where someone pushed back and the author had to defend a choice. The change came out of an investigation or a spike. It modified a shared contract: an API, a schema, an event format. It connects to a recent incident.
The retry ceiling would have scored on two: behavior changed (the ceiling moved), and the payments path counts as critical. The one-LGTM PR is a useful cross-check, not a pass.
None of these signals is definitive on its own. They’re pattern-matching, and pattern-matching requires judgment. A bot can tell you a file changed. It cannot tell you whether the change carried reasoning that matters to preserve, and that’s the hard part that needs to be named before describing what to do about it.
Detection and timing together define the methodology: capture isn’t a documentation habit but a response to a signal, within a window.
Detection is the hard phase. During sprint review, flag PRs that match two or more of the decision-weight signals above. Detection requires judgment that automation can support but not replace, and that distinction is what makes event-driven capture different from “write better commit messages.”
When a decision-weight change is detected, the reasoning needs to be captured within 24-48 hours. This window isn’t arbitrary: wait until the sprint retrospective and you’re already documenting a reconstruction, for the reasons above. The manual version is a 10-minute async exchange with the engineer who made the change, a Slack thread or a Loom. The question isn’t what changed, which the diff answers, but what alternatives were on the table and why this one. Async matters for a specific reason: synchronous sessions create social pressure that causes engineers to smooth over uncertainty and unresolved edge cases, which are exactly the parts that matter most to preserve.
Grounding closes the loop. Make the captured reasoning findable and connected to the code it describes. The manual version is a short decision record linked from the PR, not an ADR template with twelve required fields. A paragraph covering the alternatives, the constraints, and the context at the time, pinned where the next engineer will actually look for it, which is the PR rather than a wiki folder nobody opens.
The three phases aren’t sequential. Detection shapes what you extract, and what you extract shapes how you ground it. The retry ceiling change needed a record of what was downstream and why that load threshold mattered, not just what the ceiling is, and those are different questions that live in different places.
A 10-minute exchange per decision-weight PR sounds reasonable until you do the math. At 40 PRs a week with maybe 20% carrying decision weight, that’s 8 exchanges a week. Except detection itself takes time, and then there’s the writing, which is the real friction: articulating why you made a trade-off is genuinely hard, harder than documenting what you did. The “lightweight process” starts looking like a part-time job by the time you’re at 60 engineers shipping daily. The manual version works, and it’s where most teams should start, because you can’t automate detection until you understand what you’re trying to detect. That’s also why we’re building Mentat.
Think about the last sprint. How many of the changes that shipped carried real decision weight? For how many of those could your team recover the reasoning six months from now without pinging the person who wrote the code? That’s the diagnostic.


