What if the programming industry’s decades-long obsession with type checking is solving the wrong problem entirely? What if our increasingly sophisticated type systems—from Haskell’s category theory to Rust’s borrow checker—are elaborate workarounds for fundamental architectural mistakes we’ve been making since the beginning?
The software industry has convinced itself that type checking is not just useful, but essential. We’ve built entire programming languages around the premise that catching type errors at compile time is one of the highest priorities in software design. We’ve invested countless person-years into developing ever more powerful type systems, treating each advancement as unquestionable progress.
But step back and ask a simple question: Why do we need type checking at all?
The standard answer is scale. “Small programs don’t need types,” the reasoning goes, “but large programs become unmaintainable without them.” This sounds reasonable until you realize what we’re actually admitting: that we’ve designed our systems to be inherently incomprehensible to human reasoning. We’ve created architectures so tangled, so dependent on invisible connections and implicit behaviors, that we need automated tools just to verify that our programs won’t crash in obvious ways.
Type checking, in other words, is not a solution to complexity—it’s a confession that we’ve created unnecessary complexity in the first place.
Consider this: electronics engineers routinely design systems with millions of components, intricate timing relationships, and complex interactions between subsystems. Yet they don’t rely on anything analogous to our type checkers. They use different architectural principles—isolation, explicit interfaces, and time-aware design—that make their systems naturally more robust and understandable.
The problem isn’t that software is inherently more complex than hardware. The problem is that we’ve chosen abstractions and architectural patterns that create artificial complexity, then built elaborate tooling to manage the mess we’ve made.
The Accepted Wisdom
Walk into any software engineering discussion about large-scale systems, and you’ll hear the same refrains: “Types catch bugs before they reach production.” “Static typing makes refactoring safe.” “You can’t maintain a million-line codebase without a strong type system.”
These statements aren’t wrong, exactly. Within the context of how we currently build software—with sprawling inheritance hierarchies, deeply nested function calls, and invisible dependencies threading through dozens of modules—type checking does indeed catch bugs that would otherwise be painful to debug. The Rust compiler’s borrow checker does prevent memory safety issues that plague C programs. TypeScript does make JavaScript codebases more maintainable.
... continue reading