Pre-ramble
I have a post in my drafts that I started writing on which has some frequently used phrases to keep bad code to a minimum. I guess you've heard at least some of them: DRY (don't repeat yourself; write one routine instead), YAGNI (you ain't gonna need it; write the feature only once you need it) and the like. One phrase I have been been thinking especially much lately about is "premature optimization is the root of all evil," and this will be the topic of this blog post. This just might evolve into a series. It's not like I'll make any huge advances in the documentation any time soon anyhow...
I first heard the phrase in question quite recently, actually – couldn't be more than a few months ago – at my current workplace. In hindsight, I'm pretty amazed that I've managed to miss it for such a long time, because now that I heard it, I'm starting to see it being used everywhere. It's not like it'd be new a phrase, either; according to Wikipedia, it was popularized in 1974 by Donald Knuth. The way I understand the point in the phrase is that if you optimize the wrong thing or the wrong way, you might be just wasting your time, because it never would've became an issue in the first place. Maybe even worse; what would've perhaps been faster in the average case, actually performs slower than it would unoptimized, because the cases your code runs are not the "average" ones.
I'm not opposing the message of this phrase, but this post would be superfluous if I'd let it rest at that and let it be at that. No, I do have my objections relating to how it's being used. Encouraged by the unexpected corroboration in the Stack Oveflow podcast #47, I decided to write a whole blog post about just this one topic.
Root Cause
My problem with the common usage of the phrase "premature optimization is the root of all evil" is that it's too easy to be misused and misunderstood. It's too easy to hear "just friggin' code it! If it's slow, we'll fix it later." It's all too easy to understand it suggesting to code what's written fastest at first, and if it doesn't work, track back, and do it properly later. This is is a very dangerous interpretation, that might destroy the project's future. There are at least three dirty things that pop right out.
There Will Be No Spoon
Starting with the easiest, yet hardest truth: There's no "later". Face it—That tweak time that you have scheduled at the end of the project? It won't happen. A very cruel, yet realistic in my view, thought on the matter is Hofstader's Law: "It always takes longer than you expect, even when you take into account Hofstadter's Law."
I mean, really, how many projects with a deadline have you worked on, where the project was done before the deadline, with a decent margin, so that you just spent the rest of the days playing Rock Band? I guess not that many. The allured tweak time will be consumed by fixing those contract-busting bugs, or the last-minute features that "simply need to be done".
There's no time like the present.
The Spoon Currently isn't
Not entirely unrelated, the next noticeable red flag is the sound of overall hurry and a distinct chaos that comes with it. This smells like a poorly planned project. So many projects think of bugs as unexpected faults in the program. While bugs certainly are faults, and the individual bug might be unexpected in its respective location, it should never come as a surprise that code has bugs. So why not take them into account, when planning the schedule?
I've recently fallen in love with Scrum. Without going through the particulars, it's a divide and conquer method of planning projects with rapid iterations. In other words, some of the main points are breaking larger problems into smaller and smaller pieces. These small pieces are then dealt with in periods of a few weeks. At the end of each period you have something that is fully functional (as in, you can play around with it), and, little by little, you build new working stuff on top of old working stuff.
One of the strengths with Scrum is that it keeps the client involved in the projects and informed of the state thereof. If a serious bug surfaces at some point in time, the customer gets to know about it in a way or another. Severities are explained, priorities are shuffled, "mustnesses" of features are re-evaluated.
Time is made, not found.
Just Forget the Damn Spoon!
The most prominent feature in that interpretation, to me, is related to the previous note: The general rushing, and a feeling that the speed of cranking code must be maintained. The feel that there's no time to think, because the code needs to be typed ASAP, preferably yesterday. So we plan to do the major features first. Not to perfection, mind you! Just finished enough so that they work in our project. After all, once this feature is done, we need to continue on the whizbang. Because that blocks the wheybow, which in turn is required by the boola. And we can't ship without the boola, can we?
If you've ever heard the phrase "penny wise and pound foolish", this is a pretty good definition of it. You are so fixed in the short run, that you get tunnel vision and can't see the bigger picture. You think you can't afford wasting time thinking ahead, because thoughts don't have fingers to type code with. This is the numero uno reason why unit tests and technical documentation are rarely done at all – you can't sell tests or specs to your customer, so why bother in the first place?
If you hate the code you're looking at at the moment, someone, somewhere, has been awesomely penny wise. It just better not be you.
Literally
Thus far I have criticized one of the many ways the original phrase could be interpreted. If you think that there's no way to interpret that phrase in such a horrible manner, I salute you. You make me all warm inside, and I wish the best of luck in your life. The rest of you, especially those who didn't see any wrong with the interpretation above, please read onwards.
For illustrative purposes, let me write the quote once more:
Premature optimization is the root of all evil.Ladies and gentlemen, the key word within the phrase is "premature"! It can't be emphasized too much: premature. Here's how OS X's built-in dictionary defines the word:
pre-ma-turePremature. Something before its due time, something not to be done just yet. Let's look at the quote from another angle:
adjective
occurring or done before the usual or proper time; too early : the sun can cause premature aging | [with infinitive] it would be premature to do so at this stage.
If the time is right for optimization, it's all good.If it's suitable to optimize here and now, do it. Optimization isn't premature, if it's needed, no matter when you code it. If your specification says that a certain heavy routine will be run often, but the return value never changes, there's no reason you shouldn't cache the result. You don't have to wait for a rewrite to make truly beneficial optimizations.
It's good that we have a solid definition of the word. The bad, however, is that we don't have a solid definition of what is and isn't premature. This is where your experience as a programmer comes into play. You have a gut feeling, and you should definitely listen to it. If your gut feeling was wrong, excellent! You now have an opportunity to learn something of it all.
That being said, I do think that it's better to err on the unoptimized side, than to optimize 'just in case'. It's always easier to optimize clean code later, than de-optimize the clever code you wrote.