This past week, I was pairing with my team member, Sasha, to fix a particularly gnarly bug. We couldn’t figure out why errors from our API weren’t being displayed in the frontend of the application. It took us a couple of hours, and I started thinking about how we debug and what strategies we can use to speed up our debugging time.
I’ve heard that developers spend anywhere between 50% and 75% of their coding time debugging. Becoming a faster debugger would free up a lot of time for noodling on more challenging problems. Here are my 6 strategies for speeding up your debugging time.
1. Don’t start with the goal of fixing the bug
The best debuggers I’ve worked with begin with a goal of identifying where a bug is happening, not fixing what's happenning. From there, you can work to identify the root cause. The last thing you do is change code and fix the bug.
When you jump into your debugging session with tunnel-vision of fixing the bug, you may be jumping into the middle of the problem. As you move forward, you can get lost and not understand what’s happening in the code. You may need to back-track but how far? There’s no way to know.
When you know your code well, starting from the beginning every time can sometimes feel redundant. But following the same debugging process every time lets you practice your workflow and get good at it. Soon you’ll find yourself moving through the steps quicker and quicker until it almost appears as if you are starting from the fix.
2. Understand the data flow
Sometimes when you start debugging an issue, you might not know where to look. And that’s ok! No one can hold all the code they or someone else has written in their head. Taking a little bit of time when you get started to understand and re-familiarize yourself with the data flow can immensely speed up your process later. You won’t need to spend as much time back-tracking to investigate where data is coming from because you already have a mental model of the workflow.
The best place to start is from the user interaction and trace the data through to the end of the flow. For example, when I was debugging with Sasha, we started looking at what happened to the data once the user submitted the form and traced its path to the database. Once you have a base understanding of how data moves around this part of the system, you’ll be able to more quickly identify potential problem areas.
3. Isolate the problem area
Now that you understand the basic flow of data through this part of your system, you want to identify where exactly the issue is occurring. Is this bug on the server or the client? Which class or component is it happening in? Can we get more specific and find the method or function that’s failing? When I was debugging with Sasha, we were able to isolate the problem within one higher-order component’s
handleCompleted function which is called when the API returns a response to the client.
The narrower the scope of the code you’re investigating, the faster you’ll be able to figure out what to change. Whether you’re a console.logger or love your interactive debugger, stepping through 3 lines of code is much easier (and quicker!) than 300 lines of code.
4. Check your inputs
Once you know exactly where a bug is occurring, you can start identifying the root cause of the issue. I find that a majority of my bugs involve the wrong format of data being input to my code.
To catch this quickly while debugging, always check your inputs and outputs. Is the data being returned from the server exactly what I expect it to be? Is the data received in this callback what I expect? What about the parameters of my functions? Are the values correct? Are the data types correct? In my debugging session with Sasha, we discovered that null was being passed to the child component instead of the actual error data.
Other common input data issues include passing arrays when a single object is expected or strings from HTML input values that should've been parsed to an integer first. By always checking what is coming into a method, function, or controller action and what is being returned from it, you can quickly identify data issues to help with your bug fix. This leads nicely into my next tip.
5. Validate your assumptions
Never assume the code is doing what you expect. Computers are literal creatures and they don’t do what you “meant” but exactly what you tell them. While debugging, you should always validate your assumptions.
At first, this can feel slow and tedious but it drastically reduces the number of times you step through the same patch of code. If you only need to step through your code once checking your assumptions along the way instead of 5 times looking at a different section each time to find a bug, you’ll see drastic improvements in your debugging time.
What assumptions should you check?
- Check the format and type of input and output data
- Step into called functions instead of over them to see what they’re doing under the hood
- Check out the network activity to make sure your requests and responses are formatted as expected
- Put a debugger breakpoint in both the client and the server to verify that data is being sent to the location you expect (either to the correct controller action on the server or handled by the correct callback on the client)
Anytime you feel yourself “just knowing” what the code is doing, you should find a way to confirm that what you think is happening actually is.
6. Read the docs
And finally, make sure to read the documentation. This includes documentation in your codebase as the documentation for libraries or packages that you are leveraging in your code. I can’t tell you how many bugs I've coded because I assumed how to use a function or library and didn’t read about its interface. I passed the wrong parameters, configured the library incorrectly, or missed a well-documented gotcha which the documentation would’ve illuminated in ~5 minutes.
If you step through your code and all of your assumptions are correct, you’ve inspected every step of the data flow, and you still can’t identify the bug, go look for documentation. Even if it doesn’t have the answer (a lot of times it does, I promise), it can help you think about the problem differently or give you an idea of another place to look.
It may feel counterintuitive but the key to faster debugging is to slow down. Going slow once is almost always faster than going fast 5 times. It’s the perpetual tortoise vs hare debate. So when you’re debugging, try to answer these questions in order
- Where is the bug happening?
- What is the issue?
- Why is it happening, what is the root cause?
- How do I fix it?
The more practice you get understanding the code, isolating the problem, and validating your assumptions, the faster your debugging process will get. Do you have other strategies for becoming a more efficient debugger? I’d love to know! Tweet at me @mercedescodes