Know Your Limits

I just finished the nastiest debugging experience of my career–nearly 3 weeks on a single bug. After days and days of staring at code, swearing at core dumps, tailing logs, connecting to gdbserver via a multi-hop ssh tunnel from inside a secure environment, and general programmer misery, I felt like doing cartwheels when I finally figured it out, tweaked a few lines of code, and observed stability again.

Hindsight teaches me this lesson: undocumented, unhandled constraints waste enormous amounts of time and energy. If you’re interested in writing good code, you must know your limits, and you must communicate them. This especially matters when the constraints are obscure or surprising.

image credit: ericdege (Flickr)

Naive optimism

My bug seemed simple enough at first blush: Continue reading

Why Exceptions Aren’t Enough

(This post is a logical sequel to my earlier musings about having a coherent strategy to handle problems.)

Back in the dark ages, programmers wrote functions that returned numeric errors:

if (prepare() == SUCCESS) {
  doIt();
}

This methodology has the virtue of being simple and fast. We could switch based on the error code. A “feature” of our apps was that our users could google an error code to see if they had company:

Image credit: xkcd.com

However, as we wrote code, we sometimes forgot to check errors, or tell users about them:

prepare();
doIt();

Admit it; you’ve written code like this. So have I. The mechanism lets a caller be irresponsible and ignore the signal the called function sends. Not good. Even if you are being responsible, the set of possible return values is nearly unbounded, and you get subtle downstream bugs if a called function adds a new return value when a caller is switching return values.

Another problem with this approach to errors Continue reading