Design – The modern way to perform error handling…

designerror handlinglanguage-agnostic

I've been pondering this problem for a while now and find myself continually finding caveats and contradictions, so I'm hoping someone can produce a conclusion to the following:

Favour exceptions over error codes

As far as I'm aware, from working in the industry for four years, reading books and blogs, etc. the current best practice for handling errors is to throw exceptions, rather than returning error codes (not necessarily an error code, but a type representing an error).

But – to me this seems to contradict…

Coding to interfaces, not implementations

We code to interfaces or abstractions to reduce coupling. We don't know, or want to know, the specific type and implementation of an interface. So how can we possibly know what exceptions we should be looking to catch? The implementation could throw 10 different exceptions, or it could throw none. When we catch an exception surely we're making assumptions about the implementation?

Unless – the interface has…

Exception specifications

Some languages allow developers to state that certain methods throw certain exceptions (Java for example, uses the throws keyword.) From the calling code's point of view this seems fine – we know explicitly which exceptions we might need to catch.

But – this seems to suggest a…

Leaky abstraction

Why should an interface specify which exceptions can be thrown? What if the implementation doesn't need to throw an exception, or needs to throw other exceptions? There's no way, at an interface level, to know which exceptions an implementation may want to throw.

So…

To conclude

Why are exceptions preferred when they seem (in my eyes) to contradict software best practices? And, if error codes are so bad (and I don't need to be sold on the vices of error codes), is there another alternative? What is the current (or soon to be) state of the art for error handling that meets the requirements of best practices as outlined above, but doesn't rely on calling code checking the return value of error codes?

Best Answer

First of all, I would disagree with this statement:

Favour exceptions over error codes

This is not always the case: for example, take a look at Objective-C (with the Foundation framework). There the NSError is the preferred way to handle errors, despite the existence of what a Java developer would call true exceptions: @try, @catch, @throw, NSException class, etc.

However it is true that many interfaces leak their abstractions with the exceptions thrown. It is my belief that this is not the fault of the "exception"-style of error propagating/handling. In general I believe the best advice about error handling is this:

Deal with the error/exception at the lowest possible level, period

I think if one sticks to that rule of thumb, the amount of "leakage" from abstractions can be very limited and contained.

On whether exceptions thrown by a method should be part of its declaration, I believe they should: they are part of the contract defined by this interface: This method does A, or fails with B or C.

For example, if a class is an XML Parser, a part of its design should be to indicate that the XML file provided is just plain wrong. In Java, you normally do so by declaring the exceptions you expect to encounter and adding them to the throws part of the declaration of the method. On the other hand, if one of the parsing algorithms failed, there's no reason to pass that exception above unhandled.

It all boils down to one thing: Good interface design. If you design your interface well enough, no amount of exceptions should haunt you. Otherwise, it's not just exceptions that would bother you.

Also, I think the creators of Java had very strong security reasons to include exceptions to a method declaration/definition.

One last thing: Some languages, Eiffel for example, have other mechanisms for error handling and simply do not include throwing capabilities. There, an 'exception' of sort is automatically raised when a postcondition for a routine is not satisfied.

Related Topic