I often found teams convincing themselves that they don’t do flow control using exceptions, because they don’t do constructs like a ResultException as described in Ward Cunningham’s wiki.

Things may not be that bad in most of these teams.

But I’ve seem exceptions being used in ways that are very close to flow-control, especially when the presence of said exceptions is used to trigger complex business scenarios.

You might have also seen that this indeed an oft-used method, so what is so bad about doing using exceptions this way?

Rampant use of exceptions in your code-base can make it difficult to read and understand. Remember that every catch increases cognitive complexity.

My personal preference when it comes to exceptions was always this: exceptions should be used only when something exceptional happens. In other words, throw an exception only when you need to make a fast exit from something bad that has already occurred.

Image for post
Image for post
A quick exit from the building [Image by Aintschie from Pixabay]

For instance, if the business function being executed is one to capture a payment, and if some dependency required to fulfill that function is unavailable (like the external payment gateway is down) — throw an exception.

Another solution would be to avoid exceptions altogether, and choose another mechanism like a Result object.

However, I’ve not seen every team eager to adopt a new concept like a Result object. Also consider that it’s not very easy to migrate existing code either.

So this got me thinking. Are there a set of guidelines that we could follow that make the code better readable while still using exceptions?

As such, I tried to prepare a list.

Basic Guidelines

Handle exceptions thrown from third party libraries close to where they might be thrown. Do not handle that exception further downstream.

If we let an exception thrown by an third party propagate to all layers of your code, it increases the coupling to that library.

This can also raise subtle, uncaught errors when you switch the library to another one, if it is ever required.

This is fairly similar to the problem above, but I mention it as a separate point because it is something that I have seen so often.

For an example, avoid handling a database specific exception thrown from the database layer in the presentation layer.

If you throw an exception in your code yourself, handle it just once.

This should be done only in one location — hopefully a dedicated ExceptionHandler class, if you come from the Spring world.

The reason for this advice is due to the very nature of exceptions, that they can function as non-localized goto statements.

As such, I would like to downgrade the problems it can raise, by having a single place to look at where an exception is handled. For a future maintainer, this is easier than having to go through a the complete execution chain to understand what happens.

This paradigm might raise some interesting problems to solve too.

We often need to catch exceptions that we have thrown ourselves, maybe update a metric or two, or a database entity, and re-throw the same or (even) a different exception so that some code downstream can handle it.

There are ways to do the above things that might involve not using exceptions in the first place, or even revising code architecture so that you satisfy the paradigm that you handle your thrown exceptions just once.

That often is not easy, but it will pay off in future when the next person tries to read and decipher what is happening.

If you are writing a method that throws an unchecked exception, please document it. Simple JavaDoc comments will do the trick. A method that does not document the exception(s) that it throws lies about it’s contract.

Help the future maintainers of your code. If they do not have this documentation, it could be that have no idea that it throws an exception in the non-happy path case.

What usually follows is that they either

  • wrap all the code calling this method with try and catch blocks, making the code unnecessarily verbose (or)
  • forget to catch the exception, and might end propagating the exception to a downstream layer.

Both, bad places to be in.

Conclusion

These were fairly basic points that came to my mind while working on a recent project, and thinking about ways to write more maintainable and easily readable code.

Learning to be a better developer everyday.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store