I was reading a new post over at Jeff Atwood’s excellent blog Coding Horror today, and the comments section featured some discussions on exceptions and exception handling. I have had more than a few conversations/arguments with fellow programmers about error and exception handling over the years. I am going to attempt to sum up some of my thoughts across a couple of blog posts.
In C# and several, similar languages, the try/catch/finally block is used the trap exceptions.

The system will throw exceptions for a number of reasons. When working with code that is likely to through an exception you really should try to trap it and do something with it, even if that something is simply to re-throw the exception.
I lump system-generated exceptions into three group:
- IO Exceptions.
- You’re not using this API correctly exceptions.
- Weird Exceptions.
The group “IO Exceptions” means that a network connection, database connection, file connection, etc, was lost or interrupted and an exception is thrown. “You’re not using this API correctly exceptions” mean invalid argument exceptions, use of deprecated code, etc. These exceptions are intended to be caught early, during testing, to inform the programmer that the API is not being used in the way that was intended. “Weird Exceptions” are everything else. I am sure anyone with enough time in the programming world will have encountered truly bizarre, freak situations, with exceptions being thrown in a relatively safe chunk of code.
When writing database, networking or file IO code, the programmer should expect something bad to happen. What happens if the database schema has changed, or the server goes down (or is too busy), or a file is locked by another process? When dealing with anything outside the boundaries of the running process, it is wise to go ahead and assume that an exception will occur and handle it accordingly. I think that this is a pretty solid argument to make; you would have a difficult time coming up with a counter argument that held any water.
How to handle the exception once thrown is another argument altogether. When working with IO Exceptions, a programmer is usually faced with a situation where the program is using valuable resources. If the exception is simply allowed to bubble up unhanded, these resources will not be freed. In the case of a .Net data reader, if a connection is lost to the database and an exception is throw, the try/catch/finally block should make sure that the read is closed and disposed, and that the connection is closed and disposed, if needed. The simplest next step is to just re-throw the error once resources have been cleaned up.

Another school of thought is to attempt to compensate for the error. I had a co-worker (we let him go) that wrote a while loop to continued to attempt a SOAP XML Web Service call over and over until it succeeded, with no way of escaping the loop. The client code using this poorly written chunk of logic simply waited and waited. This is an extreme case of bad programming, as I am sure you would agree. I have seen similar code that retries for a limited amount of time, then throws the exception, etc.
I don’t like compensating logic in most cases. Or rather, I don’t like it in business or data logic. If an error occurs, higher-level code should do the compensating or simply inform the user that something bad has occurred. Additionally, compensating code can have unintended consequences.
First, compensating code often hides the exception. This is bad news for developers and software tested trying to test the code. Compensating code can also hide bugs that occur in regular intervals, instead of those truly exceptional-case errors (network down, file locked, etc), which is what exception-handling code is supposed to address.
One of the fundamental problems with compensating code is that there is usually only one way to compensate: try again. A very, very bad side effect of compensating code that “tries again” is that it can cause data corruption. Your customer’s data integrity should be your top concern. Code that tries to automate retries of failed calls can end up calling business logic over and over by accident. Imagine a piece of business logic that subtracts money from an account. Now imagine it being called over and over, by accident. And compensating code that hides an exception may cause other code to execute that the programmer had not intended to execute in the case of an exception.
To sum up, here is how I handle most “expected” exceptions:
- Log that the exception occured.
- Clean-up any resources that are in use in the local scope.
- Re-throw the exception, or bundle-up the exception in a new exception and throw it.
This pattern is demonstrated in the code block above. This simple approach does several important things. Please consider the following:
- Debugging code at a customer site is difficult. Logging exceptions means that you have (drum-roll) a log file to use to aid in the debugging.
- Cleaning up resources limits the impact of the exception. The task being performed may fail, but it won’t create memory leaks, database locks, files locks, or other issues that will result in bigger trouble than that previously-mentioned single task failing.
- Re-throwing the error in business or data logic allows higher level, work flow oriented code to compensate for problems, or allows the user to compensate.
- Re-throwing exceptions allow developers and testers to find bugs earlier in the software life cycle. With adequate testing, this results in better code getting into the field.
Next time I want to discuss when it is a good idea to create and throw exceptions in your own logic.
