Tony Hoare invented the null reference in 1965 while designing ALGOL W. In 2009, he called it his “billion-dollar mistake,” estimating the cumulative cost of null-related errors across the industry. The remarkable thing about that apology is not the remorse. It is that the fix was already well-understood before he made the mistake, and we have spent six decades largely ignoring it.
The software industry has a strange relationship with known problems. We treat null pointer exceptions as a fact of nature, like weather, rather than a design choice that could be unmade. They are not. They are a choice, made repeatedly, by language designers and engineering teams who reach for familiar tools over correct ones.
The Type System Already Knows How to Handle This
The solution to null is not careful programming. It is not better code reviews, more unit tests, or defensive null checks scattered through your codebase like sandbags before a flood. The solution is a type system that makes the absence of a value explicit and forces you to handle it before the compiler will let your code run.
Haskell has had Maybe since the 1990s. Rust has Option. Swift has optionals. Kotlin has nullable types. In all of these languages, a variable that might not contain a value has a different type than one that is guaranteed to. The compiler refuses to compile code that treats a potentially-absent value as though it is definitely present. The problem does not disappear at runtime because it cannot reach runtime unexamined.
This is not a novel insight. This is type theory from the 1970s, applied. The fact that Java, C, C++, and JavaScript, which collectively run the majority of production software on earth, spent decades either without this feature or with it bolted on as an afterthought, is a policy failure dressed up as a technical limitation.
Adoption Has Been Embarrassingly Slow
Kotlin made nullable types a first-class language feature when it launched in 2011 and reached stability in 2016. JetBrains built it specifically to address Java’s null problem, among other things. Google adopted it as a preferred language for Android development in 2017. The response from the Java world was largely to add @Nullable annotations, which are hints to static analyzers, not enforcement by the compiler. They are the type-safety equivalent of a “please drive carefully” sign.
Rust went further. Its ownership model and Option type make null not merely avoidable but structurally impossible. You cannot represent a null pointer in safe Rust. The language was designed around the premise that if the compiler cannot verify safety, the code should not compile. The result is a language where entire categories of bugs are not bugs you have to remember to avoid. They simply do not exist.
The engineering cost argument against these languages does not hold up at scale. A null pointer exception that reaches production does not cost a few hours of a developer’s time. It costs incident response, customer trust, and frequently cascades into failures that take down surrounding systems. The real cost of cheap engineering decisions compounds in ways that are invisible until they are catastrophic.
The Null Check Habit Is a Cognitive Tax
Beyond crashes, null-permissive languages impose a continuous mental overhead on programmers that rarely gets accounted for. Every function call in Java or C requires a judgment call: could this return null? Did the original author intend for this to be nullable? Is there a contract somewhere in the documentation, probably outdated, that clarifies this?
This is not a small burden. It is the background radiation of working in these languages. Experienced programmers develop habits and heuristics. They write defensive null checks in some places and skip them in others based on educated guesses. They get it right most of the time, and the times they get it wrong become the incidents that page someone at 3am. The whole system depends on programmers remembering to do something the computer could simply require.
Good languages do not ask programmers to remember things that can be enforced. That principle applies to memory management, and it applies to nullability.
The Counterargument
The most common objection is that Option types and their equivalents add verbosity and ceremony to code. Unwrapping a value in Rust or pattern-matching on a Maybe in Haskell takes more keystrokes than just using the value. This is true. It is also the entire point. The ceremony is the cost of acknowledging that the value might not be there. You are not fighting the type system. The type system is correctly characterizing reality.
The second objection is legacy. Most production systems are not green-field Rust projects. They are Java codebases from 2004, C++ systems that predate smartphones, JavaScript that was written before ES6. Migrating is expensive. This is also true, and it is a real constraint. But it is an argument for migrating gradually, not for treating the current situation as acceptable. Companies make this investment. The organizations that have moved critical systems to memory-safe languages with strong type guarantees do not typically wish they had stayed with C.
The third objection is that skilled programmers handle null correctly. This conflates individual performance with system reliability. Skilled programmers also get tired, join new codebases without full context, and work under deadline pressure. The engineer who finds the bug often has no idea why the original decision was made. Systems need to be correct when humans are imperfect, which is always.
We Know Better. We Are Choosing Otherwise.
Hoare apologized for null because it introduced ambiguity into systems that should have been unambiguous. He was right to apologize. The more uncomfortable truth is that the industry heard the apology, understood the technical solution, and continued writing nullable languages, choosing nullable defaults, and deploying nullable code into systems that cannot afford to crash.
This is not a hard problem. It is an old problem with a known solution that requires accepting short-term friction in exchange for long-term correctness. The languages that have made that trade are not exotic research projects. They are running browsers, operating systems, and financial infrastructure.
Sixty years is long enough to call this what it is: a choice we keep making badly.