4/08/2018

Never write "catch (Exception e)" unless you can enumerate the reasons why it is acceptable.

A try catch allows us to intercept situations in our code flow that occur somewhere down a call stack. Generally associated with exception flow but not restricted to that concept.

Primarily talking to C# (or Java) Exception semantics but applicable to other languages to different degrees. An exception is a class that inherits from a based Exception allowing it to be "thrown" for later "catching" higher up the call stack.

The "System.Exception ("java.lang.Exception") represent the top of a hierarchy under which all exception types must be declared.

So this is where the problem starts. The following code is evil and should never be used unless you understand what the exceptions are (pun intended).

Because the class Exception exists at the top of the hierarchy all exception types created for the application or system types will be caught. For example, a catch Exception will intercept a NullReferenceException (NullPointerException) what should only occur for a coding bug and never in normal production flows. All exception flows should be handled intentionally using the specific thrown class only.

One of our goals as programmers is to ensure we see all coding problems early in the coding process leaving only intentionally handled problems to show up in production. Hiding coding problems leads to production systems that blindly fail without any repercussions.

So why would you use this construct?

Control Loops

If you are writing code that exists in a root of an application, like something in a Main method, that is expected to handle all the situations that occur in a running process, then a catch Exception might be warranted.

These kinds of constructs would want to control situations like unexpected exceptions and log them for review. An exception that is not caught and ends up being thrown to the runtime may only be reported to standard out and perhaps be lost to anyone trying to determine why a program crashed.

Framework Isolation

If you are developing an application that expects to load and run a dynamically loaded 3rd party library then you will want to isolate the management of all interactions with that library. These are interactions you can not test for so protective steps are necessary to ensure predictable outcomes.

In this manner all interactions will protect the management program and allow for positive feedback to the user or developer of the dynamic library.

Thread Entry Points

The final situation that might require a catch Exception occurs when starting a background thread that throws an exception. For similar reason to catching in a control loop, the threads stack is started at a method specified at launch time and any thrown exception flows up to the runtime in the context of the thread, never returning to the launching thread.

So, if you spawn a thread and want exceptions handled in a specific manner then a catch Exception might be warranted. DotNet and Java runtimes swallow any exception from a background thread only reporting them to standard out so they may be missed during development and certainly not logged in a normal production configuration.

try { new Thread(MyThread).Start();}catch (Exception e) { // This will not catch the threads exception.}

This means that all thread starting code should wrap thread entry points in a simple management method to ensure consistent exception handling throughout the application.

Common Arguments

These basic rules are ignored for a few reasons. The most common is "I do not know what exceptions might be thrown so catch Exception makes my code safer". To begin with, it is the responsibility of a programmer to know what their code is doing, so "I don't know" is never the right answer. Secondly, you must test drive all possible outcomes from your codebase, which ensures you will know what can be thrown. Additionally, if you miss one, you want it to be made very obvious so that you can fix it as soon as possible instead of failing secretly.

Another concern is that processes should never crash in production. Unfortunately, when the catch Exception solution is used for this it also means processes ever crash in development. So instead of ensuring your program works because you intentionally designed it that way, it only secretly says up in a failing state like some diseased animal hobbling through the woods.

The last excuse excuses the practice of using catch Exceptions offering that each must result in a log message so that the problems can be fixed. This results in volumes of log messages and the inevitable conversations that start "Oh, that exception is expected, don't worry about it" as all the critical log messages are lost amongst the chaos.

If you are making these arguments today, I suggest amending your ways.