Today, we are going to talk about a vital thing related to programming — optimization. In most cases, the routine is the same; first, we learn about the problem, think about the solution, then implement it, and run the program. Our final solution may be good or bad, and the two main categories for evaluation are the execution and the implementation of our code. We may end up with an excellent execution and implementation as well as with poor ones. However, the tricky part is the time when we should optimize these aspects, therefore, making the most out of them and avoiding common pitfalls.
Optimization of the execution
The first aspect of our program that we can clearly optimize is the execution. The execution may be related to the time or memory consumption as well as the hardware or any other resource we utilize to run the program. Basically, those are all the things that make our system work faster and more efficiently concerning technical means or money.
What are our options when optimizing this element of our code? On the one hand, if we do not pay enough attention to the vital parts of our application, making sure we optimized their execution to the practical level, we may end up with the whole project falling apart due to crucial inefficiencies of certain parts. When not spotted on time, these inefficiencies may force us to redesign the entire program because some significant part wasn’t thought through.
On the other hand, if we pay too much attention and time to certain parts of the application, making them execution masterpieces from the start, we may fall short on time to even finish the project. Besides, concentrating too much on details may obscure the big picture. Sometimes, that happens when we refine a single module to the perfect state and later find out that it won’t be needed in the final product.
How should we approach the matter at hand to make sure we do not optimize the execution of our code too little or too much?
Well, a good idea is to only optimize things that matter, and to the extent that is absolutely necessary. In other words, our default behavior should be not to optimize the execution and instead pay attention to the big picture. The only exception from that rule is when a specific part of the application has to be refined because its execution is so crucial to the program that otherwise the system would not make sense at all. Still, we should improve it only to the point that it is good enough, not more. That is because usually, more optimal solutions are time-consuming. As a result, we get a program at the lowest acceptable execution level, and all possible optimizations are not premature.
Optimization of the implementation
The second aspect that we can optimize is the implementation itself. That is quite a broad subject related to development experience and encompasses things like code and design quality, static code analysis, automatic tests, documentation, and so on. Basically, all the things that make the implementation more friendly to developers and thus easier to reason about.
Nevertheless, the main difference from the execution problem we discussed before is that it is more elusive to define a good and bad code in regard to implementation. Usually, the programmers that work in the project set the rules they follow, and most of the time, those rules are consistent for the whole system. Let’s say that a good and optimized code is the one that fits all requirements established for the project.
The advantages and disadvantages of having a good quality implementation are pretty obvious. When we allow ourselves to diverge from the standard and good practices, we build a technical debt, which may lead to costly refactor and, most importantly, impede a proper tasks’ estimation, causing hard to predict delays.
On the other hand, keeping the implementation at the highest level is costly and sometimes unnecessary. When we create a temporary solution or a proof of concept, it may be beneficial to value time over quality.
What is the best approach to this kind of optimization? First of all, it seems pretty evident that we cannot proceed as we did with the execution. The implementation is a whole different concept, and starting with no optimization, which means a bad code, is a fast route to chaos. The main difference is the technical debt which grows as time goes by. When we finish the task with a bad code, it will be a problem in the future. At the same time, a nonoptimal execution is still acceptable because we can choose to refine it later.
Well, knowing that contexts differ, the proper approach comes to mind. We should start from the optimal implementation based on our standards, and the programmer should justify any exception from that rule. It means that a consistent code quality brings the most significant advantage, and we should definitely take that seriously. When we encounter far less important modules, like a temporary code or a proof of concepts, we can abandon some quality requirements to save time, but this should always be regarded as an exceptional situation.
In this case, the optimization is rarely premature. We should aim to end up with the best possible implementation, but only to the desired level of requirements. As a result, we get a less painful maintenance and a more accurate tasks’ estimation. All of that makes the lives of programmers sublime.
There are two aspects of the code we may optimize, the execution and the implementation. The first one is prone to premature optimization, so we should approach the execution problem with a solution that is not necessarily optimal but rather sufficient enough, so the whole system is operable. We should only optimize when necessary.
On the other hand, we should always keep the implementation at the highest agreed level because the potential cost of a poor code is too great. We should closely examine every situation where the temptation to not optimize the code quality arises.
The whole discussion should be regarded as a general guideline that displays the differences and priorities around the optimization topic. As everywhere, here too, there are some exceptions, but understanding the pros and cons of each approach is crucial to make your own informed decision and not fall into premature optimization trouble.