In a relatively recent blog post, I discussed why I believe that more software developers should write blogs. In that post, I mentioned that I believe that some developers do not write blogs because they feel they have nothing new to add. A blog post I recently read illustrates how even a blog post on "old news" (debugging in this case) can be a useful addition to the blogosphere. In this (my) blog post, I will be referencing that blog post and adding my own thoughts about common effective practices used in debugging code.
Over the years I have been engaged in professional software development, I have observed definite improvement in my ability to debug problems. For me, there is no question that experience is a significant contributor to debugging efficiency. The recent posting on the Turnleaf Design: Ramblings of a Junior Developer blog called Six Steps for Fixing Any Bug does a nice job of succinctly describing key steps in debugging most software problems. This is a quick read and I recommend it for anyone, but especially for relatively less experienced developers. Although none of the steps or concepts discussed in that blog post are new to me, I like how they've been articulated and collected in one location. In my post here, I want to address each one of them and add some of my own thoughts.
Billy Korando, author of the Turnleaf Design blog, summarizes six steps in debugging and provides brief explanations of each.
The Bug is Probably Mine
Under Step #1 ("Take responsibility"), Billy guesses that around 95% of the time a defect is encountered, the error belongs to the developer. If we are talking about the development staff as a whole compared to the products they are using, I'd estimate an even higher percentage. If we are talking about "the other guy (or gal)" on the development team compared to me, then it depends on the person I am comparing to. Indeed, in The Practice of Programming, Kernighan and Pike write, "Beginners have a tendency to blame the compiler, the library, or anything other than their own code."
I have seen a few developers who are always quick to blame the compiler, the JDK or other library, the framework, the COTS product, or anything else besides their own code. These things are software themselves and so do have bugs of their own, but I have found that blaming such products should normally be the last resort. Looking for the bug first in the place it is actually least likely to be is inefficient at best.
If the product you're blaming for the defect is open source, you might look at the code to verify the defect is really there. With closed source software, you're limited to writing as simple demonstrative tests as possible to reproduce the suspected defect. One easy approach is to search that particular product's bug database and search forums and blogs for identification of that defect. However, I would not recommend performing any of these steps until after checking out one's own software because I agree that our own defects outnumber those of the products we use. The percentages are in favor of spending effort analyzing our own code first.
It also depends on the particular product. If I am using Java libraries, I definitely look long and hard at my own code and use (or possible misuse) of those libraries. If I'm using a well-used framework like the Spring Framework, I consider the possibility of a bug in that software only slightly sooner than I would have in the standard libraries. However, if I'm using a little-known tool or using a well-known tool in infrequently used ways, then the chances of the defect being part of the product I am using go up.
Loving the Stack Trace
It is easy to not like stack traces. They are not easy on the eyes and can seem formidable at first glance. However, I think anyone who has spent any significant time maintaining code or fixing defects has come to see their inner beauty. Stack traces are especially welcome to the bug fixer because they are such a tremendous asset in performing step #2 in the previously referenced blog post ("Narrow down where the bug occurs").
In Java, a stack trace is not limited to exception handling. One can always access the stack trace using Thread.getStackTrace().
When a stack trace is not available (either because the language/platform does not support it or because the type of error is such that a stack trace is not readily available or useful), there are other ways to narrow down when the problem occurs. These include logging (step #5 in the referenced blog post) and simply thinking through the way the code is supposed to work.
The Value of Unit Tests
One of the popular facets of Test-Driven Development is the idea of writing a unit test that reproduces the problem and then knowing the problem has been satisfactorily addressed when that unit test no longer fails. A nice side effect of this approach is that it makes for easier regression testing and a suite of such tests help reduce the chance of breaking one or more pieces of functionality while fixing a different piece. Unit tests can also be helpful in isolating cases where there actually is a problem in someone else's code. I have been known to write "unit tests" for third-party software to ensure that they are keeping their contract. They usually are, but not always. Unit tests satisfy what the authors of The Practice of Programming recommend: "Make the bug reproducible."
There are certain classes of problems and defects that seem to require a debugger. I have found this to be especially true in the case of weakly typed languages where silly typos or spelling errors have led to strange results that are difficult to understand and often result in hanging or nonresponsive applications rather than in obvious error messages. For example, I have encountered several errors in my Flex applications that I was only able to resolve via use of the Flex debugger.
Real Developers Ask for Help
It seems that we software developers are a proud lot. It can be difficult for us to admit our own mistakes (part of the reason why it is important to admit that our own code may be the culprit before blaming everything else) and can be difficult to admit we don't know something. However, we can save ourselves and our clients significant time in some cases if we simply are willing to talk out the problem with and ask questions of other software developers.
There are several reasons this is advantageous. First, no developer can have seen and experienced every single problem or defect that might come up. Some developers know languages better than us and some may know the domain or business logic better than us. Second, sometimes we just need a "second pair of eyes." There might be something obvious that 99 times out of 100 we'd have caught ourselves, but did not catch in this case because of fatigue, being too close to the problem, being lost in all the changes we have made, etc.
A third reason to bring in another developer on those particularly hard defects is that sometimes the simple act of explaining the situation, its symptoms, and what we've tried already helps us realize the answer to our own question. I have been on both ends of this. There have been times when I have responded to someone's plea for help only to find them resolving the issue before my eyes as they explain it to me. I have also done the same thing myself, realizing the problem as I take the time and effort to articulate the problem and logically think about my options. The authors of The Practice of Programming specifically recommend "Explain[ing] your code to someone else" for precisely this reason.
Another Perspective on Fixing Bugs
When I blogged that more software developers should write blogs, I specifically stated that one reason for this is that they might point out links that are helpful to them that I was not aware of previously and find helpful. Such was the case here. The blog post Six Steps for Fixing Any Bug references (and is in response to) another useful post: How to fix bugs, step by step. The latter entry recommends other practices that I have found useful in dealing with software defects and debugging such as tracking bugs (reduces chances of forgetting important details), searching for error messages, and taking a break when appropriate (because fatigue can reduce the chances of successfully solving the problem and increase chances of doing further damage).
Chapter 5 ("Debugging") of The Practice of Programming has many great recommendations related to debugging and I have quoted a few of them in this post. The "Sample Content" at http://www.informit.com/store/product.aspx?isbn=020161586X includes sections on debugging.
Debugging is an essential part of software development. At times it can be frustrating, but I have also found it to be exhilarating when I have successfully resolved a particularly nasty bug. Debugging is one of those steps in software development that seem to most directly benefit from experience. Because all of us have different experience, sometimes the best thing we can do is to seek help in resolving a particularly thorny issue. Not only can someone else help us debug the particular problem at hand, but if we're observant we can learn other tips and tricks in debugging to add to our debugging tool set.