Having gone to university and having written code for quite some time it was something of a shock when, at my first job, my manager told me that I needed to learn how to properly debug code. As it turns out though my approach, at the time, of jumping in where I though the problem lies wasn’t actually the right way to go about it. Such an approach rarely works unless it’s a piece of code you’ve been working recently, and the layout is still fresh in your head. Here are the pointers that were given to me at that time that I still find useful today.
- Make sure you’ve reproduced the problem exactly. Let’s say you’re working from a ticket that was raised, the steps are a bit ambiguous but you think you know where the problem is so you add some breakpoints to your code and start debugging. Perhaps you will see the same problem? What if you actually find a separate issue to fix? Imagine what will happen when you mark that ticket as closed and the creator of the issue visits it again, they’ll just reopen it!
- Good reproduction steps help everybody. Sometimes the issue will already have these, in which case thank the person that raised it, even if it’s yourself, because that person has already got you part way there. Sometimes though issues are harder to track down but it’s important that when you do find the problem you document it, that way others can reproduce it.
- Revisit those steps and make them simpler! “If I click this button 46 times, stand on my head, wear an onion on my belt and look from side to side, when I press this second button the form doesn’t get submitted”. Ok so that’s not realistic, no one wears onions on their belts, but the point here is that when the test steps are long, complicated, and easy to get wrong you’re going to have a harder time debugging the issue. At this point you’re probably starting to get a feel for where the problem lies so you can use some educated guesses and a little bit of trial and error to see if you can reduce the test steps down. Don’t forget point one though, don’t go finding other issues and confusing it for your original one!
- Start with the entry points to your application. At the very least you know how your application starts, what gets it running, what triggers the action that’s causing you problems. Be it a REST API or some bootstrap code, this is going to be your starting point because you want to follow this from start to end. It might sound tedious but often it’s the best way to work out where problems lie.
- Let it run through! Often when you’re running through, you might spot what you think is the issue, at that point the temptation to stop debugging and start fixing can be overwhelming but don’t! Let the method that you’re currently debugging finish, let the rest call send back it’s response, let the database update go through! Why? Often I find that if I stop debugging and start fixing that the code I’m looking at is working as intended and I just misunderstood it given the context. Make a note of where you see potential pitfalls and keep stepping through.
- Always step in, but don’t go mad. Always step in to the calls that are being made so that you can guarantee that you don’t miss anything but don’t go too far. If you find yourself going through a 3rd part library then you need to start stepping out, these libraries are often well tested and widely used which makes them less likely to contains bugs. Having said that, even the most tested code can contain a few edge cases so be mindful and note any suspicions but try to rule out the obvious first.
- Always loop over your findings. Let’s say you’ve found where you think the problem lies. Take a moment to reread everything you’ve written so far, does this look like it can cause the issue described? Spending some time reviewing your findings helps prevent you from making unrelated or unnecessary changes. I often find talking to someone about it and explaining the problem again helps me gain a better understanding perhaps helping me find a logical fix.
- Retest, retest, retest. Once the fix is in place go back to those clear reproduction steps that you wrote previously and see if you can see the same issue again, if possible write a unit or integration test to guard against this issue reoccurring in the future. Doing this guarantees that you’ve not missed anything or worse introduced more bugs.
I know the list looks quite involved but if you follow these points every time you fix investigate an issue you find yourself doing them without realising and as your knowledge of the code base grows you may find the process getting quicker and quicker. It does get easier over time!