Book review: On battle scars and debugging
- Effective Debugging: 66 Specific Ways to Debug Software and Systems (Diomidis Spinellis, Addison-Wesley, 256 pp., 2016)
- Software Exorcism: A Handbook for Debugging and Optimizing Legacy Code (Bill Blunden, Apress, 351 pp., 2003)
- Debugging Applications: The bugslayer's guide to finding and fixing coding errors in Microsoft Windows-based applications (John Robbins, Microsoft Press, 466 pp., 2000)
Debugging is one of those skills that programmers absorb over time. It doesn't seem to be taught so much as learned through experience from those who have "been there, done that". These books take a stab at providing concrete debugging information. They have been there, they have done it, and they want to tell you about it.
Effective Debugging is the most concise and useful of the three. Even if it had been written 20 years ago it would still be the better book. Spinellis doesn't wax poetic or philosophic. He's on point for the whole text. Each chapter describes a tool, technique, or approach that can be added to one's debugging arsenal. The examples tend to be short and easy to digest, although being familiar with the tools used to make the point is helpful. Some of the chapters veer into trite and pedantic territory, but thankfully don't stay there for too long. I'd wager this book will still be marginally useful in 20 years.
So how ageless can debugging information be? I said that Spinellis' book could be "marginally" useful in the future. Is that a knock against it? Perhaps, but that's not the intent. The point is that although Spinellis uses general sounding chapter titles, the prose inevitably digs into details of a particular language, tool, or environment. The programming milieu changes over time, so the utility of the details will diminish. If there's enough similarity between what is described in the text and what the reader is using, there is a better chance of relating the text to an actual situation. This means that tool choice and problem description in a debugging book is a tricky business if you want your book to be more than just a run-of-the-mill experience report. Thinking something like that could still be helpful in 20 years is an attempt at a complement.
Spinellis has the advantage of being a recent book. On the other hand, both Software Exorcism and Debugging Applications are books that are at least 15 years old and ones that you probably won't find terribly helpful today. What was interesting was having to ground myself in a context that is sympathetic to the material. That is, I had to consider the zeitgiest of the time.
I should put some of my biases up front. I've never developed on or for Windows in any meaningful capacity. I know the development environment in name only. I have a cursory knowledge of Windows as a system and I've not made any effort to master Windows.
With that in mind, I admit that I did not read all of Robbins' book (I read the first quarter and skimmed the rest). The vast majority of it details the use and navigation of Windows development tools of the mid to late 90s running on Windows 95, 98, 2000, and NT 4.0. These are all operating systems that are barely in use today. The (copious) code examples are mostly pre-2000 C++, which is a minefield of incompatibilities. To give a flavour of its content, there is nearly 100 pages devoted to describing how to use the C++ and Visual Basic debuggers. There is a 10 page code listing (!) of a Visual Basic unit testing tool.
Debugging Applications is not a timeless book. It is very much grounded in the Windows development environment of its day. It's the kind of book that textbook publishers love because it demands new versions every year to stay relevant.
Software Exorcism is not timeless either, but tries to be more general in its advice and is more pertinent. It is also deeply cynical. Blunden writes as if he was wronged or witnessed workplace treachery on a regular basis. There is a fair bit of advice on keeping a paper trail so that when you are blamed for something you can show that the one pointing the finger is actually the one at fault. There are also sections on countermeasures to some of the remedies presented so that you can recognize ways they could be defeated. The code samples are C/C++, geared toward Windows, and generally not interesting. The advice is nothing that hadn't been said at the time but I can't deny that I wanted to read it. I was spurred on by the desire to understand the mindset of the author more than the content.
Curiously, both books spend an entire chapter on describing how debuggers work by actually implementing one using the Windows APIs1. It should not surprise you to know that neither of these exercises produced a debugger that the reader would want to use for actual debugging, although Robbins comes close to making something that skirts the periphery of utility. Furthermore, they don't even tackle the interesting parts of a debugger, like walking the stack or dealing with the symbol table (that is done, if at all, using the partially documented Windows APIs). They do demonstrate breakpoints and single-stepping, but explaining that certainly doesn't require a full application, especially since the Windows APIs do most of the work.
Although Robbins dives deep on details and Blunden doesn't exactly break new ground, both supply some perspective on the act of debugging. Their advice should be framed within the time period in which they were written. From that perspective, the advice is not bad. It can even be compelling at times. They prescribe similar practices albeit from different perspectives.
Robbins takes a managerial view. He advises that one use a bug tracker and version control, set aside time for debug builds, get a build in the hands of QA as soon as possible, and build and test frequently. Bear in mind this is before "Agile" consultants started burrowing into organizations. It's clear the tenents of agile development are present here. Although the specifics don't fit with today's practices, the spirit certainly does. The uncharitable view to take whilst reading about planning is to assume some kind of horrific waterfall model, or perhaps strict adherance to a Gantt chart. But there is no mention of what planning means in Robbins' descriptions. It's a good rhetorical device that elevates it above its tedious explanations of all things Windows.
That said, the advice on defensive programming is banal: assert, trace, and comment. What's striking is how much effort is made to supply techniques to enhance (read: work around) the limitations of the development environment at the time. For example, there is a large portion of text devoted to make assertions better in Visual Studio.
Opposite to this is Blunden. Aside from the sharp cynicism, Blunden is talking to the reader as if they, like him, are in the faction of maintenance developers. Again, some core practices of today are present: unit testing, incremental refinement, and the expectation of change. This was written at a time when web applications were starting to get noticed. Since his is a book on legacy code – based on years of experience – it's noteworthy that the principles that some seem to think originated with the web were certainly known before.
His advice on defensive programming is what you would expect: low coupling with high cohesion, verify your inputs, document wisely, avoid globals. It's nothing a sane person would dispute. It's the tone, not the content, that gives this book its flavour.
As alluded to earlier, there is a lot of code padding in Robbins' and Blunden's work. Is all that code necessary?
In their defense, consider a popular way of demonstrating tools today: screencasts/videos. To emulate an interactive session with just text can take a lot of lines. Screencasts are a popular way of demoing tool use because they nicely compress the information. And there are certainly times where showing code greatly helps to demonstrate a point. When Debugging Applications and Software Exorcism were written, good frameworks for logging and unit testing were not readily known or available, so it is understandable why they would include code demonstrating them. One must grant some leniency in criticism of literary code bloat.
Nevertheless, it's still code bloat2. A code example that stretches over a couple of pages is pushing the reader's patience; taking 10 pages is tossing it onto the crags. I'm not sure if the point to these lodes of code was to have the reader study or transcribe it. Either way, it's a losing proposition3.
Spinellis avoids this because he has the luxury of being able to use links and a Github repository — the modern day equivalent of shipping a CD with your book, with the added task of maintenance. It may reduce the size of the book, but I ignored it just the same. His examples are small enough that transcribing them is arguably more instructional.
Books on specific debugging strategies aim to impart their experience so that you can stay sane and survive. They're documentation for successfully running a gauntlet. They pull the reader in through sympathy and keep them there via empathy. Their strength is that connection, but their weakness is their information. The gauntlets they have run are not the same ones you will run. It's your job to extract the lessons from the experiences.
More accurately, Blunden implements two debuggers (one is for DOS, the other Windows) and an interpreter. Robbins implements two as well, a "simple" one and "real" one. Coincidentally, this happens in chapter 4 of both books.
And, in my humble opinion, badly formatted code bloat.
Robbins' book was borderline egregious since it came with a CD that contained all the code.