I once worked on a new project where we decided to define custom exception types for different exceptions that occurred in our domain. At the end of the project I was convinced that the work required for custom exceptions does not yield the benefit I once thought it did.

Custom Exceptions and Program Flow Control

I began to notice that the code we were writing was using exceptions as program flow control. Exceptions should be used for… well… exceptional cases. However, we were using them for arguably non-exceptional cases. Things like CustomerLateOnPaymentException began to be used for flow control. Exceptions are expensive and using them for program flow control comes at a cost. Code that is littered with try/catch statements also has a tendency to obfuscate intent.

Where Custom Exceptions Make Sense

There are times where custom exceptions make sense. Let say that you have a function responsible for saving a file to a network drive. There are two exceptional cases worth highlighting here: the drive is unavailable and you do not have access to write to the drive. A drive being unavailable may be an ephemeral error, whereas the access error is probably more permanent. It would be nice to know which exception was raised from this function so that you could handle it accordingly. If it is the DriveUnavailableException, you may want to retry the operation after waiting for a few seconds. If is the NoAccessException, chances are you won’t want to retry. Being able to interrogate exception types in this scenario will enable you to provide a better experience for your users.

Use Them Sparingly

All things considered, I’m gun-shy when it comes to custom exceptions. My default is to not use them but I will apply them in a few cases.