Complication is What Happens When You Try to Solve a Problem You Don't Understand
Code should be simple. Code should be butt simple. Code should be so simple that there’s no way it can be misunderstood. Good code has no nooks. Good code has no crannies. Good code is a round room with no corners for bugs to hide in.
We all know this. So why does most code suck?
Because it’s written by people who don’t understand the problem they’re trying to solve.
What is a Program
To make a gross oversimplification, a program is nothing but a model of things (for the sake of discussion, call them “objects”) and rules for how those things interact with each other.
A man, on the other hand, is nothing but a miserable little pile of secrets.
A factorial program is nothing but a group of objects (those “integer” things), and a rule that turns one integer into another (the factorial function). A word processor is nothing but a group of objects (the “alphabet”), and a bunch of rules that describe how those letters can be combined and displayed on a page. And a social network is nothing but a group of objects (“people,” usually “idiots”) and a bunch of rules about how those people can do stuff to annoy you.
I may be an introvert.
Anyway, these objects and their associated rules should be very simple. In fact, as Einstein pointed out, these objects and their associated rules should be made as simple as possible, but no simpler. As the model needs to be able to do more and harder things, the objects and the rules will start to:
- Increase in number
- Remember more data
- Gain more and more corner cases
This gradual accretion of nuance and behavior is called “complexity.”
Einstein, Making Shit Simpler
Complexity? But This Blog Post is About Complication, You Moron.
So, remember a minute ago when I said ‘This gradual accretion of nuance and behavior is called “complexity”’? Well… I lied.
But just a little.
In reality, this gradual accretion of nuance and behavior from none at all up to and including the minimum possible simplicity is called “complexity.” Any incremental nuance and behavior above and beyond that minimum is “complication.”
Complexity is a necessary evil when building systems that do anything useful. If you’re doing anything more complicated than putting
Hello, world! on the screen, you’re going to need some complexity. (And, depending on the language, you’ll need some complexity even then.) Complication, on the other hand, is the bane of programmers’ existence.
When you pick up a new code base and it’s a gordian mess of 1,000-line functions, 10-deep if/else ladders, and — shudder — gotos, you’re bearing horrified witness to a monument of complication. And when you start adding to your own code things like haphazard conditions, or duplicated, slightly different exceptional cases in 6 different layers of your model, or generally making any change to your program just hoping that it will work this time for the love of God without understanding the changes you’re making, you’re worshipping at complication’s altar.
So what’s a dev to do?
Ultimately, a programmer’s job is less to actually write code, and more to manage complexity. Obviously you need to build features and meet deadlines, but the code itself is incidental. Hypothetically, if you could build features without writing code — such as by making a configuration change — then you should. When you do have to write code, though, it’s your job to write the simplest possible code as much as it is to build the feature at hand.
So, since a programmer’s real job to manage complexity, there’s only one thing a developer can do in the face of complication:
It’s always bothered me that you can simplify “Simplify, simplify, simplify”
A good developer has a natural, almost visceral aversion to complexity. A good developer smells complexity a mile away, and constantly shifts the code to keep his eyes to the front and his back upwind just so complexity can’t sneak up on him. It’s only by diligently trying to avoid all complexity that one can in fact avoid unnecessary complexity.
The best way to manage complication is to avoid creating it in the first place. If you find yourself in a
mindless change →
run loop, you don’t understand your code well enough to be editing it. Stop what you’re doing, actually get up and walk away from the keyboard, think about what you’re trying to do, and don’t come back to the keyboard until you understand exactly what you’re doing and how to do it. Obviously there’s some slack here for debugging, but it’s not controversial to say that you shouldn’t change code you don’t understand, even — especially? — when it’s your own.
Unfortunately, despite our best efforts, complication always finds its way in. The best way to deal with complication that has already found its way into your codebase is to attack it whenever you find it. As you’re sitting down for a coding session and reading your code to get it back into your head, if it takes you longer than about 10 minutes to really get going, your code’s too complicated. Take the opportunity to make it simpler. (If you’re unfamiliar with refactoring, Martin Fowler’s Refactoring: Improving the Design of Existing Code is the bible. Read it, live it, love it, thank me later.) Do that every time you sit down, and before too long your code will be less complicated, and you’ll hate yourself just a little less.
NOTE: In retrospect, I realize that talking about “complexity” in a blog post that’s supposed to be about “complication” is probably a little complicated. Forgive me, gentle reader.