Being skilled at debugging can make the difference between wrapping up a project in a couple of hours, or wasting the entire weekend banging your head against the wall while trying to figure out why your program doesn’t work.
In this article we will guide you through the process of debugging – the dos, the don’ts, and a few ideas you may not have considered. Much of this is advice that we already cover in our courses, so our graduates will no doubt recognize some of these tips.
Because debugging is a skill that needs to be cultivated by beginners and experts alike, I have divided this guide into two parts. The first deals with the basics, and should be helpful for beginners. The second part is more advanced, and will hopefully contain information valuable for the more experienced programmer.
This is how the information in this guide will be organized:
DEBUGGING BASICS
DEBUGGING FOR THE ADVANCED
With no further adue then – how do we manage bugs?
DEBUGGING BASICS
1. Interpret error messages
At first, you are going to hate error messages. Trust me though, in time you will come to love them. Your IDE giving you an error message is the equivalent of a videogame putting a directional arrow in a dungeon: it won’t get you out of trouble by itself, but it’s going to point you in the right direction.
Even if the error message itself isn’t particularly clear on what went wrong, you can copy-paste it into Google to start getting answers – or at least, you can use it as a starting point. If your error message reveals that your code is unable to store a certain value for whatever reason, then skip trying to ‘solve the bug’, and ask Google directly how to store that type of value in the conditions you are working with. An error message is a clue, weak or strong. So use it.
2. Backtrack to the source
If the bug does not manifest itself via an error message, then it’s time to get surgical. Usually, a solid 70-90% of the work of debugging is really about finding out where things are going wrong. Sometimes fixing the bug will require you to rethink a chunky part of your logic, but more often the solution will be trivial once you’ve simply figured out where to look.
Finding the bug is a two-stage process. Firstly, look for the symptom: if you’re getting an unexpected or unwanted output, then go directly to the line of code responsible for generating that output; if your system crashes, activate your debugger, and let it run until the crash grinds it to a halt at the line of code where this is happening.
Right, you’ve found the point where things get critical – now look for the cause. The part of the code where the symptom occurs is not necessarily where the bug itself is. The Stack Trace in your debugger will let you investigate the specific history that led to the error. Examine that history, starting from either the most recent operation or the one that looks most pertinent to your error. That’s grind, true, but it’s normally enough to unearth the true origin of the problem, and from there, figure out the fix.
3. Get help
In some fields of work (and life), getting help should be the first thing to do. When debugging, it should always be the last. It is possible to turn to Stack Overflow or support forums for help, or, when the going really gets tough, even to the vendor of your operating system.
But the condition for getting help is that you must be able to describe what your problem is accurately, clearly and in depth. If all you can bring up is ‘my welcome animation on the homepage isn’t running’, then rest assured that nobody is going to be able (much less willing) to solve the problem for you.
Do your best to fix the bug by yourself, and use everything you learn from that process to come forward with a precise, step-by-step report of what is going wrong and where. Only then will you start getting answers.
4. Talk it out
Asking for help should come after your personal research – but that doesn’t mean the research itself must be conducted in isolation. Get a colleague to sit down with you, and explain to them what the problem is and how you’re trying to solve it. Even if they have no useful suggestions, simply being able to step out of your mind for a second can do wonders in terms of seeing the issue with fresh eyes. If a colleague is not available, talk to a non-developer friend. Or, get hold of your good old rubber duck. Getting your work done before asking for help is something different than getting all of your work done alone.
DEBUGGING FOR THE ADVANCED
The above section covered debugging tips for the beginner. We will now look at options that are a bit more sophisticated – and also a lot more counter-intuitive! Some of these ideas may well look odd at first, but they all have a method to their madness – stick with us and we’ll explain.
1. Look for coincidences
When and where is your bug manifesting itself? Is it appearing exactly every 49 minutes, and if so, what other parts of your program run on a similar cycle? Or is it happening only when working on specific devices, and in that case, what is it that differentiates these devices?
When looking for coincidences, you should really think outside the box. If you can’t find the bug no matter how hard you look in your code, then odds are, the bug may not be in your code at all. One of our instructors once told me of a boss who complained that blue-screen errors would appear when he worked on his account, but only when he was inside his office. It turned out that when adjusting things in that space he had placed the computer’s processor next to the heater, so it would overheat within the half-hour of being used.
No matter how odd the coincidence may appear, look into it. If your program malfunctions every time your mother calls you, it’s probably time to find out what’s up with mum.
2. Check the hardware
This is related but not reducible to the above point, as trouble with your hardware can result in glitches in your system which can easily be mistaken for bugs. Unfortunately, there won’t be always be a telling “coincidence” to reveal these for you.
The way to test for hardware malfunctions is not entirely dissimilar to the way to test software, except of course it’s done physically. Run the program on a different computer, on a different hard drive, on a different power outlet, one at a time. Test everything this way down to the computer’s cables. Sometimes the bug vanishes when one of these components is replaced. Other times it doesn’t, in which case, at the very least you’ll know for certain the problem is in the software.
3. Ignore the error message
By and large, as I argued in the previous section, error messages are a blessing that will point you towards a solution. For some of the more complex bugs, however, they may be misleading – and there may well be more than one such messages resulting from the same bug. While I would not recommend this as a starting point, in some cases it may be advisable to set the error messages aside and approach your code from a different perspective.
This holds true even when there isn’t an error message but only an error. Some of the most obvious clues offered by your bug may be dead ends. Experiment with pushing them out of your focus, and thinking of ways to approach your problem that does not involve them.
4. Do not go the extra mile
You may be determined to get the bug ‘out of the way’, and so you’ll stay in the office as long as you have to until that’s done.
That is a surefire way to not solve the problem.
The longer you overwork, the more stressed and tired you get, and the more your productivity falls, along with your ability to find and understand bugs. You are also exposing yourself to burnout.
Instead, take a break. Or at least work on something different for a while. Get a good night’s sleep. Refreshing your mind will always be much more productive than overheating it, and this doesn’t just apply to debugging.
5. Scrap your achievements
Or at least consider scrapping them. One of the debugging methods that is most guaranteed to give results – if undeniably the most painful – is to simply erase the code and start again.
This leads to another important discussion which we don’t really have the space to appropriately delve into here, namely version control. It goes without saying that you should be saving updated versions of your files on GitHub all the time, so if something goes wrong, you can delete only the parts you developed subsequently to the appearance of the error. This helps with the ‘economy’ of debugging, but don’t lose sight of the general principle – that your efforts to preserve work already done may involve more actual work than just redoing it from scratch.
Of course, it is extremely hard to make an assessment of when an attempt to resolve a bug will take longer than rewriting the entire method that was causing it (or at least, it’s hard to do so in time). But when that does happen, it’s often because the developer never stopped to think “I’ve spent 3 hours trying to get rid of a bug in a piece of code that took me 1 hour to write – maybe I should just start again?”
Coding is about using your head, not about banging it against the wall. If the worse comes to the worst, then it’s time to make a difficult decision and, if necessary, kill your darlings.
And with that, we have reached the end of our guide on debugging! We hope it will be useful to you, no matter what level of coding you’re at. And if you have no level of coding whatsoever and don’t know anything about the art of programming, well – remember that you can learn.