Exception handling: some thoughts on good practices

! Warning: this post hasn't been updated in over three years and so may contain out of date information.

stop signThis article is inspired in part by Mike Hadlow’s “The first rule of exception handling: do not do exception handling” article that he wrote last year. Whilst it is simply the eye-catching title of an otherwise well considered article, to my mind it also encompasses some bad advice often given on the topic. This article is born out of a desire to “set the record straight” and offer up what I see as some exception handling “good practice.”

Exception handling is one of those software engineering topics that is simple in theory, but creates a great deal of confusion in practice. It doesn’t just confuse either: it also results in some truly awful bad practices. This is often due to people learning exception handling “on the job” whilst using languages with exception handling features that range from really crap to non-existent. I had the fortune of learning about exceptions within the context of the Ada programming language whilst still at college many years ago. Whilst an old language, exceptions were built into the design of Ada from the start, unlike many languages in use today, which bolted them on as an afterthought. I feel that learning about exception handling so early in my software engineering education has helped me no end in forming a set of exception handling good practices that I (try) to follow:

  1. Never go into denial and simply bury troublesome exceptions with a

    type piece of code. This is the greatest sin of exception handling.
  2. Never just let an exception bubble up to the top of the stack causing the application to exit. Doing absolutely nothing about an exception is the second (if not joint-first) greatest sin of exception handling. Note though that in genuinely exceptional circumstances – eg, you run out of memory – this rule may be ignored.
  3. Handle exceptions as close to the scope that caused the exception as practical.
  4. Know what exceptions can occur from the API calls your code makes and add exception handling into your code to deal with them all.
  5. Document all exceptions that your own methods can throw and under what circumstances they can occur.
  6. Make exception handling part of your coding thought processes

1. Never go into denial and simply bury troublesome exceptions
Rule 1 is well-documented and generally agreed upon. Mike’s own article adequately explains why it is a bad idea. There is an analogy – I’ve no idea who the original creator was – that also explains it:

A man is shot and wounded. He hides the wound, climbs on a bus and travels across town. There he dies of blood loss. The police find him dead 10km from where he is shot, with no clues as to the cause. The case is never solved.

The lesson to be learned from the story of course is that by hiding things, you make them worse and therefore it’s good practice to not hide exceptions, and to deal with them instead.

2. Never just let an exception bubble up to the top of the stack causing the application to exit
The analogy described above is often extended to the suggestion that the man should have just lain down and died where he was, as this would give the police the most clues. In exception handling terms, this equates to never handling exceptions: let them bubble up and report them to the user as that is better than hiding them. This argument is of course a classic false-dichotomy fallacy.

Just laying down and dying in the former analogy is just plain sloppy and lazy. The exception handling equivalent is also plain sloppy and lazy. It is a false dichotomy for there are other options between exception-obfuscation and exception-laziness. Those other options are where the best practice lies and the sloppy-lazy approach hopefully never occurs if you follow rule 3.

3. Handle exceptions as close to the scope that caused the exception as practical
By extending the analogy I can hopefully show these good practices in action:

The man is playing paintball and just got paint on his goggles. He wipes it off and carries on playing (assuming head shots don’t count in his paintball game)

The man realised he has been shot, but isn’t fatally wounded. He writes down a description of his attacker and dials 999 (or 911, 112 etc depending on the country) and phones for an ambulance.

The man is shot and dies instantly. He collapses in a messy heap.

Taking these analogies in turn. First, we have the trivial exception case that can be locally dealt with and the rest of the system need not know anything happened. By example, imagine you have a simple routine that counts from 0 to 255 and then loops back to 0, the overflow exception thrown when your unsigned byte attempts to add 1 to 255 can be safely swallowed up and ignored. Propagating the exception would be a nonsense in this case. However it is important to note the the exception handler must only swallow the overflow exception here, otherwise it falls foul of rule 1.

