When Premature Optimization Isn’t
Earlier this month, I decided I wanted to write a post on not all optimization being premature optimization after hearing more than one developer use this mantra as an excuse for not making a better decision in the same week. Bozhidar Bozhanov beat me to it with his post Not All Optimization Is Premature, which makes some excellent but different points than I had planned to make in postulating that there is nothing wrong in early optimizing ‘if neither readability nor maintainability are damaged and the time taken is negligible.’
Bozhanov’s post reminded me that I wanted to write this post. I use this post to provide additional examples and support to backup my claims that too many in our community have allowed ‘avoid premature optimization’ to become a ‘bumper sticker practice.’ In my opinion, some developers have taken the appropriate advice to ‘avoid premature optimization’ out of context or do not want to spend the time and effort to really think about the reasons behind this statement. It may seem easier to blindly apply it to all situations, but that is just as dangerous as prematurely optimizing.
Good Design is Not Premature Optimization
I like Rod Johnson‘s differentiation between ‘design’ and ‘code optimization’ in his book Expert One-on-One J2EE Design and Development (2002). Perhaps the most common situations in which I have seen developers make bad decisions under the pretense of ‘avoiding premature optimization’ is making bad architecture or design choices. The incident earlier this month that prompted me to want to write this post was a developer’s assertion that we should not design our services to be coarser grained than he wanted because that was premature optimization and his idea of making numerous successive calls on the same service was ‘easiest’ to implement. In my mind, this developer was mistaking the valid warning about not prematurely optimizing code as an excuse to not consider an appropriate design that might require a barely noticeable amount of extra effort.
In his differentiation between design and code optimization, Johnson highlighted, ‘Minimize the need for optimization by heading off problems with good design.’ In that same section he warns against ‘code optimization’ for four main reasons: ‘optimization is hard’ (‘few things in programming are harder than optimizing existing code’), ‘most optimization is pointless,’ ‘optimization causes many bugs,’ and ‘inappropriate optimization may reduce maintainability forever.’ Johnson states (and I agree), ‘There is no conflict between designing for performance and maintainability, but optimization may be more problematic.’
Applying Appropriate Language Practices is Not Premature Optimization
Most programming languages I’m familiar with often offer multiple ways to accomplish the same thing. In many cases, one of the alternatives has well-known advantages. In such cases, is it really premature optimization to use the better performing alternative? For example, if I’m writing Java code to append characters onto a String within a loop, why would I ever do this with a Java String instead of StringBuilder? Use of StringBuilder
is not much different in terms of maintainability or readability for even a relatively new Java developer and there is a known performance benefit that requires no profiling to recognize. It seems silly to write it with String
in the name of ‘avoiding premature optimization’ and only change it to StringBuilder
when the profiler shows it’s a performance issue. That being stated, it would be just as silly to use a StringBuilder
for simple concatenations outside of a loop ‘just in case’ that code was ever placed within a loop.
Similarly, it’s not ‘premature optimization’ to write a conditional such that the most likely condition is encountered first as long as doing so does not make the code confusing. Another example is the common use of short circuit evaluation in conditionals that can be effective without being premature optimization. Finally, there are cases where certain data structures or collections are more likely to be appropriate than others for a given operation or set of expected operations.
Writing Cleaner Code is Not Premature Optimization
Some developers might confuse more ‘efficient’ (cleaner) source code with premature optimization. Optimizing source code for readability and maintainability (such as in refactoring or carefully crafting original code) has nothing to do with Knuth’s original quote. Writing cleaner code often leads to better performance, but this does not mean writing cleaner code is a form of premature optimization.
Others’ Thoughts on Misunderstanding of Premature Optimization
Besides the Not All Optimization Is Premature post, other posts on the misapplication of the ‘avoid premature optimization’ mantra include Joe Duffy’s The ‘premature optimization is evil’ myth and ‘Avoid Premature Optimization’ Does Not Mean ‘Write Dumb Code’.
Duffy puts it so well that I’ll quote him directly here:
I have heard the ‘premature optimization is the root of all evil’ statement used by programmers of varying experience at every stage of the software lifecycle, to defend all sorts of choices, ranging from poor architectures, to gratuitous memory allocations, to inappropriate choices of data structures and algorithms, to complete disregard for variable latency in latency-sensitive situations, among others. Mostly this quip is used defend sloppy decision-making, or to justify the indefinite deferral of decision-making. In other words, laziness.
The James Hague post points out one of the signs of potentially having crossed into premature optimization: ‘The warning sign is when you start sacrificing clarity and reliability while chasing some vague notion of performance.’ Hague also writes, ‘What’s often missed in these discussions is that the advice to ‘avoid premature optimization’ is not the same thing as ‘write dumb code.” I like this last sentence because I believe that just as some developers have adulterated the agile concept to mean (to them) ‘no documentation,’ some developers have adulterated the sound advice to ‘avoid premature optimization’ to mean (to them) ‘blindly write code.’
Premature Optimization is a Real Problem
Premature optimization is a problem we developers must guard against. As Johnson states in the previously cited book, ‘Few things in programming are harder than optimizing existing code. Unfortunately, this is why optimization is uniquely satisfying to any programmer’s ego. The problem is that the resources devoted to such optimization may well be wasted.’ There makings examples of where premature optimization wastes significant resources and in some cases even makes things perform worse. There is indeed a reason that the well-regarded Knuth wrote that ‘premature optimization is the root of all evil.’ I’m not saying that premature optimization doesn’t exist or that it’s not harmful. I’m just saying that avoiding this admitted dysfunctional behavior is often used an an excuse to avoid thinking or to avoid implementing sound decisions.
Conclusion
Like pithy bumper stickers on cars that naively boil down complex issues to a few clever and catchy words, use of ‘avoid premature optimization’ is often used much more broadly than it was intended. Even the best of recommended practices can cause more harm than benefit when applied improperly and the misuse of ‘avoid premature optimization’ is one of the best examples of this. I have seen the high cost paid in maintainability, readability, and even in performance when supposed ‘optimization’ was implemented too early and at the expense of readability and maintainability for no measurable benefit. However, just as high of a cost can be incurred by blindly using ‘avoid premature optimization’ as an excuse to avoid designing and writing better performing software. ‘Avoiding premature optimization’ is not an excuse to stop thinking.
Reference: When Premature Optimization Isn’t from our JCG partner Dustin Marx at the Inspired by Actual Events blog.
Well, in a fictional ideal world, every project would have its same development time allotted to optimization post-hoc. The use of StringBuilder etc is only defensible when we assume a common Java-literacy between programmers. In some platonic abstraction, repeated concatenation of strings is the semantically superior option. You can argue that the collective time that Java programmers spent contributing StringBuilder to the community consciousness is wasted, and they should have instead left that till profiling time.