Improve the code and quality of your product | 6 basic tips

6-consejos-básicos-para-mejorar-tu-código-y-la-calidad-de-tu-producto-Itequia

Improve the code and quality of your product | 6 basic tips

As developers, we’ve spent a lot of time chopping the code with the sole concern that it works, without worrying too much about the code’s shape, architecture, scalability, maintainability, or readability.

Mejora-tu-codigo-consejos-Itequia

We’ve all had tight deadlines, but rushing, when repeated, makes code increasingly difficult to maintain.

We shouldn’t settle for making things work, we should worry about getting a code that all developers can understand.

What will allow different teams that do not know each other to work with the same product is not the programming language, but the standards, nomenclatures, principles, and design patterns that are applied to the project.

During this article, we will not talk about SOLID principles, nor design patterns, certainly a more advanced level in the world of Clean Code.

We’ll focus on small rules that, if we keep them in mind during our development, will allow us to get more readable code that allows other teams to maintain that code without wasting hours, or even days, trying to understand a function. Even we will find it easier to pick up our code weeks later.

Bad code has a cost and it costs as much to do things well as to do them badly. We just need to know how to do them better.

Next, we explain six basic points that will help you improve your code and the final result of your project.

Tip #1 | Avoid using Switch in the code

The “Switch” isn’t a bad pattern per se, it’s a conditional that is normally used to prevent a long “if-else-if-…” string. But even so, they have a very redundant syntax and can also lead to very long methods due to too large “case”. Making readability and debugging difficult by distributing the logic in each “case”.

There are different ways to avoid the use of “Switch” that derives the most readable and scalable code and avoids the redundancy of a “Switch” with the same condition.

Personally, the way I prefer is to use polymorphism, so that the property that we are adding to the conditional becomes an object that implements the logic that should go in each “case“.

Let’s see an example below:

for (var animal in home) {
    switch (typeof(animal)) {
        case "dog":
            animal.bark();
            break;

        case "cat":
            animal.meow();
            break;
    }
}
for(IAnimal animal in animals) {
    animal.speak();
}

interface IAnimal{
    Speak();
}

class Dog extends IAnimal
{
    Speak(){
        return 'Guau'
    }
}

class Cat extends IAnimal
{
    Speak(){
        return 'Miau'
    }
}

Tip #2 | Use descriptive conditionals

That the code is descriptive is a “must”, and it is easy to think of naming things properly, although it is also very difficult to find the right name.

However, just keeping in mind that we have to describe things better will make our code more understandable and easier to read.

In the case of conditionals, it is much easier and immediate to read and understand a conditional whose condition is a method of the type:

/*Aproximación básica*/

if (isActiveStudent(user))

/*Usando una aproximación más propia de la orientación a objetos*/

if(user?.isActiveStudent())

Then read something like:

if (user != null && user.RoleId==3 && (user.FinishDate !== null  || user.FinishDate > Today))

Just by taking the condition to a descriptive method, we will make it understandable at the moment.

Tip #3 | Use descriptive names

In code, classes, functions, and variables must all have a name that reflects their intent.

Therefore, acronyms or character variables should never be used, such as:

f.i var u = User()

Since only the person who created them understands them, after a while, they will surely forget the acronyms used as well.

Tip #4 | A function must only do one thing | DRY – Don’t Repeat Yourself

Functions that do more than one thing are some of the worst things we can do by biting code. These functions coupled code in such a way that this method can only be called if we need to do the “X actions” that this method does.

In this way, we are forced to duplicate that code from somewhere else, from which we need to execute some logic.

So these functions make it difficult to understand what that code is doing and end up being methods with excessive lines of code when we should keep the methods as short as possible. It is always better to split a method, if only for readability than to leave a method.

Yes, I know, if we go extreme with this principle, we could never define a method that will call two other methods in its implementation. Since this parenting method would be doing two things.

Don’t worry, you don’t have to go crazy. The important thing to keep in mind is that the parent method itself does nothing, the two-child methods do everything, so we would not be contradicting anything.

Anyway, the important idea here is to try to keep the methods as short as possible, and to properly encapsulate the logic so that each method does what it’s supposed to and no more. This will greatly improve the readability and reusability of the code.

Tip #5 | Avoid duplicate the code

Repeating a line of code is something very serious, it leads to situations in which it is necessary to repair something in as many places as that logic has been duplicated, and when it is time to evolve the code you must keep in mind all the points where that logic has been duplicated to apply that change, deriving everything is a worse maintainability of the code.

It is logical to think that the more lines of code the project has, the more complicated it is to maintain.

