Complexity Will Haunt Your Entire Career
I've gone back and forth over the years trying to think of the most valuable attributes of software I love working on. Some candidates:
- Correctness. This would clinch the top spot if it wasn't so elusive. Correctness is a fleeting epiphany, a state that tends not to survive the real world.
- Performance. This just hasn't been that much of an issue in my industry.
- Security. The high-karma answer but realistically most of the effective measures I have taken to protect systems over my career have been operational.
Over time I have to give the nod to simplicity. Simple software solves a well-defined, presently-understood problem in the most direct manner possible. Simple software doesn't try to outsmart the future. Simple software doesn't have an agenda. Without simplicity, no other positive attribute seems attainable.
Despite this, simplicity is almost nowhere to be found in our industry. Why is that? Ultimately I believe it is because our industry values intelligence over experience. Our industry fetishizes analytical skills over hindsight. Pervasive ageism drives experience from industry. Well-intentioned but inexperienced developers repeat mistakes of the past because they have an irrational faith in analytical techniques.
(Coincidentally my obsession with simplicity is also why I continue to be a huge fan of C. Not because C is simple - quite the opposite. C is so awful that no one will want to write any more code than they possibly have to.)
Complexity is the opposite of simplicity. Here are some of my favorite complexity antipatterns:
- Microkernels. My all-time favorite. I see it once approximately every five years in a major project. An intermediate developer will ultimately decide that the best way to build an extensible system is to have a small "kernel" pass "messages" between "tasks". Awesome! Why didn't I think of that? We'll never have to write this (or any other) system again! This is the express train to spaghetti architecture. One of your esteemed coworkers is planning this approach right now...run!
- Queues. Queues are a great idea when you are collecting analytical records than can be dropped and consistency isn't important. Most of the time queues just introduce a pointless air-gap between functional units that makes debugging harder. You're not LinkedIn, stop trying to make everything look like Kafka.
- Event Sourcing. Where aspects of the microkernel philosophy mix with queue-based architectures. All of the fashionable checkboxes of modern systems development are ticked: Modular! Immutable! Reproducible! At the highest tier this model may make sense, but chances are most people implementing this are just overengineering a jenga architecture when a simple vertically-scaled relational database with some caching would have been perfectly adequate.
- Concurrency. I blame Go for this. Why write a program when you can write an operating system? Except nothing about the inner workings of your "operating system" is visible to your toolchain, and your whole program is only as durable as its weakest goroutine. This one will get much, much worse.
- Symbolic Abstraction. In the right hands, abstraction is quite helpful. Unfortunately, most developers end up creating meaningless schemes riddled with types like "Entity" and "Thing". Everything is an Entity! All of our problems are solved!
It isn't just your local Architecture Astronaut who is a big fan of these antipatterns - the executives of AWS, Azure and Google Cloud are also big supporters. They know that these antipatterns will eventually cause more project failures that drive migrations to their pre-baked solutions.
last update 2019-02-03