Ok, I should put some caveats in here. The last time I liked or hated something I ended up as a foe of well-designed data structures. No doubt I'll be the champion of buggy code next.
No, I like a particular aspect of debugging. Call it forensic programming. Most bugs are not very interesting. They stem from simple oversight (“Oh, I didn't think of that...”), the fix is usually fairly obvious, and there is no lesson to learn other than to be thorough. The interesting bugs are the ones that stop you in your tracks, make you scratch your head, and cause you to think “What the ... ?”
To find and fix one of the interesting bugs is to attempt to re-create the state of mind of the author of the program. No one writes a program with the intent of failing. The program is designed to succeed and the author clearly thought he was succeeding. To understand how the program works involves seeing the author's plan for success. You know the plan ultimately fails, but rather than designing a new and different plan for success, you have to pretend and persuade yourself that a reasonable, rational, and fairly intelligent person could plausibly think the existing code was more than sufficient for the problem at hand. At the same time, however, you have to consider numerous failure modes that are serious enough to cause the observed failure, yet subtle enough to not fail on the observed successes. There are a lot of ways for a program to simply not work. There are far fewer ways for a program to mostly work. Presumably, the program worked well enough to fool the author.
Debugging is narrative. You are telling a story. The theme is classic: man versus nature. Our protagonist is the intrepid engineer. There are mountains of data, fast-moving streams of input and output, abstraction barriers that conceal too much or too little, and the constant pressure of time for him to contend with. The antagonist is the devil hiding in the details. He cannot win a direct confrontation. His goal is more subtle. He is Beaudelaire's devil attempting to convince the world he doesn't exist. Act I finishes with the apparent triumph of the programmer. He has not simply reached his destination, he has routed the devil from all his hiding places. But the audience knows there is something wrong.
Act II opens. We know the engineers intentions and his designs are plain to us. We turn to the devil. The devil's plan is to precisely engineer the observed failure mode without affecting the outcome of the observed successes. The devil is not an engineer. He writes no code of his own. He doesn't seem evil. He's supportive! He's flattering! What a fantastic design! Your code is near perfect --- no, it is perfect. Not that there aren't limits, of course, there are always limits. But don't worry, you aren't near them. And special cases, too. But those are rare. Negligable, almost.
But the drama lies in the audience seeing the devil for what he is, all the while knowing that the engineer is completely fooled. As with any good drama, the engineer and the devil are opponents worthy of each other. The design is in fact admirable, and the devil has his job cut out for him. Or perhaps it is good comedy where the simultaneous presence of not one, not two, but all three stooges acting in concert is necessary to cause an increasingly improbably chain of events to lead to disaster.
Act III is the denoument. The bug is revealed and Poirot walks us through the deductive process of his little grey cells. It can end here with a simple, obvious bug fix or workaround, or we can leave the possibility open for a sequel where we need to completely redesign the original work.
There's a nice, fat, easy target for the internetsia to skewer. Have at it.