Thoughts on programming – Exceptions

March 27th, 2009

Exceptions bring many benefits:

  • Separation between function’s results and error information
  • When being part of the interface, they communicate cleary what special cases should be handled by the caller. (That’s why I in gererally prefer throwing an exception over returning null – returning null is too easy to oversee for the caller)

There are several discussions about error handling that i would not want to reopen, since I can see arguments for both sides:

  • Reusing existing Exceptions vs. creating your own, meaningful Exceptions (e.g. BookNotFound vs. NoSuchElementException, especially if there is more than on potential “element”)
  • Throwing exceptions for exceptional situations only, e.g. “Should a select throw a NotFound exception, or return null?”
  • Checked vs. unchecked exceptions?

One of my favourite strategies for error handling is to fail early: Your program wants to be as intolerant as possible.
A configuration value is missing? Throw an exception, don’t use a default value. Fixing a missing configuration value takes about 1 minute, finding out why the program uses an unexpected value might cost you hours. Make the program opinionated and simple.

In the coding: When your program notices an error, locally it seldom has the knowledge how to fix this. Make it somebody elses problem: Throw an exception, forget about it, continue with the normal flow.

In most programs I also introduced an “InternalException”: An exception thrown for all programming errors and “can’t happens”, if they do happen. Just in case – check, throw, and forget about it.
This can occur everywhere, hence it is an unchecked exeception.

In contrast to “can’t happen” there are some exceptions that are unavoidable in real life, so these should be dealt with in a manner that is understandable for a dummy user. Don’t show the user a stack trace for this classes of exceptions:

  • Configuration errors, installation errors, file not found
  • Invalid user input (parameters/values)
  • Other component (server, database) not reachable

When using exceptions, don’t clutter your code with try/catch. Throw everywhere, but catch only rarely: Everytime you catch, you should be able to fix/handle the problem (most times by signaling it to the user).
If you cannot handle the exception, let it flow until someone can.

This is only half the truth: One of the main other reasons for try/catch is exception mapping, the rational for this is seemingly good: Implementation hiding, especially at component borders.

try
{
    doSomething();
}
catch (JDBCConnectionException e)
{
    throw ComponentUnavailableException (e.getMessage());
}

Note there is one common mistake in the code above: You loose the valuable original stack trace. You would want to wrap the original Exception instead:

    throw ComponentUnavailableException (e.getMessage(), e);

Stack traces are a gift by mother Java, don’t throw them away light heartily.

The other major problem is that this mapping leads to a lot of duplicated try/catch code, especially if you want to map more than one exception in more than one method. As Ruby On Rails teaches us, mapping is seldom a good thing.

Untyped exceptions and your framework (Spring!) might help, but this is one of the tricky situations where code duplication is sometimes hard to avoid.

VN:F [1.9.22_1171]
Rating: 0.0/10 (0 votes cast)

Trackback URI | Comments RSS

Leave a Reply

Name (required)

Email (required)

Website

Speak your mind