In this article we will discuss exception handling best practices. There is a lack of meaningful information about exception handling on the Internet, so let’s go.
Have you ever seen that recommendation to avoid exception handling like this:

[code language=”csharp”]
try {
//do something
}
catch(Exception ex) {
}
[/code]

Well, indeed, I agree that this exception handler looks pretty bad. But is it helpful to know only that? No, it is not. The problem of proper errors\exceptions handling is very far from being popular. There are some articles on the Internet, but I feel like there’s a lack of information on that topic, and this problem should be popularized among at least junior and middle developers.

What does an “Exception” mean?

If you noticed, the title of the article differentiated the notions of an “error” and an “exception”. Many programmers still think that they are the same, but this is very far from the truth (though, nobody knows where is the ultimate truth regarding errors\exceptions handling problem).
The simplest way to explain the difference between errors and exceptions is to say that “errors are returned, but exceptions are thrown”. So, the original meaning of throwing exceptions is to fail fast, unwinding the stack, until the proper handler (catch-block). The most important part here is “unwinding the stack”, this is the main difference from returning an error. The most appropriate cases in which exceptions should be thrown are:

  • improper API invocation
  • program reached critical circumstances in which it can’t continue to execute

As Eric Lippert said here and there:

  • Exceptions are noisy when you’re debugging.
  • An exception is a goto without even a label.

Eric Lippert continues, “Their power comes at an extremely high cost; it becomes impossible to understand the flow of the program using only local analysis; the whole program must be understood. This is especially true when you mix exceptions with more exotic control flows like event-driven or asynchronous programming. Avoid, avoid, avoid; use exceptions only in the most exceptional circumstances, where the benefits outweigh the costs.”

Practical problems of handling Exceptions

How many times your program failed because of an unexpected exception thrown from an API you invoked?
Let’s consider an incredibly simple case: you have to read a file from the disk and notify the user if something goes wrong. Somewhere in your code, you’ll use, say, File.ReadAllBytes(). Now the question is what exceptions are you going to catch? (don’t forget the notification requirement)
Well, if we want to adhere to the principle of catching only those exceptions we are aware of, then we need to know about what exceptions can be thrown from a particular API call. Which potential exceptions mean that “we failed to read a file”?
One would think that we can handle FileNotFoundException and maybe a couple of others. After a week or a month, a user will see either suddenly closed application, or a message like “Something went wrong, sorry. Call to someone who can help you.” The irony here is that no one can even enlist all potential exceptions from a particular API call, especially if that call drills down to the system, or trying to acquire system resources. The second problem with handling specific exception types is hidden by versioning of software. Clients of the first release may be sure that they can just handle exception of type A from a call to method Do(), but in the next version the functionality of Do() was extended and now it can throw exception of type B. As a result, clients are broken. These problems are the reason why C#-team didn’t implement so-called checked exceptions.

Can we just catch them all?

It’s a temptation to put balls on all that difficult stuff connected with proper handling of errors and exceptions. Despite the good chances some readers could consider me an idiot, or something like that, I’ll say that sometimes it can be a reliable and sufficient for success strategy to just catch all exceptions. In my opinion, as always in engineering, we can’t just blindly state that “we should never ever write try{}catch(Exception ex){}“. In my opinion, you should reason about your errors\exceptions handling strategy when you start developing a new application, bearing in mind a couple of things:
1. What sort of application are you going to develop? Is it a nuclear weapon managing software? Is it software for a device intended to struggle with cancer? Is it a cardboard game? Is it a notepad-like app? Obviously, requirements for reliability are different for such applications.
2. What is the proficiency level of your team? Are you absolutely sure, that your team is ready to deal with proper errors\exceptions handling? It’s very, very hard to write this kind of code. I’m not joking. At first, you have to be sure that decent professionals surround you; otherwise, it may be better to rely on the “catch them all” approach.
3. The size of an application. Well, I can’t define the upper boundary in number of code lines, when you start to suffer from the “catch them all” approach, the only thing I can say, that there are tons of good (at least not worse than other) software, which relies on “catch them all” approach, or plain error codes. Now, I participate in a project where we use “catch them all” strategy; our solution consists of over 100k lines of code and we don’t suffer much (we suffer from other things, rather than improper exceptions handling).
So, generally speaking, you should think at first about whether you can afford yourself the easier life, or not. The choice is up to you. There is no single right answer for all cases.

Summary

Well, I hope now you realize the extent of the problem. Actually, no one knows how to do errors\exceptions handling in a good, painless way. As always in engineering, there’s just not a silver bullet.
At first, I thought that I would say all I want to about this problem in a single article. But now I feel that this is enough just for the intro. The most useful info from the practical perspective dedicated to handling errors\exceptions will be considered in the next part. Stay tuned.

P.S.
A couple of interesting links concerning the discussed problem:

P.P.S.
You can also read my article regarding WPF exception handling.