Exception handling in Java is crucial for reliability, but a common source of bloat & prone to miscoding. However, dealing with exceptions correctly can be surprisingly easy.
We cover 10 “foundation” best practices to simplify your coding & help you handle exceptions correctly.
1. Prefer ‘throw’ to ‘catch’
Principle here is that, in general, most exceptions are unrecoverable from your code. Your code does not (should not) have the ability, or specific knowledge, to repair the network or restart the database. That would violate encapsulation.
Given that, the responsibility of your code is to fail immediately & safely, and report an error. The critical danger to avoid is allowing code to continue with incorrect/ erroneous data, and save or output false results to the business level.
2. Categorize exceptions by cause
Since exceptions come with a stacktrace, it is more useful to categorize them by subsystem causing failure, rather than by which application module the failure occurred in.
For example, categorizing exceptions into SQL, file access, or configuration types is generally far more useful than separate types for Customer, Account, and Order modules.
Keep the heirarchy simple. For the vast majority of applications, the application module which failure occurred in is irrelevant — no practical difference in handling or reporting would be made.
It is useful, however, to distinguish major underlying causes of failure/ and different degrees of recoverability.
3. Keep your exception heirarchy simple
A simple hierarchy is easy for developers to use & throw, making it obvious to find “the right exception”. It should offer basic broad categories for diagnosis & handling.
Overly complicated hierarchies, non-obvious naming, or per-module exceptions leave developers scratching their head looking round for what to use. (This is why people end up using RuntimeException). It shouldn’t be that hard.
Library code which is genuinely separate from the application body may deserve it’s own exceptions, but don’t go overboard.
A clean & effective hierarchy may look like this:
- FailureException extends RuntimeException
- RecoverableException extends RuntimeException
In this hierarchy, exceptions a developer needs to rethrow will be under an “App*” exception classname. This makes it easy for developers to find & throw the correct type.
4. Catch at the outermost level
Exceptions must be reported to the business/ external world — and this is done by returning a 500 “error response” or displaying an error in the UI.
Since such an ‘catch’ block or exception-handler requires access to the outside to respond, it means exceptions must be caught & handled at the outermost level.
Simultaneously, the final handler should log the exception & full stacktrace for investigation.
‘Catch’ blocks in internal code should be avoided and their use minimized as they interfere with the reliable propagation of exceptions to the outermost handler.