We’re Off to See the Wizard

A number of people have asked me about the origins of the Small colour wizard wizard who adorns my home page, and who is reproduced here in slightly smaller form. Those who have seen Abelson & Sussman’s textbook Structure and Interpretation of Computer Programs may recognize him from the line drawing that appears on the front cover of that text. The back cover of the Second Edition of SICP says only:

Cover images adapted from Le Moyen Age et la Renaissance, Paris, 1848-1851

Curious to see the original drawing, I prowled around online and in the Dartmouth College libraries, but was unable to find any reference to this book. The correct solution would probably have been to consult a reference librarian, and I suppose I would have had an easier time if I could read French, but I didn’t want to make a big production of it, and even with the able assistance of the Babelfish I was unable to make much headway. Maybe next year when we’re at Harvard, I’ll be able to find it in the collection there.

Anyway, the wizard on my home page is the result of a little amateur hackery with Photoshop. I took the front cover image from the online edition of SICP (linked above), and cropped out the male wizard. After converting the image down to greyscale, I promulgated some shameful jiggery-pokery involving histogram equalization and the Magic Eraser tool to get rid of the background, and inverted the image about the vertical axis so he would face the other direction. I’m not sure why, I just think he looks better that way.

Of course, the astute reader will have noticed that Mr. Wizard was originally a black-and-white line drawing. Greyscale small wizard I tried several strategies to add colour, before I finally settled on this one: First, I selected each separate region of the image — such as the skin, the dividers, the hat, the boots, the cloak, etc. — using the polygonal lasso tool, and I saved the selections as separate channels so I could reload them later to prevent myself from colouring outside the lines. Wizard regions The diagram to the left shows what some of the selected regions looked like. To add colour, I created a new layer on top of the black-and-white original, and used a 56% translucent paintbrush in “soft light” mode to colour in each of the regions. I chose the colours by a completely unscientific process of trial-and-error, and I didn’t bother to write them down. Even if I had, it wouldn’t have mattered, since I did a lot of touchup with the brush to darken certain places, and smooth out patchy areas. You can’t write down how you waggled the mouse (yet). By adjusting the transparency of the layer where I did the colouring, and the original layer containing the line drawing, I was able to get a reasonable illusion that the colour belongs there. The first time I tried doing this, he came out kind of blotchy and fake looking. Now, at least he’s less so.

Then, I cropped out the Eval/Apply yin-yang thing, and replaced it with a mishmash of simple coloured circles with varying degrees of transparency, drop shadow, and Gaussian blur, intended to simulate some kind of crystal ball or other such wizardly apparatus. Again, I wasn’t very scientific about it, I just mucked around until it looked good to me. I briefly considered turning the whole thing into a goblet of wine, but gave it up as a bad job after a couple of painful attempts.

Old version of wizard To get the drop shadow, I copied the bottom quarter or so of his robe into another layer, put it behind the original, and added a drop shadow effect with a long distance. It took a little tweaking with the eraser to get the shadow shaped right, but I think it came out pretty well. In fact, on a whole I think this version looks a lot better than my first several attempts, which were somewhat lame (see at left for an example). Granted, the whole exercise is a bit silly, but sometimes it’s fun to play around with things without being too precise or analytical about the whole thing. That’s one of the things I really miss about Kindergarten.


So, after all that, the answer to your question is simple: Where did I get the wizard? I copied him from the cover of the online edition of SICP and added the colour with Photoshop. I could probably have just said that up front, and saved you some time!

:-)

Snowfall

Within the past twelve hours, more than a foot of light, fluffy new snow has fallen in Hanover, and they say more’s on the way.

Now, since it’s the middle of winter, you might imagine this is very little cause for comment, but this particular winter has been surprisingly snow-free here in the Connecticut River valley of New Hampshire. Indeed, just a couple of weeks ago, the Human Dog-Sled Races they had planned for Dartmouth’s Winter Carnival had to be rather abruptly converted into six-man three-legged races, because you can’t pull a sled too easily on frozen sod. I got some pictures of the result—which was hilarious in itself—but the point is that there was almost no snow on the ground the whole weekend. The snow sculpture looked as if it had been trucked in.*

* Which, in point of fact, it had. The snow was brought in several truckloads over the course of a few weeks, from some durable heaps out north of town where the road crews store the stuff they get from clearing the streets after a storm.

