Joel on Exceptions

“People have asked why I don't like programming with exceptions. In both Java and C++, my policy is:

  1. Never throw an exception of my own
  2. 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. In fact they are significantly worse than goto's...“ [1]

Joel mentions two reasons for this rediculous view:

  1. They are invisible in the source code. Looking at a block of code, including functions which may or may not throw exceptions, there is no way to see which exceptions might be thrown and from where. This means that even careful code inspection doesn't reveal potential bugs.
  2. 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. “

Lets deal with each of these individually.

1. They are invisible to the source code: Yah, you find which exceptions might be thrown the same exact way you find which errors would be returned if you didn't have exceptions, RTFM. I don't see how it is really different in that respect... except that if I have multiple libraries, I am always gaurenteed that the base exception will be derived from the exception class. So, at the very least, this gaurentees that if I haven't written error handling code to deal with some new error introduced in an update to some other portion of the code (or one that I never bothered to deal with), I can implement some generic error handling in a consistant manner. If you are dealing with C++ libraries, this isn't the case. In one lib, you might have to check against anything other than S_OK, and in another lib, the clear state might be something like NO_ERROR. There is no required consistancy (in fact, you don't even have to return integers if you really don't want to). Now... although I don't like them, checked exceptions also solve this problem by requiring you do document all possible exceptions. So, for solving this problem, they are far superior to error codes, but they can add a lot of complexity to your app.

2. They create too many possible exit points for a function: Not really. This is why we have the “finally” clause. As long as you use the finally clause, you don't have to worry about things ending up in partial states, because the code in the block will always execute before the method returns. Of course, C# also gives us the using statement, which garentees that an object will be disposed without having to do anything explicitly.... but that is another topic for another day.

Joel's solution:

“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“

Really... lets take a look at the two approaches side by side.

