Wednesday, May 16, 2012

Exceptional Introduction

The blog is moving to a new domain. This post has found a new home: http://www.patrickdelancy.com/2012/05/exceptional-introduction/

There has been a lot of discussion over the years about when and how to use exception throwing and handling. The general consensus seems to shift every once in a while. The performance of exceptions is not the primary purpose of this article, but I felt the need to discuss it in order to truly address the role of exceptions in software troubleshooting.

Exceptions are Expensive, like Store Brand is Cheap

Maybe the best way to breach the topic of exception performance is to simply give you a short list of axioms that I believe to be true.

1. Exceptions are expensive

It has been proven many times that there is additional cost associated with constructing and throwing an exception. This seems to be exaggerated in managed applications (.NET, Java, etc.). That being said, this extra "cost" of constructing and throwing an exception would only be noticeable in time-critical real-time systems, or if they are used excessively. That brings me to my next axiom...

2. Exceptions should be just that... exceptions to normal operation

I firmly believe that when an application is running properly, there should ideally be no exceptions thrown... ever.

Don't start writing your flame comments yet! Notice the emphasized words in that statement. I completely understand that this situation is difficult if not impossible. We live in a corrupted world, and there is no such thing as a perfect developer, let alone a perfect application or system.

Building on this perception of errors, if you are returning success messages in an error or exception context, stop it! I recently worked on a system where an API call returned this message: <error code="$0000">Error: Completed successfully</error>

There are absolutely great reasons to write exceptions into your application... that brings us to my third axiom...

3. Exceptions can be used effectively to increase system stability and code maintainability

No, this is not a contradiction of my previous statements. Maybe the best way for me to tackle this section is to just give you a bullet list.

Good times to raise exceptions:
  • Invalid parameters
    • When validating input parameters to a function, constructing an exception can provide a lot of valuable information to help troubleshoot the problem. Possibly even informing the user so that they can correct the inputs and re-submit.
  • Enforce code assumptions
    • If you are building a library (for internal or external use), you should first of all document any assumptions you make about the objects coming from your factories or the objects you are acting upon. When it is feasible, test these assumptions and throw an exception so you can trace the source of the problem.
  • External resources are unreachable or behave in an unexpected way
    • This could be a database, 3rd party library, web service, etc.
  • etc. 
Bad times to raise exceptions:
  • To return valid values to a calling function
    • Use return values instead!
  • On a timer, as a status update
    • Code smells (indicator you are using this anti-pattern): You have a custom exception type called "StatusReport"
    • Raise an event instead!
  • etc.
Exceptions have been designed to capture details about errant behavior in an application. They contain a lot of valuable information that can help to troubleshoot everything from programming errors to network instability. That was a lot of background and build-up to get to the main point of this article...

Exceptions contain valuable information. Don't ignore them!

Following from my three axioms above, if an exception is raised, don't ignore it! An empty catch block is a common code smell. This particular code smell usually enters the code when you are working on something else, and an exception is preventing you from reaching the code you care about at that moment. So you slap in an empty catch to suppress the exception and get to your other code. This is like sticking a piece of gum on a leaky pipe and walking away.

"But what about expected errors in a 3rd party library?" Okay... so you are using a library that was written more poorly than your own code, and it raises an exception that can be safely ignored. What happens when that library gets updated, and that "safe to ignore" exception doesn't fire anymore, but a similar exception could be thrown which CANNOT be safely ignored? Now you are suppressing a valid exception that could result in bad behavior within your own application! In the very least, you should have some tracing or logging message that is generated when the exception occurs. I plan to address this more in an upcoming article about application logging and tracing. For now, we will leave it at that.

No comments:

Post a Comment