Debugging Tip: Learning From Bugs
- 4 minutes read - 764 wordsYou were stuck, and now you aren’t. Congratulations! Before you move on, it’s vital to stop and learn from it. It’s the best way I know to get better and spare your mind for increasingly harder problems.
“What Did I Miss?”
Every time you get unstuck, ask yourself: when I was stuck, what did I miss?
- Was I misreading a message?
- Was I hypnotized by an irrelevant detail? 😵💫
- Did I make a bad assumption and reject information that didn’t fit into my assumption?
“What did I miss?”
If you received help from someone, think back to the last question they asked you before you got back on the path. What was that question? What were the questions before that question– what explanations did your helper rule out? Everybody approaches problems differently, but great problem solvers tend to follow similar mental checklists.
It’s enticing to take the win and move on, but don’t cheat yourself out of asking this valuable question.
A Practical Example
From my recent work: I’m developing a single-page-app and my browser screen is blank. There are many explanations for this problem, but only one is the primary cause. Let’s figure it out!
The page I built relies on data from an API. So first question, are we getting data from the API? I check the Network Tab of Chrome Devtools; no, were are not. Our API is returning 422. Immediately, I know that this is a server-side problem1. Why? A status code of 422 means:
A [422 status code] indicates that the server understands the content type of the request entity, and the syntax of the request entity is correct, but it was unable to process the contained instructions.
I asked for data, the server said my request was valid, but it couldn’t give me what I asked for. It is a server-side issue. This seems like a small victory, but ruling out half my codebase, or more in a multi-tenant architecture, as the source of the issue has turned a big problem into a smaller problem.
What else is in the response?
{
"exception": "undefined method `number' for nil:NilClass",
"from": [
"/app/helpers/orders_helper_decorator.rb:18:in `edit_item_path'",
...
]
}
See that ...
? It’s 600+ lines of noise I’ve omitted. Learning from past bugs
has taught me that with Ruby code like this, the first line of the stack trace
matters a lot.
I jump to that method:
# orders_helper_decorator.rb
def edit_item_path(line_item)
edit_path(
id: line_item.product.slug,
order_number: line_item.order.number
)
end
Remember the error: "exception": "undefined method 'number' for nil:NilClass"
. On the highlighted line below, we’re calling number
on
something Ruby is telling us is nil
. My line_item
does not have an order to
ask its number.
# orders_helper_decorator.rb
def edit_item_path(line_item)
edit_path(
id: line_item.product.slug,
order_number: line_item.order.number
)
end
I smack my forehead because I know that items once always to belonged to an order, but currently it’s an optional association. I now have enough information to describe and start solving the problem.
To summarize:
- We found an error on the frontend! But we quickly deduce that the issue is server-side.
- Where’s the stacktrace pointing? A Ruby method. We go there.
- We reason through the stacktrace, use debuggers, and keep digging
When are you ready to start fixing the bug? When you can say:
“Items once always to belonged to an order, but currently, that relationship is optional. So we can’t rely on having an order number in this code.”
Here comes the important part! Once you figure this out, walk back through the solution and analyze your thinking. Where were you efficient and inefficient? How would a programmer you admire approach this problem? What kind of questions would they ask?
Perhaps in this example, I would have recognized the Ruby stack trace in the Network Tab, read the file and line number, and gone straight there, instead of reading the Rails routes file and finding the responsible controller. Or, instead of trying to reproduce it again, I go right to my frontend error monitoring software because I know it’s set up well and catches errors like these most of the time. Eliminate needless steps.
Wrapping Up
“Today I Learned” blog posts are a fantastic way to turn this reflection into a daily habit. Figure something out, learn enough so that you can explain it, write about it, ship. Josh Branchaud’s TIL is a great example of this practice.
When you get stuck and then unstuck, stop and learn from it.
-
To be precise, the issue may not be caused by server-side code, but the answer is there. The server knows why it rejected my request. ↩︎