Yes, I’m also a software developer and from time to time software stuff needs to be talked about. Today is one of those days. And today’s topic, Scape Goats!
There are a few ubiquitous quotes that programmers know and share. Most people don’t get them without some background in software development, but meaningful they are.
“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.”
– Brian Kernighan
“Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.”
– Martin Golding
“A programmer is a wonderful machine that converts coffee into software.”
There’s a grain of truth in each one, the problem is that the following has no grain of truth. Only a misunderstanding and a shifting of blame:
Premature Optimization is the Root of All Evil
What is it supposed to mean? Well there is a wonderful article called The Wrong Abstraction. It describes a compromise that most programmers don’t think of. Replication of code is almost as bad as replication of data. Replicated code is difficult to maintain, in fact new programmers will not likely find it. If one piece of code is updated, the duplicate will likely stay the same. It’s also an inefficient way of writing code. Less code means faster output. Typing out the same lines, or copying, pasting, and editing takes time and is error prone. The same code has to be run and “tested” several times to confirm its validity.
Abstraction happens when code is turned from one form into another to avoid duplication. When a section of code is a common entry point, or has the potential to be called in multiple places, AND is sufficiently complicated (Think algorithms), it is a good candidate for procedural separation. That’s technically a form of abstraction, but it’s super simple. You create methods for this. It’s not really a new or complicated solution.
The wrong abstraction exists when insufficiently complicated code is transformed, an industry standard boiler plate is removed with proprietary solutions, or the wrong solution is applied to a problem.
All of this was presented as a prerequisite. What is often called a Premature Optimization is not the same thing as the wrong abstraction. What it typically means is code that somebody doesn’t agree with… That last statement is dangerous, because a lot of great programmers are guilty of making that judgement. But that risky statement is true. That judgement is used as a blanket term to defame the programmers who came before. Just admit it. When a programmer leaves a project they suddenly become at fault for everything. That’s natural, but it doesn’t make it right.
Sure, not agreeing with a solution can be done for perfectly valid reasons. Many times design patterns are half implemented, or implemented when they are not necessary. But calling the code in question a Premature Optimization is an over simplification. There are real reasons why this happens. Coming up with a nondescript blanket term to label all poorly implemented solutions is a willful refusal to acknowledge or fix the causes!
As with most assumptions that someone else is “unreasonable,” it is the product of a common human misconception that humans are often unreasonable. Fact of the matter is, humans are almost always perfectly reasonable and make perfectly reasonable decisions. They arrive at those decisions with different collected information and foundational understandings. That does not make them unreasonable, just unpopular or unliked.
So what are those causes? Typically there is a single root cause for what’s described as a Premature Optimization. Insufficient time allowed for architecture. Much like QA, architecture phases of any project, no matter how small the project, improve its overall quality. We get better software when it is properly architected. Now I’ll be very loose with the term “architecture” because choosing the right programming language, programming tools, and 3rd party packages is part of that success. However, the fact remains that having a solid plan based on solid information will direct the programmer towards the right solution. When we see the wrong patterns and incomplete implementations it’s usually caused by not having enough time to consider the proper implications. Describing this situation as a Premature Optimization is a mistake because the actual issue is LACK of optimization. Less optimization would not improve the product.
Another notorious cause is shifting requirements. If a very specific optimization is used, say an IDL with only smallint variables for reduced memory usage when passing data over an RF frequency, and halfway through the project the target hardware can support blue tooth, the IDL definitions suddenly become over optimization. There is nothing premature about this situation. It is a victim of the hazards of software development life cycles. No amount of echo chamber conversations about removing abstraction can solve this issue. And implementation of this attitude will certainly make it worse.
And the most misunderstood cause is differences in programmer backgrounds. Some developers are simply used to having a library stacked with advanced solutions. Every class has an interface, no exceptions. This doesn’t make them wrong, or right. It’s a different approach that requires more setup. Component loaders with a messaging queue underneath are wonderful environments to work in. Someone who doesn’t work in them often can see them as unnecessary. Perhaps they are right, but only by their own standards of what is and is not necessary. The developer who works best with a messaging queue will see the system as necessary. Calling this difference of opinion a Premature Optimization is not fair.
Describing all of these causes in oversimplified terms is nothing more than a scapegoat. There is rarely a situation where the idea of preventing solutions deemed superfluous is actually the solution to the problem. Rather the state of being superfluous is a new development to a previously adequate approach. The work to remove the unfit solution and replace it was always unavoidable. Foresight with incorrect data is no different than absence of foresight. Neither offers an advantage over the other, nor does suggesting absence of foresight provide a solution that can be salvaged easier than a properly formed solution.
Instead I want to throw away any further use of the phrase Premature Optimization. Instead I’d like to offer a different, and accurate saying.
Spaghetti code is the root of all evil.
No matter how well architected, implemented, or maintained a project is, the number 1 cause of poor solutions is last minute time crunch. “Just make it work” gets the code out the door, but it creates more problems than any incomplete architecture ever will. In fact, by placing spaghetti code that is vital to operation over top of a reasonable solution, the solution becomes superfluous. It appears to be unnecessary but it is not the implementation, rather the lack of implementation of the solution, that will ruin the source code.
So stop using other people’s architecture and unforeseeable circumstances as an excuse. Instead let’s spend MORE time optimizing our solutions at the beginning and insuring that information is accurate. The simpler it is to maintain code, the less likely that we will run out of time to cook dinner and end up heaping pasta upon it because it was easier.
I’ll leave with what I feel is the biggest blow to an idea of Premature Optimization:
“Junior programmers create simple solutions to simple problems. Senior programmers create complex solutions to complex problems. Great programmers find simple solutions to complex problems.”
– Charles Connell