Error Codes:
DWORD DrawFloodFilledBitmap()
{
  DWORD error = NO_ERROR;
  HBITMAP hBmp = CreateBitmap(width, height, planes, bpp, NULL);
  if(hBmp == ERROR_INVALID_BITMAP)
  {
     // handle error  
     error = GENERIC_ERROR
  }
  else
  {
      // do some more stuff
      BOOL success = FloodFill(hdcBitmap, x, y, color, fillType);
      if(!success)
      {
         DWORD errorCode = GetLastError();
         // handle error
         error = GENERIC_ERROR;
      }
      DestroyBitmap(hBmp)
  }
  return error;
}
Exceptions:
void DrawFloodFilledBitmap()
{
  Bitmap *b = null;
  try
  {
     b = new Bitmap(width, height);
     // do something
     b.FloodFill(x,y,color, fillType); // yah, I know this isn't a real method
} catch(Exception ex) { // handle errors GenericException ge = new GenericException(“Could not draw bitmap“); ge.InnerException = ex; throw ge; } finally { if(b != null) b.Dispose(); } }

Now, both contain relatively the same amount of code, but which is better? One thing you will note is that in the top method, we don't have a good way of returning one of our own, documented error messages, while still being able to provide more context (as in, yes, if failed... but, why?). We could return the individual error codes instead, but then we are back again to not having all our return values documented, which can make things messy for the handling code. Now, what happens if CreateBitmap is updated and now has two error values? Our code breaks, crashes and burns... and because of the way the method was defined, there isn't anything we can do about it either. We are left at the mercy of the library developers. However, by using exceptions, we have a consistant way to not only provide that deeper context (which is extremely useful when you just can't figure out why a method is failing), but we also have a consistant way to deal with new errors. Additionally, all our error handling logic is isolated in one portion of our code, which makes matinence a lot easier (ie. you don't have to dig around to find out where exactly the error is coming from).

Things get even more nightmareish when you start dealing with errors from different libraries. If I have a Win32 error, I call GetLastError to get extended error information and then from there, I can get a text based description of the error, but in library “x“, I might not have any way to get either extended information or a text based description of the exception. Both are completely up to the library developer. But, even if they do decide to implement these functions to help me out, now I have to call a different method for errors from each library I am calling into. Accidently call the wrong one, and boom...things blow up again, or they don't and you get some really screwy error messages.

Of course, pages upon pages could be written about the problems with the error code method, but eventually we will come to realize why most developers end up just ignoring error codes all together, it is just to cumbersome to deal with them properly. Yes, it can be done... but that doesn't mean it will get done.

[1] Exceptions. Joel Spolsky. http://www.joelonsoftware.com/items/2003/10/13.html

PS: looks like I'm not the only one who disagrees: http://discuss.fogcreek.com/joelonsoftware/default.asp?cmd=show&ixPost=77463&ixReplies=20

7 Comments

  • "you don't have to worry about things ending up in partial states"



    You still have to worry about partial states - the "catch" and "finally" clauses need to clean up any partial state that may have been caused by an exception thrown while the data structures were unstable.



    And how many people wrap their entire function inside a try/finally to clean up partial states?

  • That is just semanticts, yes, you technically still have to worry about things ending up in partial states, but it isn't a random, "my program threw an exception, so now I have all these open connections" type of thing. You can easily solve the issue by using "finally" blocks...I was pointing out the exact same thing you did, so I don't see any disagreement there.



    In any case, how many people wrap their entire function inside a try/finally block? Well, I definately don't anymore, because of the using statement, which is far superior to the try/catch/finally approach. However, before I knew about the using statement, I definately did, and seeing that it was the standard best practice, anyone who knew anything and was concerned about such things would probably have also done it. Nothing is foolproof. Morons are going to leave connections open just because they are morons, you can't really do much about that.

  • I love the way your example for error codes used C, but the exception example uses C#. Seems very unfair to me.



    "what happens if CreateBitmap is updated and now has two error values? Our code breaks, crashes and burns"



    Why? I can't see any reason.. error codes usually return zero for success and anything else as the actual error. So long as you check against zero your code should at least recognise that there's an error (if not which one). Compare that to adding a new exception -- which won't be caught without a change to the calling code. Ooops -- that's a breaking change.



    I also like the way your error code example can deal with an after CreateBitmap, whereas such an error in the exception example would be fatal (within the method, anyway).



    If you are going to post this stuff, at least try to make your examples unbiased and realistic ("catch( Exception ex )".. feh).

  • Regarding language choice, it is because C# uses exceptions and C uses return codes, so it makes a hell of a lot if sense to use two languages who are built around these concepts. The example is mostly "if" statements and method calls, so it doesn't make much of a difference. It's not like I was making calls to malloc and doing pointer manipulation.



    With regard to checking against zero, you couldn't be farther from the truth. The CreateBitmap call returns a handle which will not be zero, if it succeeds! So, your logic doesn't exactly work there. Even if INVALID_BITMAP was defined as zero (so things were reversed), you would still have an issue if another code was added for a different error, because != INVALID_BITMAP (or != 0) would indicate a failure.



    Regarding new exceptions, a new Exception WILL be caught, because all exceptions are derived from the base exception class. So, by catching exceptions of the type "Exception", you are assured that no exceptions will go uncaught.



    Regarding resumability, if CreateBitmap fails, you can't exactly fill nothing, so you would not want to resume. If you did want to resume for some strange reason, all you have to do is make your try block smaller.

  • For language choice, the problem is that you used OO techniques in the exception example making it look artificially better.



    For checking against zero, you missed my point. "Error codes usually return zero for success" -- keyword usually. In the case of CreateBitmap it returns NULL on failure. Same concept -- there's still no breakage of code with an extra error choice (because they come from GetLastError -- not my favourite method I'll admit).



    Catching the top level Exception class is meant to be bad coding practice (I thought -- don't have time to find anything about it on the internet though). Your theory is that people calling your method should wrap it in "catch( Exception e )" in case it adds a new exception between versions?



    As for resuming.. my point was that the example was poor as the two code examples are different -- there should be two try blocks to be equivalent (again making the exception method look artificially better).

  • I agree that error codes could do with being more standardised.. and I'm not saying I don't use exceptions. But I'll only throw an exception in truly exceptional circumstances, such that I never expect to have to catch one unless the application is about to die.



    My point with resuming was just pointing out the differences in the two examples.. one can resume the other cannot, so they are not equivalent. I haven't looked at your newer entry yet (I'll do that in a minute).



    I agree with your point about a method returning null completely. I prefer this way of dealing with simple errors, it really makes me wonder why File.Open doesn't use it.



    At this point I've just realised that I don't really use error codes at all. Most of my methods tend to return success or failure (bool or by returning null, or whatever).. they never seem to return a "code" as such. I think this is probably because my methods (in these c# days) are a lot smaller and rarely have more than one or two things that could go wrong.



    I still have a strong dislike of exceptions (more from my C++ days than in C#), but I don't tend to use error codes either. Kinda makes me wish we had a third mechanism available.

  • Boys, you didn't get the message. The 'goto' is also just semanticts.

Comments have been disabled for this content.