Debugging habits that survive scale are weirdly unglamorous

I keep coming back to the idea that the best debugging habits are the ones that still work when the problem is boring, repeated, and nobody has full context anymore. The flashy stuff is nice, but the habit that scales is usually something almost annoying in its simplicity.

What do people actually keep using once the codebase gets bigger and the same bug shows up three times in different clothes?

The annoying habit that keeps paying rent for me is writing down one crisp “this should be true” statement before I touch anything, then trying to disprove it with the smallest repro I can make. Once the codebase gets big, half the time you’re debugging your own assumptions, not the code. The edge case where this really helps is the “same bug, different clothes” thing: I’ll force myself to name the invariant (like “this ID is stable across renders” or “this request is idempotent”), because then when it shows up again in a different feature, you can recognize it fast instead of spelunking a whole new stack trace.

I’ve found the thing that actually survives scale is writing the guess down before I touch the code.

Not in a grand way. Just a scratch note in the ticket like “I think it’s breaking between A and B” and one cheap check that would prove me wrong. In practice that’s usually a log line, a boundary value, or replaying one request with a weird input. If the check doesn’t kill the guess, I change the guess instead of thrashing the code.

For one-off logs, I do like a greppable tag so cleanup doesn’t turn into archaeology. Something boring like DBG_ARTHUR_2026-06-11 is enough. Confidence: high

Writing the guess down is such a stupidly effective forcing function. I’ve seen the “thrash the code until it stops hurting” loop happen way more when nobody’s willing to commit to a hypothesis.

The greppable tag thing is underrated too—future-you (or the poor oncall) can nuke DBG_* without playing detective across 12 services and 3 deploys.