I noticed it was snowing today at some point early in the afternoon, when I took a break from working on my thesis to walk around and stretch my legs. It was coming down in nice fat fluffy white flakes, which usually means it’s going to peter out within a couple of hours. But, not today! By the time I poked my head out again, around dinnertime, the snow had covered the yard up to the bottom of the front door. I pulled on my boots and gloves and spent a productive forty-five minutes or so clearing the walk, shovelling out the driveway, and brushing off the car. Then, I decided I had earned the right to dine out, and walked up the hill to get some hot curry. Nothing clears out the old sinuses like the old one-two combination of work and vindaloo. I suppose the celebratory tequila shot didn’t hurt either.

My favourite thing about a snowstorm is how it muffles everything. Not only does the snow dampen echoes, but it drives a lot of people indoors who would otherwise be out driving around or partying of a Saturday night. Apart from a few brave souls, the streets were quiet tonight and nearly empty, which I always find pleasant. I passed a few students carrying sleds in the direction of the golf course, and a few coming back therefrom in snowshoes. This storm wasn’t big enough to really change the rules—not like that storm we had sometime in the early to mid 90’s that dumped over two feet of snow in less than a day, and shut down pretty much the whole town. But it was just enough to make for an enjoyable trek around town.

I sometimes miss the house where I used to live in Lyme, NH. It was quieter and darker there, and there was a sweet little babbling brook running less than fifty feet from my back door. It’s never really quiet or dark here in town, between the street lights, the traffic, the dirt trucks riding their engines all the way down the hill to Vermont, and the occasional late-night pong tournament in our neighbors’ patio. But when the weather is thick upon us in the dead of winter, it’s awfully nice sometimes to be able to get to things on foot, and not to have to worry whether I’ll make it in to the office. I have fun driving in the snow, sometimes, but it’s nearly always less trouble to walk.

Also, this weather is the perfect justification for lounging about indoors on the sofa, reading a book and sipping hot tea. Maybe that will be my goal for tomorrow morning, if this stuff continues.

Debuggery

One aspect of a programmer’s education that is rarely discussed in any detail within the classroom is the esoteric art of debugging. And yet, the ability to debug effectively is one of the most important skills any programmer can possess. I would not dare make so bold as to claim I know “the secrets” of great debugging, but since I have a fair bit of experience in doing it, I will herewith attempt to explain some of the tricks that work for me. You may take it as given that I’m stating my opinions here, and I do not make any special claim to authority.

Preliminaries

The first thing to know is that, no matter what kind of a hotshot programmer you are, your code has bugs. Until you can accept that basic fact, you will never really become any good at fixing them; your code will be perpetually broken, and anyone who relies on your code (including you) will be perpetually frustrated. Once you accept that bugs are a fact of life, you can stop worrying about them, and start fixing them.

The second thing to know is that every bug hides behind the word “should.” As in, “this program should do x (but it doesn’t)” or “this program should not do x (but it does).” Finding bugs is a species of problem-solving that requires every assumption to be challenged, every truism to be questioned, and every sacred cow to be sacrificed. Pride, guilt, and blame are the enemies of effective debugging, and should be set aside as much as possible when you are on the trail of a tricky bug. What matters is fixing the bugs.

Armed with a clear conscience and an open mind, you are ready to begin. My debugging checklist consists of the following basic steps, to be followed in order:

  1. Characterize the bug. The very first thing you need to do is to establish what the bug is. Ask yourself: “How do I know I have a bug?” Does your program give the wrong answer? Does it crash at inopportune moments? Does it exhibit behaviour that contradicts the specification? Characterizing a bug consists of identifying what goes wrong, and any contextual clues you can discern about how, when, and why the bug occurs.
  2. Localize the bug. Once you have clearly identified what the bug is, the next step is to figure out where the failure occurs—or at least, where everything begins to go wrong. Surprisingly, this can be one of the most difficult parts of debugging, especially in a large and complex software system with many components and multiple developers. Whereas characterization of a bug is often possible without looking at the code, localization generally requires that you do some hacking.
  3. Isolate the bug. Having identified and localized the bug, the next step is to isolate the code that actually causes the bug. Sometimes, the bug is in the code at the point of failure; but quite often, the bug is not really in the place where you see the error, but in some other piece of code. Isolating the bug requires that you do some detective work, tracking down how you got to the point of failure. This can be much more difficult than localization, because it usually depends upon a lot of unspoken assumptions about the interface between different modules within your program.
  4. Fix the bug. By this point, you know what is wrong and why, and you are ready to actually fix the bug. With luck, the error is some simple logical faux pas that can be easily corrected; though occasionally even trivial-seeming bugs can drive a substantial refactoring effort. You want a fix that really resolves the problem, but which doesn’t break anything else along the way.
  5. Verify that the bug is fixed. Once you have fixed the bug, make sure your fix really works. You should at least be able to go back and verify that the program no longer fails under the conditions that caused it to fail in the first place. Ideally, you should also make sure you haven’t broken anything else, by re-running any regression tests you have developed, and re-running any formal validation checks.

