Anyone who has done any real coding is likely to have seen an exception message, and/or a stack trace which comes with it. It's usually a whole load of file names followed by a number and what, to the outside, looks like gibberish. A lot of the time these will come directly from the language, and be overly generic in nature.  It's that generic nature which custom exceptions should avoid.

I spend my days working with PHP, so will get messages such as "Trying to access array offset on value of type null". In Java, this might be shown as NullPointerException, or ArrayOutOfBoundsException. Sure, you can roughly understand what they are getting at. But the question is why it happens. That's what the stack trace is for. But, like error messages (are they evil?), the messages need to be informative.

Enter, custom exceptions

The brilliance of custom exceptions is that you can have them tell you exactly what is needed. They then suddenly become useful in themselves for understanding the errors, and why they happened.

As an example, in an application I am working on, when searching for a record in the database an empty result set is returned if no match is found. No exception, just empty. If we try and work on something we expect to be in that result set we would normally get the error "Trying to access array offset on value of type null". Instead we have a custom exception, EntityNotFoundException which we throw (and then catch to stop things breaking).

With that custom exception, the behaviour of the database abstraction layer doesn't have to change in specific cases. We tell it to send the error back up the stack where we can be informative to the user. It means that if it appears in our logs, then someone has tried to load something which doesn't exist. If something else appears, then we've already ruled out one cause.

They are easy to implement

Ok, I am specifically talking about implementing them in PHP here, as I've not created any in other languages for a long time (I did in Java at University, but that was about 15 years ago). Below is our exception class for the EntityNotFoundException - just with the actual namespace changed for anonymity.

namespace Application/Exception;

use Exception;

class EntityNotFoundException extends Exception
{
    protected $message = "Entity not found with that ID";
}

It's really that simple. Then, when we try and load a record, we use the following in our model:

$record = $this->database->findById($id);
if (is_null($record)) {
	throw new EntityNotFoundException();
}

The exception can be caught within the controller, and then a useful error message displayed to the user. It makes the whole thing a lot more stable, and the messages a lot more useful.

Exceptions aren't (typically) exceptional

Exceptions are so named because it's what happens when the regular running of a program does something outside of the normal rules. That doesn't mean they are unknown happenings. They are, in fact, quite the opposite. The fact something has been attempted which shouldn't have been, and the code is giving an error relating to that means it has been thought of previously.

The word 'exceptional' literally means unusual or not typical. This isn't the case for coding exceptions. As they can be thought of, easily predicted, and easily achieved shows they aren't exceptional by any stretch of the imagination.

With my example of the EntityNotFoundException, I would fully expect this to happen at times on any system which allows users to delete data. I would more than expect it if I had an API and had non-sequential IDs and someone tried to guess IDs to get more data than they should - I'd even return a 404 status for them to be helpful.

Summary

Exceptions are useful for knowing what is happening in your system, even if they aren't exactly what you want to be happening. It doesn't matter if they are caused by user error, developer error, or some other kind of failure - they happen! Trying to work out what happened and why can be a challenging task, but it can be made easier by using custom exceptions. They allow more meaningful error output for very little effort, and can save a lot of time trying to identify issues.