null is probably the worst mistake in programming languages history and has spread in all modern languages since its invention in 1965. Many years later, Tony Hoare, the inventor of the null reference, even apologies for his mistake:
I call it my billion-dollar mistake … My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.
In this article, I will explain how
null affect code integrity and how we can avoid using it and most importantly returning it to improve code reliability.
What is wrong with
Well, let’s give it a chance and see what happens. Imagine an e-commerce app allowing customers to get one discount each month. Such a requirement could have been implemented like so:
Notice that if the monthly discount has already been used,
null is returned.
From the implementation point of view, this is pretty obvious the result can either be a
null, but what about the usage point of view?
As you can see from the usage point of view, it is not that obvious. Of course, there is a chance you know
null can be returned, because you implemented both sides, you spent much time analyzing each used method or by some miracle, someone decided to write documentation about it. Let's pretend it is the case for now so we can complete the above method:
Great, now we handle null reference exceptions, that was easy! Well, think about what could have happened if the “one coupon by month limit” requirement was introduced after the first attempt of
ApplyMonthlyDiscount. In that case,
GetMonthlyDiscountCoupon could have looked like this:
ApplyMonthlyDiscount could have never check for null references.
Does that means we should always check for null references based on the fact that methods can change and return
No, as you can imagine this could quickly become a mess of null checks making the code lease readable and hard to follow.
Establishing the rule
A better solution is to establish an important programming rule across your team. This rule is simple:
Of course, some verifications will have to be done to enforce this rule such as making it part of code reviews of enabling some language features like the nullable reference types in C# 8.0 or the
strictNullChecks flag in TypeScript.
Now that the rule is set, we can take it for granted and get rid of these annoying null checks!
Still, be aware that method arguments should still be validated. Values coming out can be controlled but values coming in cannot. Take a look at guard clauses for that.
Now what if we must return something that is really optional like in our discount coupon example, how do we do this without breaking the rule?
Special case pattern
One way to avoid returning
null is to return a concrete implementation of the returned type that does nothing. This technique is known as the special case pattern. Here is an example from our previous scenario:
The important point here is that the special case class should be able to be used like any other implementations:
This technique is very simple to understand and implement and should be used when possible, but in some scenarios, it cannot be used. Let’s tackle one of these with a more complex but more powerful approach.
Optional object pattern
In some cases, the above solutions will not work and we will have to pull out more power and use the optional object pattern. Imagine we want our previous discount coupon to have a code and be attached to the order. In the previous special case implementation it does not make any sense:
It does not look so bad, but it does not make much sense to attach a coupon with an empty code to the order:
A better solution is to return an optional object from
GetMonthlyDiscountCoupon and to turn
Order.Coupon into an optional as well:
With this in place, the caller cannot access the coupon value directly and so is forced to make a decision:
Unfortunately, unlike Java, C# lack the
Optional<T> type. Implementing such a pattern is out of the scope of this article but you take a look at Zoran Horvat's great implementation.
But remember with great power comes great responsibility. This means implementing this pattern also means maintaining additional code.
An alternative would be to use an open-source implementation such as Optional in C#.