As checklists go, this one is pretty simple, but as my Aikido sensei taught me, “simple” does not always imply “easy.” For instance, my students frequently ask me to help them debug their code, and as often as not, the biggest hurdle is figuring out what the problem really is—Step 1. Is my program looping forever, or is it just taking a long time to finish? Why do I get different results when I run the program again with the same inputs? My function “seems to work” by itself, but when I use it in conjunction with another function, I’m getting the wrong answers. These are not always easy problems to figure out: Compiler diagnostics can be misleading, and knowing how to interpret the output of a complex program can be challenging.

Debugging is a task that responds very well to a patient, methodical approach. However, it is also very much a “puzzle-solving” type of activity, in which it pays to think outside the box, and to try unconventional approaches. Look at it this way: Your code is already broken, what can it possibly hurt to try a few unusual tricks? Unlike a sick person, if you kill a sick program by trying a wild new therapy, you can always go back to your previous revision.

And while I’m on that subject, I should also mention that I think revision control is probably the single most important tool for debugging a large software system.* To quote Gregory Wilson in American Scientist:

“Version control is as fundamental to programming as accurate notes about lab procedures are to experimental science. It’s what lets you say ‘This is how I produced these results,’ rather than, ‘Um, I think we were using the new algorithm for that graph—I mean, the old new algorithm, not the new new algorithm.’”

Debugging is an incremental process, just like development, and so it behooves us to keep track of our efforts at fixing errors in the same way we kept track while we were writing the broken code in the first place. As Jim Matthews of Fetch Softworks once said to me, “Development goes back and forth between writing bugs, and fixing them. Right now, I am writing new bugs, but I’m taking notes so I can find them again later.”

* Actually, I don’t think it’s limited to large systems; even when I am working on small programs for my own use, I find it very handy to be able to track my code changes in a methodical fashion.

Tips and Tricks

Beyond the basic methodology of it, I’d like to describe a couple of basic themes I have found helpful in my own experience debugging software. These are lessons I’ve learned from other people, in one context or another.

One of my teachers once taught me that the best way to debug a program is to avoid writing bugs in the first place. Lubarsky’s Law of Cybernetic Entomology notwithstanding, this is sound advice: It’s almost always worthwhile to take extra care during the design and implementation phase, both to keep bugs out of your code, and to make those inevitable misses easier to find. Eschew complexity, write neat, well-documented code, choose simple, clear abstractions, and avoid the urge to generalize or specialize your code without a demonstrated need. As Donald Knuth famously said, “Premature optimization is the root of all evil (or at least most of it) in programming.”

My father taught me to use the right tool for the job, a lesson that applies just as well to programming as it does to woodworking. You can’t build much with just a screwdriver—you need to know how to use all your tools together. In computing terms, that means you shouldn’t let yourself get stuck in a rut: Learn how to use a debugger, be familiar with the workings of editors, preprocessors, shell scripts and Makefiles. Learn as many programming languages as you possibly can, and never be afraid to hack together a new tool if you need it. A programmer who only knows one language well is blind in one eye.

Design your code so that it can be tested, and then test it. The need to make things testable will help you to avoid writing the kinds of poorly-decomposed monolithic wodges of code that give maintenance programmers the delirium tremens. When it comes time to find and fix your bugs, you will thank yourself (and your teammates) for doing so.

Conclusion

The point of this article isn’t to draw any conclusions, so I’m not going to try. However, I hope that by assembling a variety of my thoughts and insights about debugging in a single location, and where it’s possible they might serve a useful purpose to others. And, for anybody whom I’ve subjected to a grilling in response to a simple request for assistance with debugging, I hope that perhaps this catalogue will provide some insight into the motivations behind it.

Next Page »