Prácticas de Codificación Limpia en JavaScript: Guía para escribir mejor código

/* by Tirth Bodawala - June 22, 2023 */

Escribir código limpio, legible y reutilizable es una habilidad esencial para todo desarrollador de JavaScript. Esta práctica no sólo hace que tu código sea más fácil de entender y mantener, sino que también reduce el tiempo necesario para incorporar nuevos desarrolladores. Esta entrada del blog te guiará a través de 16 prácticas de codificación limpia que puedes empezar a utilizar hoy mismo para mejorar la calidad de tu código JavaScript.

1. Nombrar con sentido

El primer paso para escribir código limpio es dar nombres significativos a tus variables y funciones. Los nombres deben comunicar claramente la finalidad de la variable o función, haciendo que tu código se explique por sí mismo.

Considera el siguiente ejemplo de JavaScript:

// Bad
let x = 10;
let y = new Date().getFullYear();
if (x > 30) { /*...*/ }
if (y - x > 1990) { /*...*/ }

// Good
let userAge = 30;
let currentYear = new Date().getFullYear();
if (userAge > 30) { /*...*/ }
if (currentYear - userAge > 1990) { /*...*/ }

En el primer ejemplo, las variables x y y son crípticas y no revelan su finalidad. El segundo ejemplo utiliza nombres de variables claros y descriptivos, mejorando la legibilidad del código

2. Condicionales positivos

Evita los condicionales negativos siempre que sea posible. A menudo son más difíciles de entender que sus homólogos positivos. Considera lo siguiente:

// Bad
if (!userExists(user)) { /*...*/ }

// Good
if (userExists(user)) { /*...*/ }

El segundo ejemplo es más fácil de entender, ¿no? Cuando lo lees en voz alta, suena como el inglés, lo que hace que tu código sea más intuitivo.

3. Funciones de responsabilidad única

El Principio de Responsabilidad Única (PRU) establece que una función debe hacer una cosa, y hacerla bien. Las funciones deben ser concisas y no deben superar una media de 30 líneas (excluyendo comentarios y espacios en blanco). Si una función hace más de una cosa, plantéate dividirla en funciones más pequeñas y manejables.

// Bad
function createAndDisplayUser(name, age, address) { /*...*/ }

// Good
function createUser(name, age, address) { /*...*/ }
function displayUser(user) { /*...*/ }

4. Utilizar argumentos por defecto

Los argumentos por defecto pueden hacer que tu código sea más limpio y fácil de entender que si utilizas cortocircuitos o condicionales. Proporcionan valores por defecto para los argumentos no definidos. Otros valores falsos, como ”, “”, false, null, 0 y NaN, no se sustituirán por un valor por defecto.

// Bad
function getUserData(name) {
  const userName = name || "John Doe";
  /*...*/
}

// Good
function getUserData(name = "John Doe") { /*...*/ }

5. Mantener un único nivel de abstracción

Una función debe operar en un único nivel de abstracción. Si tu función hace más que eso, suele ser un indicio de que hace más de una cosa. Dividir una función más grande en otras más pequeñas puede mejorar la reutilización y facilitar las pruebas.

// Bad
function checkSomething(statement) { /*...*/ }

// Good
function checkSomething(statement) {
  const tokens = tokenize(statement);
  const syntaxTree = parse(tokens);
  syntaxTree.forEach(node => { /*...*/ });
}
function tokenize(code) { /*...*/ }
function parse(tokens) { /*...*/ }

6. No ignores los errores detectados

Los errores detectados no deben ignorarse. Si se captura un error en un bloque try/catch, es una indicación de que esperas un error potencial en esa sección de código. Por tanto, debes tener un plan sobre qué hacer cuando se produzca ese error. No basta con registrar el error en la consola, ya que puede perderse fácilmente entre otros mensajes de la consola.

// Bad
try {
  functionThatMightThrow();
} catch (error) {
  console.log(error);
}

// Good
try {
  functionThatMightThrow();
} catch (error) {
  notifyUserOfError(error);
  reportErrorToService(error);
}

7. Minimizar comentarios

Aunque los comentarios pueden ser útiles, deben utilizarse con moderación y sólo cuando sean necesarios. Un buen código es autodocumentado. Si sientes la necesidad de añadir un comentario, considera la posibilidad de refactorizar tu código para hacerlo más claro. La única excepción a esta regla es cuando se trata de lógica empresarial compleja que no puede simplificarse más.

// Bad
function hashing(data) {
  // The hash
  let hash = 0;
  // Length of string
  const length = data.length;
  // Loop through every character in data
  for (let i = 0; i < length; i++) {
    // Get character code.
    const char = data.charCodeAt(i);
    // Make the hash
    hash = (hash << 5) - hash + char;
    // Convert to 32-bit integer
    hash &= hash;
  }
}

