Debugging 101

On Monday we had a situation where one of our websites was not performing properly.  I was not the programmer who wrote the code, nor was I debugging, I was just giving my knowledge on the subject and trying to help out.  If it were up to us programmers, we’d take all the time in the world to figure it out, to us, that is the right thing. However, being that we’re taking up precious time, we have to think about what’s best for the client, to continue to charge them for debugging work, or to provide a compromise that does not necessarily hinder functionality.  The issue we had, as I’m told, stemmed from a cfhttp post to a remote page that was timing out during the post, so the information that was returning wasn’t being processed correctly, and thus throwing errors.  So our compromise was effective: rather than have the page post silently to another page, it’s actually going to the page.  This is not something I would have chosen as a solution, or even thought about, because to me it’s backwards functionality, but in reality it is a better solution than spending more hours on trying to fix it.

So the question then is, how do we as programmers figure out code problems? And once we find the problem, how do we fix it? We do not have some magical wand that mysteriously fixes all code problems, we have to go through a series of steps to find out what’s going wrong.  Our first tool is the compiler, or in some of the cases of web programming, the server itself.  This will give us immediate big ugly error as feedback, including the offending line of code, if we’ve done something really wrong like using an incorrect tag or misplaced punctuation.  Once that first layer of debugging is complete, and there are no longer reported errors, then it starts to get hazy.  The problem becomes a logic problem, it’s in the code, but the way it’s coded is not correct.  This is where we begin pouring over the code itself.

When we have logic errors, it’s hard to figure out just what’s happening.  Generally these will show no errors in the logs and we can’t recreate the situation at our desk.  When we’re finding that users are still having trouble, whether it’s having the page redirect, or showing no output at all, we have to start making our best guesses at what might be the issue, and then have to go on from there, and going on usually means some tedious code reading. We have to look over the code and go through step by step to make sure what we’re doing is working in theory.  Sometimes you can spot a logic error just by going through and following the path that code takes, sometimes not.  If you can’t find it just by looking at the code, that’s when the next phase of debugging has to come in, showing what’s happening while the program is running.  This might mean showing data on the screen or in a log if there is no screen output.  It may also mean making use of further tools to actually take a look at the services running, but that’s another story entirely.  Most of what we know we can find through ‘breakpoints’ and output of variables at points within the program.

Getting feedback here is another thing entirely, we have to actually program feedback in and test.  It is especially difficult where we are using functions that have no screen output, so we sometimes have to rely on logs or other tricks of the trade.  My favorite tools for this in coldfusion are the tags <cfdump> <cflog> and <cftrace> but usually we can just suffice with outputting to the screen the variables we think should have values.  In all of these cases we can find out whether or not certain parts of our code are being run (such as if statements going into the correct branch) by placing strategic output points.  For example, sometimes our code may be skipping over a piece of ‘if’ logic.  What I will do is output the test before we get to the logic, and then output again once we should have been in the logic.  Sometimes a variable may be off or not be set properly, so we can find that out.

In the end, debugging is probably the most time we spend on our code.  The old saying that we learn best from our mistakes is definitely true here.  It doesn’t take long to punch some keys and get code looking like it should work, especially when there’s a good design plan behind it.  But sometimes when we think everything is perfect, the results are not, so we have to go back through and find out when things went squirrely.   Our debugging tools help us with that every minute of every day that we are coding.