Blog of Rob Galanakis (@robgalanakis)

The Exception Bogeyman

I recently ran across a post by my colleague Chad Stewart, Lead Dev Support Engineer at BioWare Austin: Try/catch control flow

First of all, you ought to know that exceptions are deliberately named. That is to say, they are reserved for exceptional cases.

try
{
 this.SomeRiskyOperation(someParam, someLocal, -1);
}
catch (System.Data.SqlException)
{
 return true;
}
catch
{
 return false;
}
return true;

This is sad because it is a big fallacy.  Exceptions as a concept have been around since well before modern programming languages, and they are for not at all exceptional cases in those modern languages (.NET and java are one implementation type, python would be another many are familiar with).  An ‘exception’ merely states that the invoked code could not execute normally.  It has nothing to do with being exceptional, or infrequent, or anything like that- it just indicates that code could not execute normally and cannot return a meaningful result.

If you think about something like a divide-by-zero exception- this is an issue of ambiguity.  What would you get if you divided a number by 0?  No one can say.  So what would you get if you tried to Frob and Sprocket but the Sprocket was null and there’s nothing to Frob?  Well, no one can say either- which is why null should be an invalid argument to Frob and Frob should throw an exception.  Likewise if the Frob method can only work on Sprockets that have been Blurmed.  If the Sprocket hasn’t been Blurmed, what’s Frob to do (unless its behavior in the case of a null or non-Blurmed Sprocket is clear)?

Similarly in the ‘not exceptional’ vein, python programmers have the ‘easier to ask for forgiveness than permission’ principle.  Rather than pre-validate everything, they try/except their way forward.  That is clearly a form of program flow, it is if/else by another name.

It’s sad that people don’t understand exceptions.  A large part of that is because exceptions are still thought of as exceptional.  Exceptions are not exceptional.  Not in modern managed languages they’re not, at least.  You need to understand how they work, how they are used, what they are for.  They are as important to program flow as any other language construct, they are just more complex.  There are best practices and ways to write robust code, there are also bad practices and it is easy to write bad code.  It is just more difficult to write good robust code when considering exceptions, because it is fundamentally more difficult (I’ll talk about exception handling and gotos in a future post, I promise).

Which is good, because most of the time, you don’t have to worry about exceptions (except if you use them like python programmers).  Which is good, because most of the time there’s nothing you can do about the exception.  Instead of worrying about exceptions, worry about writing good code, that is immutable, transactional, modular, testable, clear, explicit, etc.  I guarantee if you write better code, your exception handling experience will be better.  And exceptions reciprocate: code that enforces contract and behavior through exceptions will be more modular, testable, clear, and explicit.

So, back to Chad’s example code.  The problem with that code had nothing to do with exceptions. Rewrite that code without exceptions and it doesn’t get any better.  It may get faster, but the speed issue isn’t exceptions’ fault either- that is still just faulty code.  You can write slow code without exceptions (and I’ll bet your code spends infinitely more time in blocking IO than it does in exception handling).  The problem with that code is that it was shitty code. It was shitty because it wasn’t correct for its domain, which was inside of a tight loop.  There’s honestly nothing so conceptually wrong with that code from the sample- if SomeRiskyOperation is free of side effects, and the containing method has a clear true/false contract.  But I don’t have enough context to judge.  I am just saying that that code  which looks horrible may very well be fine.  It is just an example of the exception bogeyman, that uncomfortable programmers use to terrorize younger programmers that will listen, and it is a huge lie.

This isn’t the last you’ll hear from me about exceptions.

2 thoughts on “The Exception Bogeyman

  1. Chad Stewart says:

    Oh, Rob. I think we agree on different levels.

    I’m not saying exceptions are fundamentally flawed. I’m saying reserve them for when things are absolutely out of your control, or for where you know 100% (not 99) that you can safely move past an exception without a problem. Those are what I think you can consider “exceptional.”

    I am fully in agreement when you say things like: “Rewrite that code without exceptions and it doesn’t get any better. It may get faster, but the speed issue isn’t exceptions’ fault either- that is still just faulty code.” “Well, no one can say either- which is why null should be an invalid argument to Frob and Frob should throw an exception.” However, I agree because I think try / catch usually gets added to the mix in the wrong occasions only to mask this.

    The real questions here are why are we trying to divide by 0 and why are passing a null Sprocket? This is something that could indicate a large problem at the root level. Try/Catch/Ignoring the problem is probably not the right call. Now, that kind of depends based on the architecture of the system. Obviously a web server or an MMO server should not blow up. However, that still doesn’t mean it’s okay to sweep the error under the rug. That information needs to be surfaced. I guess the code sample was a little too vague to convey the message. You’ll notice that if SomeRiskyOperation doesn’t complete, we totally return true as if we did everything we needed, because maybe when someone was working on this code they got an exception that COULD be ignored. Now the Exception is that we can’t connect to the database and nothing is working. It’s still an SQLException, and maybe the third party you’re using only provides one type of named Exception. (You’ll notice I didn’t even use System.Exception in the example. If you ever sweep that one under the rug…)

    If you know you’re going to pass an Invalid Argument to a function half the time, why bother calling that function in the first place? Granted, code that throws an Exception should leave an object untouched instead of in a heinous state, but why do all that work?

  2. Chad Stewart says:

    @Chad Stewart

    Oh, and I think there’s a big difference between flow control and being able to safely ignore / recover from an exception. The next step of execution shouldn’t be relying on whether or not we got an exception. It should be able to safely continue, but it should not fork the logic. Perhaps a better example of this would be to catch the SqlException and decide to try it in Oracle. (In that example, go back to square one and use an Interface.) If you utilize an Exception to determine whether or not you’ve been passed a null reference, maybe you need to rework that one a little bit.

Leave a Reply