// Good
function hashing(data) {
  let hash = 0;
  const length = data.length;
  for (let i = 0; i < length; i++) {
    const char = data.charCodeAt(i);
    hash = (hash << 5) - hash + char;
    hash &= hash; // Convert to 32-bit integer
  }
}

8. Eliminar código comentado

Dejar código sin comentar en tu base de código puede llevar a confusión. Si el código no es necesario, elimínalo. Siempre puedes recuperar el código antiguo de tu historial de control de versiones si lo necesitas más adelante.

// Bad
// doSomething();

// Good
// Code without unnecessary comments.

9. Importa sólo lo que necesites

Con ES6, JavaScript introdujo la desestructuración, que te permite desempaquetar valores de matrices o propiedades de objetos en variables distintas. Puedes utilizar esta función para importar sólo las funciones que necesites de otros módulos, haciendo que tu código sea más limpio y eficiente.

// Bad
import calculate from './calculations';
calculate.add(4,2);
calculate.subtract(4,2);

// Good
import { add, subtract } from './calculations';
add(4,2);
subtract(4,2);

10. Argumentos de la función límite

Limita el número de argumentos de una función para facilitar las pruebas. Lo ideal es que una función tenga de uno a tres argumentos. Más de eso podría ser señal de que tu función está haciendo demasiado, lo que viola el Principio de Responsabilidad Única (PRU).

// Bad
function createEmployee(name, age, address, position, salary) { /*...*/ }

// Good
function createEmployee(employeeDetails) { /*...*/ }

11. Utilizar dispersiones de matrices para copiar matrices

Copiar matrices utilizando la extensión de matrices es más limpio y directo que utilizar bucles. He aquí un ejemplo:

// Bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i += 1) {
  itemsCopy[i] = items[i];
}

// Good
const itemsCopy = [...items];

Con esto concluye el post sobre prácticas de código limpio en JavaScript. Espero que estos consejos te resulten útiles para escribir un código más legible y fácil de mantener. Recuerda, escribir código es como un arte. No se trata sólo de hacer el trabajo, sino también de crear algo que sea bello en su estructura y diseño. Así que, ten en cuenta estas prácticas y empieza a transformar tu desordenado código en una obra maestra

Atyantik Technologies: Adoptar prácticas de código limpio

En Atyantik Technologies, comprendemos la importancia de las prácticas de código limpio y las hemos incorporado a nuestros hábitos diarios de codificación. No nos limitamos a escribir código, sino que creamos soluciones. Nuestro equipo se enorgullece de ofrecer código de alta calidad que sea fácil de leer, mantener y ampliar.

Las prácticas de código limpio de las que hemos hablado en este post no son meros principios abstractos para nosotros: forman parte de nuestro ADN de codificación. Nuestros desarrolladores tienen por costumbre asegurarse de que cada función, variable y módulo tenga un nombre significativo, que transmita claramente su finalidad y elimine la necesidad de comentarios innecesarios.

Evitamos los condicionales negativos para que nuestro código sea más sencillo, y limitamos el número de argumentos de las funciones para mantener el Principio de Responsabilidad Única. Nuestras funciones están diseñadas para hacer una cosa y hacerla bien, reduciendo la complejidad y haciendo el código más digerible. También nos aseguramos de que nuestras funciones dispongan de mecanismos adecuados de tratamiento de errores, que impidan que fallen silenciosamente y nos permitan reaccionar ante los errores con eficacia.

Mantenemos la práctica de no dejar código sin comentar en nuestra base de código, entendiendo que los sistemas de control de versiones sirven para preservar el historial de nuestro código. Valoramos la potencia de funciones de ES6 como la desestructuración y la extensión de matrices, utilizándolas para que nuestro código sea más eficiente y limpio.

Cada revisión de código en Atyantik Technologies es una oportunidad de aprendizaje. Animamos activamente a nuestros desarrolladores a que revisen el código de los demás. Esto no sólo conduce a una mejor calidad del código, sino que también nos ayuda a aprender y mejorar continuamente nuestras prácticas.

En conclusión, en Atyantik Technologies no sólo codificamos: escribimos código limpio, eficiente y mantenible. Creemos que estas prácticas no sólo son beneficiosas para nosotros como desarrolladores, sino que también aportan un inmenso valor a nuestros clientes al garantizar que el código que entregamos es de la máxima calidad, fácil de entender y está preparado para futuras ampliaciones. Nos comprometemos a mantener estos altos niveles mientras seguimos ofreciendo soluciones excepcionales a nuestros clientes.