Make Exceptions Work For You

January 29 2006

In my experience, most programmers are scared of exceptions. They dread the news that their program has terminated in an unexpected manner, mainly because it implies that debugging is necessary (although perhaps a small part of the reason is because it suggests that their creation has fallen just a smidgen short of perfection).

As such I don't think there's anything wrong with a small fear of exceptions. However it seems to be gradually becoming a part of programming culture to 'do something' about exceptions, and there's really only thing that can be done to them: suppress them. While certain limited classes of exceptions - 'file not found' for example - are generally fairly benign, others - an incorrect array index for example - are far more serious. All too often, although the programmer thinks the program will keep running after an exception, instead it hobbles to a slow and painful death.

This mindset seems particularly prevalent amongst Java programmers. First, Java's horrible notion of checked exceptions positively encourages exceptions to be suppressed, just to try and keep the number of try ... catch constructs to a semi-reasonable level. Second, in multi-threaded Java applications, an exception which reaches the top level in one thread terminates only that thread; this gives the impression that exceptions in multi-threaded programs are somehow less serious than in their single threaded brethren. I have seen many Java programs randomly suppressing some exceptions, and allowing others to spew backtraces others to stdout, but continuing on anyway; this seriously undermines my confidence in the programs' correct running. But the funny thing is that people just say 'oh, ignore that exception, it doesn't seem to cause any harm.' This is a mindset that I find alien - to me, exceptions are always serious, and they always need to be fixed.

I have come to look at things in a completely different light. Although seemingly perverse at first, I don't mind exceptions being raised in my program. Obviously I don't want my program to have flaws of any kind, but since it would be foolish to think that I can create a program without flaws, the sooner flaws are identified, and the more accurate the information about them, the better. The backtraces with line numbers that most decent programming languages produce when an exception is raised are, in my opinion, the single most useful debugging aid possible.

Jim Shore's Fail Fast article articulates a similar philosophy, using assertions to terminate a program when an assumption is violated, but I find assertions to be useful only in certain limited circumstances. I actually like to take things one step further in a crude sort of fashion. I have long realised that when I'm programming a new function, I'm generally only coding the bits of the function necessary to make my current use case function correctly. The other bits of the function that will make it fully general I fill in at some later point. This, I feel fairly confident in stating, is a common development strategy. The problem with it is that sometimes one forgets to fill in the other bits of the function. Then some poor unsuspecting fool comes along, feeds a value to the function which it can't properly cater for, and gets a random results. Random results are the worst thing that can happen in a program, as tracking down something random generally degenerates into guesswork.

So what I now do is harness the power of exceptions to stop a program dead in its tracks (at least, unless one is using Java). When I come to the point where I think 'two things could happen here, but I only want to code for one of them at the moment', I raise an exception. I don't care what the exception is, merely that it is raised, and that it is easy to find in the source code. In Python and Converge, assuming I only wanted to cater for the case that x == 0, I use the following idiom:

func f(x):
  if x == 0:
    // do whatever
    raise "XXX"
Although "XXX" is an acceptable exception in Python, it isn't valid in Converge which expects instances conforming to the Exception class. But that doesn't matter, because when the user (which might be me, but might be someone else, at some unspecified point in the future) calls f(0), the program will terminate in an orderly fashion that makes accurate pinpointing of the cause of the problem trivial. Whats more, I occasionally do a search for XXX in my source code just to see if there are any unfinished bits that I can usefully tackle. This idiom not only kills two birds with one stone, but it's so low effort that now I use it automatically, without even really thinking about it.

With a little bit of thought, it's generally possible to get a similar effect in languages which don't have native exceptions. For example in my C programs I define the following macro:

#define XXX do { printf("XXX exit file %s line: \n", __FILE__, \
  __LINE__); abort(); } while (0)
Quite often the file name and line number is sufficient information for me; in other cases, I use a debugger to produce a backtrace which ends at the call to abort.

So I say don't fear exceptions - make them work for you.

Follow me on Twitter


Blog archive


Last 10 posts

What Challenges and Trade-Offs do Optimising Compilers Face?
Fine-grained Language Composition
Debugging Layers
An Editor for Composed Programs
The Bootstrapped Compiler and the Damage Done
Relative and Absolute Levels
General Purpose Programming Languages' Speed of Light
Another Non-Argument in Type Systems
Server Failover For the Cheap and Forgetful
Fast Enough VMs in Fast Enough Time