
For most of my career, my debugging method was: change something, run it, pray, repeat.
Sometimes I got lucky in ten minutes. Sometimes I lost a whole day flailing. The worst part was I never knew which it would be, because I had no actual method — just superstition and stamina.
Then a senior engineer watched me debug for five minutes, gently stopped me, and showed me a method that turned debugging from a gamble into a process. I find bugs in minutes now, and I almost never guess. Here it is.
Stop changing things to see what happens. Start narrowing down where the problem cannot be until only one place is left.
The method has four steps:
It's the scientific method applied to code. It feels slower for the first thirty seconds and is dramatically faster after that.
Photo by Ilya Pavlov on Unsplash
Random-change debugging is seductive because it feels like motion. You're typing, running, doing something. But you're not gathering information — you're rolling dice.
The trap is that it occasionally works. You change a random thing, the bug vanishes, and the lesson your brain learns is "guessing works." So you keep doing it, and the days you lose to it get blamed on the bug being "really hard," not on the method being broken.
Here's the reframe that fixed me: debugging is not about fixing. It's about locating. Once you know exactly where the bug is, the fix is usually obvious. Almost all the time goes into finding it, and guessing is the slowest possible way to find anything. It's the same locate-don't-flail instinct behind cutting a nine-minute build in half by profiling first: measure where the problem is before you touch anything.
If you can't reproduce a bug on demand, you can't fix it — you can only get lucky. So the first job is always to find the exact steps that make it happen every time.
This feels like a delay when you're itching to fix. It isn't. A reliable repro is the foundation; everything after it is fast, and without it you're debugging a ghost. Nail down the inputs, the state, the sequence. Make it happen on command.
You can't fix what you can't reproduce. Half of all "impossible" bugs are just bugs nobody pinned down first.
This is the step that separates the method from the madness. Before touching anything, state a specific, falsifiable guess about the cause.
Not "something's wrong with the data." That's not a hypothesis; it's a shrug. Instead: "I believe the user object is null by the time it reaches the render function." That's a claim you can prove or disprove in one move.
A real hypothesis points at exactly one test. A vague one points at a hundred random changes. The quality of your hypothesis is the quality of your debugging.
Now the powerful part — binary search on your own code.
You have a bug somewhere between point A (input, known good) and point Z (output, known bad). Don't inspect every step from A to Z. Jump to the middle and ask one question: is the data still correct here?
Either way, you just eliminated half the code in one check. Repeat. A problem spanning a thousand lines collapses to the exact spot in about ten checks. This is the same idea as git bisect, applied inside a single run instead of across history — one of the Git habits that quietly save my team hours every week. Binary search is one of the oldest reliable ideas in computing, and references like Google's web.dev lean on the same divide-and-measure discipline for tracking down performance regressions.
Photo by Alexandre Debiève on Unsplash
Keep halving. Each check either confirms or kills your current hypothesis and shrinks the search space. You're not hoping — you're systematically removing places the bug can hide.
The feeling is completely different from guessing. Every single step makes guaranteed progress. You always know more than you did a minute ago. The bug isn't a mystery you're praying to solve; it's an animal you're cornering, and it has fewer and fewer places to run.
When the search space is one line, the bug is there. And by then, you understand the situation so well the fix is usually trivial.
Last week: a page showed the wrong total. Old me would have started tweaking the math randomly.
Instead: I reproduced it (one specific order triggered it every time). I formed a hypothesis ("a discount is being applied twice"). I cut the problem in half — checked the value at the API boundary. Correct there. So the bug was on the front end, and I'd just eliminated the entire back end in one check.
Cut again — checked the value entering the component. Wrong already. So it happened between fetch and component. Cut again. Found it: a currency conversion ran in two places. Total time, maybe eight minutes, most of it calm. The old method would have eaten an afternoon and my mood.
| Guessing | The method |
|---|---|
| Change things, hope | Narrow down where it can't be |
| Feels busy, low info | Every step removes possibilities |
| Time varies wildly | Predictable, minutes |
| Fix without understanding | Understand, then fix easily |
| Stressful | Calm |
The part of this method that changed my work most wasn't technical. It was emotional, and I almost never see anyone talk about it.
Guessing-based debugging is stressful in a specific, corrosive way. You're never sure if you're close. You can't estimate how long it'll take. You might solve it in two minutes or lose a day, and not knowing which is its own kind of dread. That uncertainty leaks out — into your mood, your estimates, your willingness to take on the scary bug at all. I used to feel a small spike of fear when a hard bug landed in my lap, because the honest answer to "how long will this take?" was "I have absolutely no idea."
The method dissolves that fear, because it replaces hope with progress. When every step is guaranteed to eliminate half the search space, you always know you're moving forward. You can even estimate: "this spans a few hundred lines, so call it ten checks, so maybe twenty minutes." The bug stops being a monster of unknown size and becomes a finite thing with a knowable shape. That changes your whole relationship to hard problems.
It also changed how I work with other people. When a teammate is stuck and spiraling, I don't hand them the answer anymore. I ask: "Can you reproduce it reliably? What's your hypothesis? Where could you check to eliminate half the code?" Walking someone through the loop teaches them the method instead of solving their bug for them, and watching the panic drain out of their face as the problem shrinks is genuinely satisfying. The calm is contagious. A method doesn't just find bugs faster — it makes the whole act of debugging feel less like a gamble and more like a craft you're quietly good at, and that feeling is worth as much as the time it saves.
If you want more of these repeatable methods that turn the scary parts of the job into calm, predictable work, it's the kind of thing I keep writing up — try the loop on your next bug before you reach for anything else.
Q: Doesn't reproducing first slow me down? It feels slower for two minutes and saves hours. A reliable repro is the difference between fixing a bug and chasing a rumor. Skipping it is the most common way to lose a day.
Q: What if I can't form a hypothesis? Then you don't understand the system well enough yet — so use the binary search to build understanding. Even without a strong guess, halving the search space teaches you where to look.
Q: Does this work for the really nasty bugs — race conditions, heisenbugs? Yes, though reproducing them is harder. The method's first step — get a reliable repro — is exactly the part that breaks nasty bugs open. Once it reproduces, the rest follows.
Q: How is this different from just using a debugger? A debugger is a tool; this is the strategy for using it. The debugger lets you check the midpoint; the method tells you which midpoint to check and what to do with the answer.
The day I stopped changing things at random and started narrowing things down on purpose, debugging stopped being the scary part of my job.
Debugging isn't about fixing. It's about locating — and you locate by halving the space until the bug has nowhere left to hide. Reproduce, hypothesize, cut in half, repeat. That's the whole thing.
Next time you hit a bug, resist the urge to change one random line. Ask instead: where can I check the value to eliminate half the code? Try it once. I think it changes how you work too. What's the first thing you'll cut in half?
I spent years saving the hardest task for when I 'felt ready.' Doing it first instead quietly fixed my focus, my dread, and my output.

I tracked every distraction for a week and was horrified by what I found. Then I fixed the three that mattered most.

No following, no network, no luck. Just an unglamorous system I ran for eighteen months. Here's exactly what I did.

Comments
Sign in to join the conversation
No comments yet. Be the first to share your thoughts!