Jake Worth

Jake Worth

The Case Against "Try This" Debugging

Published: June 26, 2023 3 min read

  • debugging

I recently wrote about a debugging technique that I recommend: making a prediction. The idea is that before trying an experiment during a debugging session, we should stop and verbalize what we think is about to happen. I received a response that could be paraphrased as: “Isn’t that just debugging?”

If only! In this post, I’d like to define this issue, which I call “try this” debugging.

“Try This” Debugging

What is “try this” debugging?

Imagine we’re debugging as a pair and we’re stuck. The easy answers aren’t working.

Suddenly, I produce an idea. It might be a:

  • Stack Overflow answer
  • GitHub Gist
  • Popular comment on a GitHub issue
  • Slack post from a colleague

I share this with you and say the magic words: “try this.”

Why should we try it? Because it might work. Just try it! What do we have to lose? We’ve all known this desperation. We’ve all “tried this.”

But what is this idea? It’s a Hail Mary pass. And I want us to throw it much later than many programmers do. I want to hear the words “try this” as a signal that we’re not debugging, but rather just throwing things at the wall.

What do I mean? What’s the cost of “trying this?”

The Cost of “Trying This”

“Trying this” is expensive: it’s slow, it robs us of learning, and it produces bugs and bad code.

First, “trying this” is slow. Instead of reasoning from first principles, we’re Googling, reading questions, bad answers, outdated answers, pasting code that ends up being just an example of the bug, etc. It’s like trying to find the name of a song when you also don’t know any of the lyrics. You might find the answer, but slowly.

Second, “trying this” invites hacky code because it blocks us from learning what fixed the bug. Unless you try to understand the solution you found, there’s magic in these solutions. We all know how stuck we were, and nobody’s going to question progress. But sometimes, we should, because we missed a learning opportunity. The next time we encounter that bug, we’re back at zero. Often the solution was a mix of things that didn’t matter and one thing that did, and accepting all the changes adds inscrutable, hacky code to the codebase.

Finally, “trying this” solutions are a gold mine for bugs. There’s a high correlation between the code that broke everything, uncovered via painstaking means, and a commit message like “:P Trying something else!!!”

In pair programming, “try this” often derails good lines of questioning. When shared in the middle of an active debugging session, it has the potential to distract from a genuine solution.

The Solution: Make a Prediction

The solution I’ve found is to shift to proactive by making a prediction. My previous post covers this technique. To add an additional example, imagine a test is failing and we’re stuck trying to understand why. What should we do?

Make a prediction! “The test is failing” is not a prediction. Maybe we read the error message and notice that our test runner can’t find some copy on the page.

expect(getByText("Welcome, Eleanor!")).toBeInTheDocument();
// ❌ FAIL: Unable to find an element with the text: 
//    Welcome, Eleanor!

Eleanor is unwelcomed! What are some predictions we could make? Here are a few:

“I think I’m importing the wrong test helper.” This is testable! We can consult other similar tests that are passing.

“It’s a race condition.” Testable! We can make the test wait, or assert about something else we know should be on the page simultaneous to our message.

“I have a typo on my assertion.” Very testable! We can change our assertion to something we know will fail, like “Welcome, Impossible!”, or print out the entire page in HTML or screenshot form.

“There’s a bug in the test library.” Not quite as testable, but okay! We can verify this with Googling or source diving.

All of these are step toward knowing something. They are much more useful than the pure fact that the test is failing.

Conclusion

Sometimes we have to try things. We’ve all been there. Resilience is important.

But throwing things at a wall isn’t engineering. I want “try this” to be the very last thing we try. We’ve lost the plot. Let’s take a break or go home.

What are your thoughts on this? Let me know!


Join 100+ engineers who subscribe for advice, commentary, and technical deep-dives into the world of software.