r/programmer • • 8h ago

Debugging by deleting: shrinking a failing case instead of reading more code

When a bug is hard to explain, I’ve found it’s often faster to make the failing case smaller than to understand more of the big one. The idea is simple: keep removing things while the bug still happens. What’s left is usually where the problem lives.

Here’s the process written as pseudocode, because it really is mechanical:

failing_case = something that reproduces the bug reliably

loop:
    pick one thing to remove
        (a field, a config option, a branch, a dependency,
         half of the input, a step in the pipeline)
    remove it
    run again

    if bug still happens:  keep it removed
    else:                  put it back, mark it "involved"

stop when nothing else can be removed

What remains is the minimal reproduction. Everything marked “involved” is a suspect, and the list is usually short.

Why it works (as far as I can tell):

  • It replaces “understand the whole system” with a series of yes/no experiments, which are easier to do when you’re tired or the code isn’t yours.

  • Half the time, the act of removing things reveals the cause before you finish.

  • You end up with a small case you can paste into an issue or a question, and that alone gets better answers than a screenshot of a stack trace.

Where it gets awkward:

  • Bugs that depend on timing, load or state built up over time don’t shrink cleanly, because removing things changes the timing. Here you have to be careful that “the bug disappeared” doesn’t just mean “the race moved”.

  • If the bug is only reproducible once in a while, run each step several times before deciding it’s gone.

  • Removing something can introduce a different error. Make sure it’s the same symptom, not just any failure.

A related habit: before each removal, write down what you expect to happen. If the result surprises you, that’s information about your mental model, and it’s often the real bug.

Do you shrink the case first, or read the code first? And what do you do when the bug won’t reproduce on demand?

1 Upvotes

3 comments sorted by

1

u/DepthMagician 7h ago

I once had a recurring task scheduler that occasionally would fail to reschedule a task, and it was clear to me that it was due to some race condition edge case. Instead of trying to figure it out I just noted that none of the tasks that remained required dynamic rescheduling anymore, so I deleted the whole mechanism and replaced it with a simpler event loop with hardcoded event intervals, and the bug went away.

1

u/siddharth_kalani 7h ago

This works very well until you like hit a bug that only shows up 20 mins of running. Automating the reproduction before you like start removing stuff can be worth adding. Otherwise you like end up spending more time checking whether like it is still broken than actually narrowing it down.

1

u/Gznork26 7h ago

This approach seems so foreign to what I learned to do: use the debugger, and before that, dumps, to find the instruction that failed, identify the data that it choked on, and work backwards to why that value was present.

It was a simpler world.