Time to read
, 1136 words, 4th grade
We humans have a marked tendency to take things too far.
Itʼs natural. Power awes us, whether it comes in the form of money, prestige, raw physical power, or high tech. Who wants a three-bedroom house when you can have a twelve-bedroom, fifteen-bathroom mansion? Am I right?
Even if all you honestly need is a studio apartment. Or a tiny house. Or a yurt.
I see this also in industry, and particularly in tech work. And Iʼve made this mistake myself, when I was young(er) and somewhat naïve (read: dumb). I remember a decade or so ago having a smart young friend over for drinks. I could hardly wait to show him the absurdly over-engineered app Iʼd built.
Imagine my chagrin when he said,
Oh, Charles! Never write a line of code until you have to! 1
That simple admonition blew my mind. Everything changed for me that day. Iʼd been teaching “just-in-time” for more than a decade, but Iʼd been coding “just-in-case.”
In fairness to myself, I knew that I was over-engineering. I did it on purpose.
At that time I worked for small businesses as the sole dev on contract. If I wanted to learn enterprise systems, then Iʼd either have to give up my small company and go work in enterprise (as eventually I did), or over-engineer.
Of course, I did all the over-engineering on my own time, spending hundreds of hours per year on it.
Some people think Iʼm nuts. They may have a point.
At first I interpreted this as telling me when to write the code. After a while I realized that it was also telling me how to write my code. Or rather, how not to.
That moment was quite literally the turning point in my career. I pivoted 180° and turned everything inside out.
Abstraction can be useful because it amplifies power. But the price is often complexity and mystification. In programming, we tend to abstract too early and too often.
But as my friend Guillermo might have said to me: never abstract until you have to.
That means that if you have only one instance of a particular widget or algorithm, then make it fit for that specific purpose and no other.
If you have two very similar widgets, hmm. Probably still not worth abstracting the pattern out. Despite what some famous programmers say, there is nothing wrong with copy and paste. Actually, it is quite efficient, if you do it right.
Got three, four, or more very similar widgets? OK, now itʼs time to abstract … maybe.
We are so afraid of “repeating ourselves” that often we will abstract everything.
Not long ago, I suffered the agony of working on a code base in which the devs had abstracted to absurd lengths. Seems one of the devs was quite enamored of composition. Over-enamored. The consequence? Nothing was simple or straightforward.
Functions wrapped in functions wrapped in functions wrapped in functions. All to make things “easier”.
Ha ha. You canʼt make this stuff up.
Figuring out how a very simple feature worked took me the better part of an hour. What should have been a few lines of code in one file had grown like a pernicious weed. Now it spanned a hundred lines or more over eight files.
It was like being in a horror movie. Or a Kafka novel, if youʼre the literary type.
This pattern repeated itself throughout the app. A very simple and discrete application swelled into a monstrosity. All this to make it “more configurable and reusable”. Ha ha. Kill me now.
But nothing was ever reused. They could have avoided all that configuration as well. All they had to do was make it fit-for-purpose.
And, of course, they had time to write spaghetti code, but never time to document it.
We must remember that code is often rewritten. Are you sure youʼre going to need those superpowers before the code is obsolete or replaced?
And my over-engineered app of which I was so proud? Like so many others that had preceded it, it was obsolete long before I ever needed any of that extra power. Actually, I spent so much time over-engineering it that it never went live. Doh!
Keep it simple, stupid is an old principle but a wise one. People have been saying that to me since I was a child. Not sure why it took me so long to catch on.
The Principle of Least Power says donʼt use a flamethrower when a Bunsen burner will do. Less is more.
Inspired by Tim Berners-Leeʼs Rule of Least Power, it states:
- Do not obsess over DRY (Donʼt Repeat Yourself). Try AHA (Avoid Hasty Abstractions) instead.
- Add external dependencies only when you need them. Then add only what you need, no more.
- Add new features only when enough users request them to make them worth it.
- Write no code until you must, then write only what you need. Practice just-in-time coding.
- If there are two or more equal solutions to a problem, then choose the simplest one. William of Ockam insists!
Over-engineering and over-building wastes time, money, and effort. So does feature creep. Build things just in time and just enough. Do we really need a chainsaw? Or will this paring knife do the job?
This bad behavior is particularly rampant in “enterprise.”Nothing gets a manager more excited than complex systems. They like to pretend that theyʼre running Google, it seems.
But Google processes roughly 100,000 search queries every second. Iʼve worked for companies that were lucky to get that many hits in a month. So of course they want to emulate Googleʼs processes. Itʼs insanity.
At Craft Code, we embrace exitprise instead. As in, Iʼm outta here.
Let us then reiterate the least power principle:
- Keep your code as simple as you can.
- Use the fewest dependencies possible. Zero would be nice.
- Split your apps up into decoupled components with intelligent boundaries. (More on this in a future article.)
- Avoid rushing to use the latest cool toy — you donʼt have to be cool (to rule my world).
- Write the code yourself whenever possible.
Youʼll reduce tech debt to near zero. Your dependencies wonʼt hold your code hostage. Your attack surface will be much smaller. And youʼll know your own code base inside out.
Try it and see for yourself.
1. The smart young man was a guy named Guillermo Rauch. He has done pretty well for himself since. Good advice, I guess.