Optimization is a word that englobes a significant scope of possibilities, especially in the world of software development. Developers always like to "optimize," but what is it really? -- This word is many times miss-used to excuse subjective point of views, other times it is used only to demonstrate "improvements" that are not sincerely necessary. In this article, we are going to be talking about the mistakes we make as developers when trying to improve our code bases by "optimizing" them, and, we are going to see which are some excellent ways to start doing this correctly.

Always measure first, quantify, and please... don't just blindly "optimize".

A fundamental lesson that we as software developer learn with the years is that numbers matter, something is impactful if you have a way of quantifying the co-relation between the effort you have applied to it and the results it has given.

For example, there is a common belief that lines of code are somehow inversely related to higher performance or better quality.

Usually having less code means less surface area to cover, which means, more clarity and less possibility of bugs. However, this only applies if you have a "high quality" code base that is secure, consistent and is using the right tools for the right job.

Let me elaborate a little bit, take the following piece of code:

const mainWrapper = document . querySelector ( '.wrapper' ) ; if ( mainWrapper ) { const enabledInteractionClass = 'enabled-box' ; const element = document . querySelector ( '.inner-elm' ) ; element . classList . add ( enabledInteractionClass ) ; mainWrapper . classList . remove ( enabledInteractionClass ) ; }

There is nothing wrong with the code above. It just has an extra couple of variables to make the code more declarative. I have seen code reviews where people convert this implementation to:

const mainWrapper = document . querySelector ( '.wrapper' ) ; let enabledInteractionClass = 'enabled-box' ; const enabledElementClass = mainWrapper ? document . querySelector ( '.inner-elm' ) . classList . add ( enabledInteractionClass ) : mainWrapper . classList . add ( enabledInteractionClass ) ;

Then, the argument comes where a developer says, "we can avoid the extra variable for document.querySelector('.inner-elm') " and he/she calls this an optimization, your answer is that you like it more the first way because it is more declarative in comparison, and you spent 5 mins discussing this. What the code reviewer does not realize is that he is now defining enabledInteractionClass all the time, and he is using the variable enabledElementClass for something that does not require it, so where is the improvement? Was this a decision based on a solid foundation? How does this code make the codebase better? is it measurable?

Disclaimer: I'm in favor of well constructed/used ternary operators and short-circuiting, this example just shows how NOT to use a tool just for the sake of less lines of code or to defend a subjective point of view.

There is a term for worthless micro-optimizations that end up in a waste of time.

Bikeshedding is the art of wasting time by having technical disputes over minor, marginal issues, issues that won't have a real positive impact in your work while there are more important things to look at. So, the next time someone wants to argue over insignificant improvements, just say the word bikeshedding. The etymology for this term can be found on wiktionary.org and urbandictionary has another good definition for it.

The point being, micro-optimizations are something you should avoid doing without basis, if you want to optimize some code, measure and identify bottlenecks first.

Measure first

Suppose your website/application is a little bit slow when you scroll, don't just go and increase the throttle time for your listener and mark it as done, analyze what is going on and understand what the reason behind this issue is. A way to measure scroll performance is through Frames Per Second (FPS), so you know you have a quantifiable way to understand what is happening and a starting point.

Let's see a good way to tackle this problem

First, check if the code/tasks executed on the scroll are doing what they suppose to be doing, scroll junkiness could be caused by memory leaks. If the code is wrong, fix it, if it is ok look at your alternatives: Can we use IntersectionObserver instead of the scroll event?

instead of the scroll event? Is throttle needed? can we debounce it? If after looking at your alternatives you don't see any other option then as a last resource consider increasing the throttle time.

Why to go through all this trouble instead of just increasing the time? because nothing ensures you that after increasing the time it won't happen again, or even worst, nothing ensures that there is not a more important issue underneath.

This is just an example applied to scroll performance, but the same principle can be applied over any other circumstance by having the correct metric to measure.

Improve the quality of your code base

Some of the most common ways of optimizing code bases are linters, if you use javascript ESLint is a good one -- As for your styles, you could use Stylelint. You can later add these two elements as required steps in your build process to create an extra layer of consistency, linters are a good way to promote consistency in your code base.

Nevertheless, linters alone are not going to guarantee the quality of your code base , it heavily depends on the level of your team, you have to be aware of this before trying to push things too hard. Some recommendations to start increasing the quality of your codebase are:

Promote design patterns. (We have written about some of them, i.e Observer Pattern, Factory Pattern, Builder) you don't have to use these explicitly, remember, the idea is to use the right tool for the right job, design patterns are not always the best option, but they are for sure a way of adding consistency and security to your application. Pairing Sessions / Brainstorming. Some hugely underestimated and yet powerful tools are communication and teamwork, make sure the team communicates their ideas before implementing them at least with one other person, this ensures that the knowledge spreads, more people gets involved and gives their opinion. Together you will find a more solid approach to solve a problem and you will avoid the fatigue of rejected pull requests, a couple of good ways to promote team communication are Katas and RFC's. Your future self matters. When you write code or make a code review, think in your future self, what would happen tomorrow if you had to scale your module? I like to follow the DIRTFT (Do It Right The First Time) principle, this is certainly not easy to do, even today I don't get to write the best possible code I can when I do it the first time, but I iterate over it until I get to a place where I feel comfortable with it. Think before you code. Create a model about what you have to do, understand what is needed and the alternatives you have, never start writing code right away before analyzing the implementation. Care about data structures and time/space complexity. This point requires deeper knowledge on how algorithms work and why. I have personally noticed that the more experienced the team is the more they care about this, in the frontend world there is a miss-conception that data structures/time space complexity does not matter as much, but this is a mistake, these tools allow you to write scalable/performant modules, it is part of choosing the right tool for the right job.

There is not a fixed formula for code quality, this is a progressive and subjective topic, it highly depends on the requirements of the company you are working on and your team. One last recommendation I could give you is to cosider promoting declarative code, give attention to the readability.

Recommendations, tips and good practices to optimize your site (Performance):

One excellent way to measure your site's performance is through the RAIL model. Below I have a list of some common problems that websites confront and some possible tools that could help you solve them, this list is not meant to solve your problems directly but to point you in the right direction:

Unused code

Impact metric : Bytes / Download time

: Bytes / Download time Helpful tools : webpack-bundle-analyzer, Chrome Coverage

: webpack-bundle-analyzer, Chrome Coverage possible solutions: Tree Shaking

Scroll Junk

Heavy/Slow Animations

Elevated Number of Network Requests

Impact metric : Seconds/Milliseconds Load Time / Application Response Time

: Seconds/Milliseconds Load Time / Application Response Time Helpful tools : Memoizer, Redis

: Memoizer, Redis possible solutions: Memoization, Caching

Memory Leaks

There are many other metrics and important optimizations you can make, it is impossible to fit them all in a list but the tools below are a big help as far as web optimization is concerned:

This is just a starting point

The path to web performance and code quality is long, it is not straightforward in most of the cases, there are some things that you only learn from experience. This article is meant to be a starting point for those who are looking for directions but don't know where to start or how to start.

Hope you have enjoyed this article, see you in the next one!