Don't Guess
- 4 minutes read - 690 wordsThis is a response to “Don’t Guess” from the excellent “The Best Programmers I Know” by Matthias Endler. My goal is to crystallize my understanding of this trait.
“In the face of ambiguity, refuse the temptation to guess.” –The Zen of Python, Tim Peters
Guessing is defined as “form[ing] an opinion of from little or no evidence.” As a programmer, it’s tempting to guess while debugging. We might say: “Hey, I wonder if this is happening in GamePlanSyncer
? That sure causes a lot of bugs”, while nowhere near the corresponding code.
It’s a trap! What you get is either something that doesn’t work or something that does work, but you don’t know why. And it denies the empowering truth that computers can be understood.
Bad: Guessing
Let’s look at an example.
Imagine we have a Ruby on Rails controller action that creates something called a game_plan
.
def create
game_plan = GamePlan.new(game_plan_params)
if game_plan.save!
GamePlanSyncer.run!(game_plan)
render_success(GamePlanSerializer.new(game_plan))
else
render_error
end
end
Something is going wrong in this code, because a game_plan
is being created and returned, but its parameters differ from the ones we sent.
We read through the code and see a bunch of innocuous logic, plus a call to GamePlanSyncer
. Maybe this call stands out to you. Maybe you’ve heard this class derided in meetings as a bug magnet.
If we’re “forming an opinion of from little or no evidence,” suppose we guess that this sync class is the problem, and comment out its call:
def create
game_plan = GamePlan.new(game_plan_params)
if game_plan.save!
# Disabled!
# GamePlanSyncer.run!(game_plan)
render_success(GamePlanSerializer.new(game_plan))
else
render_error
end
end
We’ll make it more painful by saying that the bug only happens in production, so we must deploy this change, via a ~30-minute CI process, to test our theory. There’s always a time cost, but let’s make it hurt.
Hey, at least we’re trying something! Unfortunately, the thing we tried tends to have two unfortunate outcomes:
- The bug doesn’t go away
- The bug does go away
Let’s look at each.
The bug not going away is likely because we had no foundation for this experiment. While not fixing the bug, we invested time and maybe caused other things to break. And what’s next? There are six more lines of code in this method; do we have one to six more 30-minute experiments ahead?
If the bug does go away, that’s worse! First, it reinforces this approach. It’s like hitting the right key by accident– it feels like progress but teaches you nothing. Second, since we have no idea why it worked, we didn’t learn. Third, it can strengthen a flawed mental model such as: “We need to limit our asynchronous synching.” We probably don’t! But that’s all a success case has to offer.
We all guess sometimes, but it’s important to consider the downsides and try to level up when we can.
Better: Evidenced-Based Predictions
So, if guessing is bad, what should we do? Do the opposite: make an evidenced-based prediction.
Here’s one:
“We’ve read through the controller code; it’s standard. But this sync class we call is huge and untested. More than once it mutates the game plan. I think one of those mutations is wrong. Let’s put a log statement before and after this suspect one. My theory is that X attribute is going to be different in the second log statement, which would tell me that this mutation is wrong.”
It takes years of debugging practice to learn to think like this. Could it be wrong? Absolutely. But it’s binary. And even if it is wrong, we’ve decisively ruled out an idea.
Ironically, the anti-pattern I first tried, disabling random code, and my proposed solution, a targeted log statement in a different file, take about the same amount of time to try, at least initially. They seem like pretty similar approaches. But one is a directed step, and the other is wandering.
To reiterate an argument that I and others have made: Computers Can Be Understood. They’re magical, and yet, they are never, ever, magical. Each part is something that you, with enough time and effort, can understand.
Prove that you believe it, by not guessing!