But how do we avoid duplicating code?

There’s no easy answer, but the first step is to extract the logic into properly parameterized functions or extract values ​​into variables.

Let’s see an example below:

function getAquaticAnimals() {
    const allAnimals = getAllAnimals()
    const aquaticAnimals = []
    for (var animal in allAnimals)
        if (animal.type === "aquatic") {
            aquaticAnimals.push(animal)
        }
    }
    return aquaticAnimals
}
 
function getLandAnimal() {
    const allAnimals = getAllAnimals()
    const landAnimal = []
    for (var animal in allAnimals)
        if (animal.type === "land") {
            landAnimal.push(animal)
        }
    }
    return landAnimal
}

function getFlyingAnimals() {
  const allAnimals = getAllAnimals()
  const flyingAnimals = []
    for (var animal in allAnimals)
        if (animal.type === "flying") {
            flyingAnimals.push(animal)
        }
    }
  return flyingAnimals
}

In the code above, we can see that the two functions are doing the same thing, with the only difference being the news type. So if we extract that logic into a method, we can reuse that code as well as improve the readability of the code:

function getAquaticAnimals() {
    const allAnimals = getAllAnimals()
    return getAnimalNames(allAnimals, 'aquatic')
  }
  
  function getRustNews() {
    const allAnimals = getAllAnimals()
    return getAnimalNames(allAnimals, 'land')
  }
  
  function getFlyingAnimals() {
    const allAnimals = getAllAnimals()
    return getAnimalNames(allAnimals, 'flying')
  }

  function getAnimalNames(allAnimals, animalType) {
    const animalNames = []
    for (var animal in allAnimals)  {
      if (animal.type === animalType) {
        animalNames.push(animal.name)
      }
    }
    return animalNames
  }

You may have noticed that the “getAllAnimals()” call is repeated in each method, this could be a bad practice or not, depending on the use we want to give to the reused function ‘getAnimalNames’. It may be necessary to parameterize the set of animals with which the function works.

function getAquaticAnimals() {
    return getAllAnimalsNameBy('aquatic')
  }

  function getRustNews() {
    return getAllAnimalsNameBy('land')
 }

  function getFlyingAnimals() {
    return getAllAnimalsNameBy('flying')
  }
  
  function getAllAnimalsNameBy(animalType) {
    const allAnimals = getAllAnimals()
    const animalNames = []
    for (var animal in allAnimals)  {
      if (animal.type === animalType) {
        animalNames.push(animal.name)
      }
    }
    return animalNames
  }

But, if, on the other hand, that function will always work on the total set of animals, it is a good option to remove the ‘allAnimals’ parameter and leave the methods as follows.

Deriving in a more readable code although slightly less reusable, we can always create a second function ‘getAnimalNames(allAnimals, type)’ that returns us reusability.

function getAllAnimalsNameBy(animalType) {
    const allAnimals = getAllAnimals()
    return getAnimalNames(allAnimals,animalType)
  }

  function getAnimalNames(animalsArray, animalType) {
    const animalNames = []
    for (var animal in animalsArray)  {
      if (animal.type === animalType) {
        animalNames.push(animal.name)
      }
    }
    return animalNames
  }

Tip #6 | Boy Scout Rule

As a proud former Boy Scout, we always used to say something like “You have to leave the forest cleaner than we found it”. So, when we left a place, we collected what was ours, and we always took some garbage that we found around us.

This is a rule that applies perfectly in the forest that our code represents.

Every time we visit the code, we must do things correctly, and apply the principles that we have described above, among others, so as not to add more garbage.

You always have to leave the environment a little better than we found it, even if we are not responsible for that code. Thus, we achieve that, based on small iterations or visits, little by little, we will improve the code and leave it cleaner.

In addition, all the time we have spent trying to understand the purpose of a method or function that we have come across, we can save for the next developer if we somehow improve those unclear and obfuscated parts, in short, dirty.

Quality or functionality in the code?

We, programmers, are responsible for writing quality code. The client should note that quality is not only respecting the functionality, but resolving bugs, or carrying out evolutions is easy and without extra cost due to poorly implemented codes, since bad technical decisions are paid for by both the client and the developer.

There are many more rules than the ones we have discussed, but by applying those mentioned, the readability and maintainability of the code will greatly improve, and consequently, productivity.

Beyond new Frameworks, programming languages ​​, or services, it is our responsibility as developers to learn agnostic programming techniques to become better programmers and be able to offer quality products, both inside and out.

Sergi Forns – Key Software Developer at Itequia