In the next analogy, the victim doesn’t just lay there, he starts the process of dealing with the situation. Likewise, in many situations where an exception occurs, the code needs to act on that exception and report something more meaningful to the code above. For example, imagine you have some code that attempts to save a user’s work to a remote storage device that has gone offline. The wrong thing to do would be to just pass on the RemoteStorageDidntRespondInATimelyFashion exception to the parent code (which will definitely have no idea on how to handle this exception.) Instead, perhaps you could record details in a log file and pass on a more general CannotSave exception that the parent code does understand, perhaps by offering the user a “Save As” dialogue.

When your application suffers a fatal problem, eg runs out of memory, trying to save work, throw new exceptions etc will all likely fail. There is nothing you can do, so in this special case it is OK to let your application collapse in a messy heap.

4. Know what exceptions can occur from the API calls your code makes and add exception handling into your code to deal with them all
API documentation is there to help guide a developer in how to make use of those API calls within her code. A common mistake many make when reading said documentation is to concentrate on what parameters a method takes and what it will do when things go right. What can go wrong is often overlooked. This ignoring of what can go wrong underpins the problems many have with exception handling. If you don’t read about what can go wrong, you’ll unlikely to plan your code around coping with such failures. This then sets the developer on the slippery slope to exception obfuscation or exception-laziness. To avoid that trap, remember that reading exception throwing details is equally as important as reading the functionality details of all API documentation.

5. Document all exceptions that your own methods can throw and under what circumstances they can occur
In the previous rule, I said developers should take notice of exception information in API documentation. Clearly this can only occur if the exception details are there in the first place. If you expect others to include such details, then lead by example by including them yourself in your own APIs. Remember it is as important to tell the users of your APIs about what can go wrong as what happens when it goes right.

6. Make exception handling part of your coding thought processes
There is a simple rule of thumb surrounding exceptions: if you don’t think about them, or go into denial about them, they make your life harder. To my mind the real first rule of exception handling should be “think about exception handling.” I have deliberately placed this rule last, but really it is the entry point into good exception handling practices. As you write code, think about what can go wrong and decide what your code needs to do about it. If your method throws a NullPointerException, it should be because you decided that is what it should do, not because you forgot to take null pointers into account.

Be an exceptional developer: think about exceptions.

3 thoughts on “Exception handling: some thoughts on good practices

  1. I agree with all of these rules, however find myself breaking rule number 1 in certain circumstances (mainly in unit tests)!

    One of my huge frustrations with java is when you end up with code such as:

    } catch (ThisException te){
    } catch (ThatException te2){
    } catch (AnotherException ae){
    } catch (BadFootException be){
    } catch (MorbidException me){
    } catch (BadSmellException e){

    In other words, a few lines of code declare about 5 or more different exceptions from different class hierarchies and you end up with over 20 lines of nearly identical exception handling code. You can get around this by simply catching Exception but this is not ideal, you end up suddenly dealing with RuntimeExceptions in the same manner, and you lose the freedom to simply duck a specific exception.

    Looks like they plan to deal with this in jdk7 with the new multicatch facility: http://www.baptiste-wicht.com/2010/05/better-exception-handling-in-java-7-multicatch-and-final-rethrow/ But by the time jdk 7 is released I will probably be in my 3rd career!

  2. Java 7 does indeed have a good solution to this problem. In the meantime, how about this as an alternative (syntax not checked BTW, but the general idea should work):

    Whilst it is catching all exceptions, it explicitly deals with the ones you are able to handle and re-throws all others. It is less elegant than the Java 7 solution, but still reasonably elegant none the less.

  3. Apart from some intrinsic exceptions which have to be handled (like loading stuff), I think there are very few occasions in AS3 when you can’t error-proof code just by checking for null pointers or invalid numbers etc. AS3 doesn’t help things by calling it an ‘Error’
    instead of an exception. It just makes people think try/catch is for handling bugs.

    For example, you could theoretically do this

    for(var i:int=0; i<array.length; i++) // etc
    // handle errors arising from array not being defined

    But in reality, you would probably do this

    if(!array) array = [];
    for(var i:int…etc)

    Or whatever action is required if the array is undefined.

    So perhaps you should add a rule

    7. Only handle exceptions which are explicitly thrown by the code. Design out errors.

Comments are closed.