Being Wrong in Writing: How an Instrumentation-Only Commit Rescued a Debugging Investigation

I recently closed out an investigation that I got wrong before I got right, and the wrongness is the interesting part. Not because the mistake was exotic — it was the most ordinary mistake in debugging — but because the process that caught it is one I'd been building deliberately, and this was the first time I watched it earn its keep end to end.

The confident wrong answer

The bug presented as a construction failure: the bot's assembly logic would reach a point where it should have been able to build a valid result, and instead it came up empty. A screening layer sits right in that path, and it consumes an external data field from an upstream feed. My inference went the way inference tends to go under time pressure: I looked at the failure, looked at the nearest suspicious dependency, and wrote down a root cause. The field must not be arriving. The screen treats missing data as disqualifying, so nothing survives to be constructed.

It's a satisfying story. It has a villain (the data feed), a mechanism (missing values read as bad values), and it fits the symptom. I committed it to the issue's spec file as the working root cause.

Here's the thing I've learned to do that saved me: I wrote it down as a falsifiable claim, not a diagnosis. The spec didn't say "the data is missing, therefore fix X." It said, in effect: if this root cause is correct, then when we look, we will find evaluated candidates for which the field was never delivered at all. A prediction specific enough to be checked against reality.

The rule: no fix until the question is answered

The next commit was the discipline itself. I shipped a change whose entire job was to make one distinction the code had been collapsing: unknown — the field never arrived — versus present but outside an acceptance condition — the field arrived and failed a check. The existing telemetry lumped them together: a candidate that never received the field and a candidate whose delivered value simply didn't satisfy the screen produced the same downstream signal. My root-cause theory lived entirely inside that ambiguity.

The commit was labeled instrumentation-only, and it meant it. No behavior change, no fix smuggled in alongside, no "while I'm in here." Its only deliverable was an answer.

This is a rule I've adopted after being burned by its absence: when a root cause hasn't been observed — only inferred — you don't get to fix it yet. You get to build the instrument that would confirm or kill it. Fixing an unconfirmed root cause feels productive, but it's really just encoding your guess into the codebase, where it's much more expensive to un-believe.

The answer, the correction, and why the wrongness stays on the record

The instrumentation answered, and the answer disproved the hypothesis: the field was arriving. My root cause was dead.

I committed that correction as its own spec entry, plainly labeled: the investigation continues, and my previous root cause was wrong. That felt slightly humiliating and completely necessary. The wrong claim and its refutation now sit next to each other in the history, which means the next time I'm tempted to blame the data feed for something, there's a documented case of that instinct failing a direct test.

With the missing-data theory eliminated, the search narrowed fast — which is the underrated payoff of falsification. The real defect was upstream of the screen entirely: a selector earlier in the pipeline was supplying an input from a category the downstream construction step could never succeed with. The screen wasn't rejecting good candidates; everything downstream was behaving correctly on the wrong input. The actual fix was then small and confident: detect the incompatible selection and recover to a compatible one. Root cause committed, fix committed, handoff note closed out — same day the instrumentation answered.

If you only read the final commit, this looks like a clean investigation. The full arc is: confident wrong claim → instrumentation → refutation in the data → correct root cause → fix. And the wrong claim earned its place in that sequence. It wasn't noise; it was the engine. Because it was stated precisely enough to be falsified, it told me exactly what instrument to build. A vaguer suspicion — "something's off with the data, probably" — is much harder to falsify and might well have survived contact with the evidence. Killing the precise version is what pointed the search upstream.

That's why the correction ships alongside the claim it refutes. There's a version of build-in-public that only publishes the wins, and a version of engineering-with-agents that quietly rewrites its priors and pretends the first theory never existed. I think both are worse than the alternative: commit the claim, commit the test, commit the correction, in that order, in the open. The bug is fixed. The instinct that misdiagnosed it is on file. Both are shipped artifacts now, and I'd argue the second one is worth more.


Nothing on this site is financial advice. This is an engineering journal about building software; it contains no market data, trade records, or recommendations.

Disclaimer: This journal documents a personal software-engineering project. The system described trades a paper (simulated) account. Nothing here is investment advice, a recommendation, or a signal, and no market data or trading performance is provided. Content is about building software.