
You never really learn to swear until you start to code.
“Any given program, when running, needs debugging. Any debugged program is obsolete.”
Programs have bugs. It’s just part of being human - we make mistakes. A program is only as good as its authors. Yet surprisingly enough, when you take programming classes, they rarely spend time teaching you how to efficiently debug code.
Yet in the real world, once you start working on actual applications, you will spend most of your time enhancing, testing, and DEBUGGING. Every step of the way as you write code, you will be debugging it when it does things you don’t expect it to do. Or when users do things you didn’t expect them to do. Or when you write an enhancement and it inexplicably breaks existing code.
You get the picture.
The last thing you want to do is waste even more time debugging than you already spend doing it.
I’ve learned a lot of things about debugging code and writing code that is less likely to have bugs in the first place. Here are my favorites that I’d like to share with you, in no particular order.
1. Leave your assumptions at the door
“The code that is hardest to debug is the code that you know cannot possibly be wrong.”
If you are tracking a bug down, don’t make assumptions about what is causing it. You will only see what you look for. You can spend hours overlooking the real problem. Pretend it’s someone else’s code if you have to. Just drop your assumptions about what values are in your variables, what objects can’t possibly be null, or anything else.
2. Breakpoints are your friends
After you’ve ditched those assumptions, make breakpoints all along your code and narrow down exactly where the bug is. There’s no point in tracing through a bunch of code that is working just fine, now is there?
3. Watches are also your friends
Once you’ve narrowed down your problem to one section of your code, set up your watches. Check the value of every one of your variables as you step through your breakpoints, even if you think you know what it should be (see #1).
One of the most common things I see is people assuming a loop is even iterating to begin with when in fact it never runs because the condition isn’t met. A collection is empty that they were sure had to have values in it, or a counter wasn’t set properly. This is especially frequent in languages that let you shoot yourself in the foot, like VB running without Option Strict on, Perl, and Javascript. Those will all just go ahead and create an empty variable if you make a simple typo!
4. If all else fails, print a trace
Not everyone has the luxury of an IDE to set breakpoints and watches with. So log print or echo statements! Alert things out if you’re using Javascript. Stick enough print statements in there and you can trace an awful lot of code. It’s better to spend some time typing out a metric crapload of echo, print, or console writelines than to spend days banging your head on your desk.
Those who have had to debug an SQL stored procedure or classic ASP code will understand.
5. Write small chunks at a time and test them independently
The more code you have that isn’t tested, the more you have to trace and watch to debug. Write your code in small, manageable chunks and test as you go. Write it so that you can easily take out a chunk and stick it in a little test program - most languages these days support methods and functions - modularized code. Take advantage of it.
Don’t write an entire object with all its methods and properties, and a consuming application, without testing a thing. That’s just asking for trouble. I have a ton of tiny little applications just for testing chunks of code. I write a chunk, test it, then integrate it into the whole. I can’t tell you how much time I’ve saved doing that.
6. Don’t throw away your exception stack trace
Some languages have a certain syntax for exception handling that will create a new exception without retaining the original exception. Be careful that you don’t accidentally do that!
And for your sanity’s sake, don’t catch an exception and do nothing with it. It is not worth the minor saving in execution time or resources that it takes to do proper exception handling.
You can spend hours (even days) trying to trace an exception source when had you just popped out the original stack trace, you’d have seen the problem immediately. This is especially common when you’re enhancing an existing application and it breaks something in the original code base.
So those are my top suggestions. Debugging lessons earned the hard way. Please share your hard-earned lessons in the comments!