What the Handoff Drops
A piece of work almost never finishes in the hands that started it. Someone scopes it and someone else builds it. Someone builds it and someone else takes the pager for it. Someone takes the pager and then rotates off, and a year later a third person is reading the code trying to work out why it does the strange thing in the middle.
Each of those transfers has a document attached — a ticket, a design doc, a runbook, a README. And each of those documents is a lossy encoding of something that was, at the moment of writing, entirely obvious to the person writing it.
The Load-Bearing Detail
The failure isn’t that people write bad handoff docs. Most handoff docs are fine at what they attempt. The failure is that the important thing is usually not on the list of things you’d think to write down.
A ticket says what to build. It rarely says which of the three constraints in it was the real one and which two were guesses. A design doc explains the architecture. It rarely says which alternative was rejected because it wouldn’t work, versus which was rejected because nobody had time. A runbook says what to do when the alert fires. It rarely says that the alert has fired eleven times and ten of them were the same upstream partner doing a batch job at 2 AM.
That last category — the accumulated pattern-knowledge of someone who has lived with a system — is the part that never survives a handoff, because the person holding it doesn’t experience it as knowledge. It’s just what they know. Nobody writes down what they consider obvious, and the whole difficulty is that obviousness is not a property of the fact, it’s a property of the relationship between the fact and the person.
The Cost Shows Up Late
What makes this expensive is the delay. A dropped detail doesn’t fail at handoff time. It fails weeks later, when the new owner makes a perfectly reasonable decision that would have been obviously wrong to the previous owner.
They loosen a timeout that was tight for a reason nobody recorded. They remove a retry that looked redundant. They simplify a conditional that was there because of one customer whose data is shaped differently from everyone else’s. Every one of those is a good change on the evidence available. The evidence just wasn’t complete, and there was no way for the person to know that, because absence of context doesn’t announce itself.
The pathology this produces is worse than the individual bugs. Teams that get burned this way learn to treat all inherited code as untouchable. Nothing gets simplified, because nobody can distinguish the load-bearing weirdness from the accidental weirdness, and the safe move is always to leave it alone and build beside it. That’s how systems calcify — not from bad code, but from unexplained code that nobody feels licensed to remove.
Writing Down the Why
The practice that actually helps is narrow and specific: when you do something non-obvious, record why, next to the thing, in the same commit.
Not in the ticket, which will be closed and unsearchable. Not in a chat thread, which is gone. In the code, in a comment that says “this timeout is 3s because the upstream partner’s p99 is 2.4s and we’d rather fail fast than queue” — which is boring to write and enormously valuable to the person who finds it in eighteen months.
The test for whether a comment is worth writing isn’t “does this explain what the code does.” It’s “would a competent person, with no memory of this decision, plausibly undo it?” If yes, write the why. If no, skip it. That filter keeps the comment density low enough that people keep reading them.
None of this makes handoffs lossless. It just moves the loss from the load-bearing parts to the parts where it doesn’t matter — which is the most any documentation practice has ever achieved, and considerably more than most manage.