Null, Validations and Exceptions
Engineering means coding. However how many times do we consider proper error handler? Most of the languages like Java, .NET, Python, C++ can be a very trick in the sense of handling errors. Business validations are like tests but they are hardcoded on the software and run part of the main flow of our applications. We need to worry not only with the "Happy Path" but also with the "What if" moments where things might not happen as we expect. There are many different opinions and options around in sense of validations, exceptions, and error handling especially in a language like Java. Error handling, Exceptions, and validations are a critical part of making systems more reliable and hardening production I would argue. Amazon Builder library talks about that in the context of avoiding Circuit Breakers and hardening the main execution path and make running code more reliable.
Null The billion Dollars Mistake
So if you did not watch, you should watch this video from Tony Hoare. Long Story short, Null is dangerous and often could means 2 things (error or empty value) which is tricky by itself. We should avoid using null, however in most languages, null is part of ecosystem libraries and frameworks. Another issue of Null is that when you company code grows you end up allowing null for backward compatibility(happens a lot). If you are building and designing a shared library either you get it right from the getgo or you might have lots of refactorings to do in order to improve things.
Java has the @NotNull annotation. Which is fine but IMHO everything should be NotNull by default and that's one interesting thing about Kotling where the default is different them java. Go language does not have Null as well. How could we about null? Validations are the answer! When you validate you can return some default value or Exception depending on the Case. Look google Gson lib for example.
Validation First
I learn to code with the principle of validations first. The first thing you do in any method or function is to validate the parameters. That's also the OOP Constructor rule. When you apply validations first you can avoid all sorts of errors and makes the usability of a library/service much better. One approach you could do is the Notifications Pattern. Several libraries do validations in a central and generic class. Validations are also often called Pre-Conditions(Look Guava for instance), Assertions, and Checkers. Java also(since java 7) has the Objects(Sample code in Apache Commons) that makes it easier to do validations.
Exception Best Practices
Effective Java(3rd edition) is a great book to get more from Java. There is a section just about proper exception which can be summarized as:
- Item 49 - Check Parameters for Validity
- Public/protected methods use @throws on Javadoc
- Objects.requireNonNull
- @Nullable when the parameter can be null
- Non-public method(private) can check parameters with assertions (assert)
- Item 69: Use Exception only for exceptional conditions (Dont use for ordinary control flow)
- Item 72: Favor the use of Standard Exceptions
- Item 73: Throw Exceptions appropriate to the abstraction (High level would catch lower level)
- Item 74: Document all exception throw by each method (/** Javadoc @throws */
- Item 75: Include failure capture information in the detail message
- Item 76: Strive for failure atomicity (Generally Exception should leave the object how it was before the exception)
- Item 77: Don't ignore Exceptions
Exceptions should be used to blow the program not to control execution flow. You might realize some item numbers are missing on my list. That's by design because I also dont believe in Check Exception and like many I do believe they are a design mistake.
Other Available Options
If you are into Annotations, one interesting approach for you might be Hibernate Validation Exceptions. Which can be interesting for Domain Validation however annotations often are super abused in java. If you are into Spring, there is a validator there.
Functional Programing and Modern Ways to handle errors
For functional programming we have the Monad Maybe also know in java as Optional which IMHO works much better than returning null and much better than returning exceptions. If you look Go language for instance you are forced to deal with errors after each function execution. Which is a interesting and very simple way however it can get a bit tedious.
An exception should be used only when you want to blow the program and something hard to recover just happen. In most cases, you should be returning default values or using Optional. These practices will make you code not only easier to read but also more reliable and solid at the long run.
Cheers,
Diego Pacheco