50% OFF - The Complete TDD course
Are you ready to master clean code, testing and Test-Driven Development (TDD)?
I recently launched a complete TDD course containing everything you need to craft high-quality software.
Now there is a 50% OFF for the course
Get instant access by clicking here.
Motivation
Stop using NULLs. It is a bad practice. I consider having NULL in a language to be a poor decision. And I am not the only one. The inventor of NULL Tony Hoare apologized for inventing NULL at a software conference called QCon London in 2009. He called it a billion-dollar mistake:
“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” - Tony Hoare | Null References: The Billion Dollar Mistake
First, let's look at the problems NULL causes, then explore solutions for the fix.
The problems
NullPointerException
Most languages have their version of NULL-related errors, such as NullPointerException or NullReferenceException. They all occur when a program tries to access an object reference that has been assigned to NULL
. Developers often spend countless hours debugging and fixing problems caused by these errors.
Ambiguity in Meaning
NULL is confusing. It doesn’t reveal any intent. Does it mean "unknown," "not applicable," or "empty"? This lack of clarity leads to mistakes. The absence of meaning forces developers to make assumptions, and assumptions are the breeding ground for bugs.
Poor Error Handling
The presence of NULL forces developers to add boilerplate NULL-checks throughout their code. These checks clutter the codebase and make it harder to maintain. It’s like wrapping every line of logic in wrap, just in case something might break.
Propagation of Errors
A NULL value, if not handled carefully, can propagate through the system, causing unexpected behaviors and crashes far from the source of the problem.
Increased Cognitive Load
Developers must always be on guard, asking, "Could this be NULL?" This constant worry takes their focus away from solving real problems.
Solutions
#1 - Null Object Design Pattern
The Null Object Design Pattern eliminates the need for NULL
checks by using a special "NULL
object" that provides safe default behavior. Instead of relying on NULL
to represent the absence of a value, we can use this pattern to simplify business logic.
For example, for specific (TEST) environments, we might not want to provide any logging. Adding a if-else
condition in the business logic would create unnecessary noise. Instead, we can create a NullLogger
that does nothing:
By doing so we can set up a default behaviour without changing the source code.
#2 - Optional and Nullable Types
Many modern languages like C#, Java, Kotlin offer constructs like Optional
or Nullable
. These explicitly represent the presence or absence of a value, making it clear to developers when a value might be missing. They also offer many helper methods, making the code more ergonomic and intention-revealing:
#3 - Fail Fast with Exceptions:
Instead of allowing NULL to propagate, validate inputs at the boundaries of your system and throw exceptions immediately when invalid data is detected. This makes issues more apparent and easier to debug.
#4 - Use Modern Languages without NULL
I am a big fan of Rust lang and one of the main reasons is that it does not have any NULL. Rust provides a native data structure called Option that encodes the concept of a value being present or absent:
This type enforces safe handling of missing values, requiring explicit handling by the developer. Rust uses this structure everywhere in their core libraries. It clearly reveals the intent of absent values, resulting in a more intuitive absent value handling.
Other modern languages like Haskell, Elm and Zig also don’t have NULLs for the same reason: NULL is an outdated idea.
Conclusion
Using NULL is a dangerous practice. While it was useful once, its downsides are far greater. Using techniques like NULL Object Pattern, Optional types, and Fail-fast validation, you can write safer and cleaner code.
Avoiding NULL-related issues is just one aspect of writing clean and testable code. My TDD course covers this and much more—helping you become a professional developer who writes robust software confidently. What you get:
The fundamentals of Test-Driven Development (TDD)
3 real-world examples in C#, TypeScript and Rust
Using TDD to design high-quality software
The two schools of testing with the 5 test doubles
Testing legacy code and refactoring best practices
Keep rocking, Dani !
NULL is trouble waiting to happen. Using Option is my favorite approach.
Simply put, Daniel.