Joel Spolsky seems to simply not get it on exceptions. This is somewhat surprising; I really like most of what he writes. In this case, he's just not right, at all. Here's where he starts:
People have asked why I don't like programming with exceptions. In both Java and C++, my policy is:
- Never throw an exception of my own
- Always catch any possible exception that might be thrown by a library I'm using on the same line as it is thrown and deal with it immediately.
The reasoning is that I consider exceptions to be no better than "goto's", considered harmful since the 1960s, in that they create an abrupt jump from one point of code to another.
Hmm. Loosely coupled code, anyone? Sometimes, you have exceptions at a low level in the application that really can't be dealt with, unless you are at the UI level. Here's what Joel says:
- They are invisible in the source code. Looking at a block of code, including functions which may or may not throw exceptions, there is know way to see which exceptions might be thrown and from where. This means that even careful code inspection doesn't reveal potential bugs.
- They create too many possible exit points for a function. To write correct code, you really have to think about every possible code path through your function. Every time you call a function that can raise an exception and don't catch it on the spot, you create opportunities for surprise bugs caused by functions that terminated abruptly, leaving data in an inconsistent state, or other code paths that you didn't think about.
Hmmm. Everything he says here about exceptions is true of events as well. Are they evil? Are they to be avoided? After all, a piece of code may not know that it will get interrupted by an inbound event. So what is an exception? It's an application error event. That's what it is - nothing more, nothing less. What's Joel's answer?
A better alternative is to have your functions return error values when things go wrong, and to deal with these explicitly, no matter how verbose it might be. It is true that what should be a simple 3 line program often blossoms to 48 lines when you put in good error checking, but that's life, and papering it over with exceptions does not make your program more robust.
Bleah. I've seen code written using that theory. It very, very quickly becomes an unmaintainable nightmare, and has errors being propagated from deep in the bowels of the application up to a level where they can be handled. This is clean how? Maybe the problem is that exception handling in Java and C++ sucks - in Smalltalk I can do something like this
answer := [self doSomeThing that Calls ManyLevelsDeep] on: SomeException do: [:exception | exception isResumable ifTrue: [exception resume] ifFalse: [self reportError: exception]
So what will that do? It will resume the exception (i.e., resume as if the exception never happened in some cases, and report the error in others. It's compact, and it's easy to follow - and it has the benefit of avoiding a whole bunch of checks on whether or not I got an error throughout the call chain. In other words, it makes the code easier to read and easier to maintain. Joel's way makes the code crusty, complex, and hard to follow. It puts error handling code up and down the call chain in places it has no business residing. What Joel is advocating is writing code that misplaces responsibility - very bad form. I don't usually disagree with him, but on this, he's just wrong. A lot