El sutil arte de agregar comentarios de código en JavaScript

Mar 18 2023
Depurar código sin comentarios es como ser un detective tratando de resolver un crimen, pero el único testigo habla en un idioma extranjero, y hay algunas personas influyentes (clientes) respirándote para resolver el caso ayer. fuente de la imagen — dos hombres mirando una computadora portátil · Foto de stock gratis (pexels.

Depurar código sin comentarios es como ser un detective tratando de resolver un crimen, pero el único testigo habla en un idioma extranjero, y hay algunas personas influyentes (clientes) respirándote para resolver el caso ayer.

fuente de la imagen — Dos hombres mirando una computadora portátil · Foto de stock gratuita (pexels.com)

Introducción

Agregar comentarios de código es importante por varias razones:

1. Legibilidad del código. Los comentarios ayudan a los desarrolladores a comprender el propósito detrás del código al explicarlo en un lenguaje sencillo, lo que facilita su lectura y modificación.

2. Mantenimiento. Los comentarios lo ayudan a recordar el propósito de su código, lo que facilita el mantenimiento y la actualización en el futuro sin causar problemas accidentalmente.

3. Colaboración. Los comentarios evitan errores y malas interpretaciones en proyectos colaborativos al proporcionar contexto para los cambios de código, asegurando que todos estén en la misma página.

4. Documentación. Los comentarios del código brindan claridad sobre el propósito y la funcionalidad, lo que garantiza el cumplimiento de los estándares y las mejores prácticas de la industria. Hacen que el código sea más legible, mantenible y fácil de trabajar, al igual que la guía de instrucciones de LEGO en la construcción de un juego.

Al tomarse el tiempo para agregar comentarios significativos y precisos al código, los desarrolladores pueden mejorar la calidad general y la capacidad de mantenimiento de sus proyectos.

Modelo mental para agregar comentarios al código

Antes de escribir comentarios sobre una pieza de código, es importante tener claro la intención de hacerlo. Un comentario de código es un mecanismo de comunicación que debe comunicar los siguientes aspectos del código:

1. Finalidad. Explique el propósito del código, qué problema resuelve y por qué existe.

2. Entrada. Especifique las entradas requeridas por el código, incluidos sus tipos, valores esperados y si son opcionales u obligatorios.

3. Salida. Especifique la salida producida por el código, incluido su tipo y cualquier detalle relevante.

4. Uso. Proporcione ejemplos de cómo usar el código, incluidos fragmentos de código relevantes.

5. Vinculación. Agregue enlaces a código relacionado y recursos externos.

6. Mejores prácticas. Resalte cualquier práctica recomendada o error relacionado con el código.

Al seguir el modelo mental correcto, los desarrolladores pueden asegurarse de que su código esté bien documentado, sea fácil de entender y se pueda mantener con el tiempo.

Consistencia y Estandarización

Es fundamental mantener la coherencia en el formato y el estilo de los comentarios en todo el código base. Usar una herramienta como JS Doc (documentación de JavaScript), que es un conjunto de estándares de documentación y herramientas para documentar código JavaScript, es una buena opción.

Las siguientes son algunas de las características de JS Doc:

  • Utiliza etiquetas para capturar información sobre la estructura, la función, los parámetros, los valores devueltos y otros detalles del código para crear una referencia legible por humanos para otros desarrolladores.
  • Una etiqueta JS Doc comienza con un @símbolo, seguido del nombre de la etiqueta y cualquier información relevante. Por ejemplo, la parametiqueta se usa para describir un parámetro de función, mientras que la returnsetiqueta se usa para describir el valor de retorno de una función.
  • Puede generar documentación en HTML u otros formatos a partir de comentarios en línea en el código fuente.

function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

function factorialWithCallback(n, callback) {
  try {
    const result = {
      input: n,
      output: factorial(n),
    };
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}

Etiquetas JS Doc de uso común

Echemos un vistazo a un par de etiquetas JS Doc que se usan con bastante frecuencia.

  • @todo

/**
 * @todo Add details about the factorial calculation and edge cases.
 */
function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

/**
 * @todo Add an example usage of the callback function.
 */
function factorialWithCallback(n, callback) {
  try {
    const result = {
      input: n,
      output: factorial(n),
    };
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}

El primer paso es describir lo que hace el código. @descriptionLa etiqueta se utiliza para proporcionar una descripción detallada de una función, objeto o propiedad. Veamos cómo se ve el código después de agregar esta etiqueta.

/**
 * @description A function that calculates the factorial of a number using recursion.
 */
function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

/**
 * @description A version of the factorial function that returns the result via a callback.
 */
function factorialWithCallback(n, callback) {
  try {
    const result = {
      input: n,
      output: factorial(n),
    };
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}

Aquí viene la parte donde se dice qué entrada toma el código. @paramLa etiqueta se utiliza para describir los parámetros de una función. Agregarlo al ejemplo hace que el código se vea así:

/**
 * @description A function that calculates the factorial of a number using recursion.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 */
function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

/**
 * @description A version of the factorial function that returns the result via a callback.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * @param {function} callback - The function to call with the result of the calculation.
 */
function factorialWithCallback(n, callback) {
  try {
    const result = {
      input: n,
      output: factorial(n),
    };
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}

Hasta ahora sabemos lo que hace la función y las entradas que necesita. Luego viene la descripción del valor de retorno de una función. Agregar @returnsal ejemplo hace que el código se vea así:

/**
 * @description A function that calculates the factorial of a number using recursion.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * 
 * @returns {number} The factorial of `n`.
 */
function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

/**
 * @description A version of the factorial function that returns the result via a callback.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * @param {function} callback - The function to call with the result of the calculation.
 * 
 * @returns {void}
 */
function factorialWithCallback(n, callback) {
  try {
    const result = {
      input: n,
      output: factorial(n),
    };
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}

Habrá momentos en que la función encontrará errores y no podrá producir los resultados deseados. @throwsayuda a documentar esos escenarios. Agregar @throwsal ejemplo hace que el código se vea así:

/**
 * @description A function that calculates the factorial of a number using recursion.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * 
 * @returns {number} The factorial of `n`.
 * 
 * @throws {TypeError} If the input is not a number.
 */
function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

/**
 * @description A version of the factorial function that returns the result via a callback.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * @param {function} callback - The function to call with the result of the calculation.
 * 
 * @returns {void}
 */
function factorialWithCallback(n, callback) {
  try {
    const result = {
      input: n,
      output: factorial(n),
    };
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}

A veces, el consumidor o invocador del código no está interesado en los detalles de implementación de bajo nivel y está preocupado por la invocación y la obtención de los resultados esperados. @exampleLa etiqueta se usa para proporcionar un ejemplo de cómo usar una función u objeto. Agregar @exampleal ejemplo hace que el código se vea así:

/**
 * @description A function that calculates the factorial of a number using recursion.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * 
 * @returns {number} The factorial of `n`.
 * 
 * @throws {TypeError} If the input is not a number.
 * 
 * @example
 * factorial(5);
 * // returns 120
 */
function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

/**
 * @description A version of the factorial function that returns the result via a callback.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * @param {function} callback - The function to call with the result of the calculation.
 * 
 * @returns {void}
 * 
 * @example
 * factorialWithCallback(5, (error, result) => {
 *   if (error) {
 *     console.error(error);
 *   } else {
 *     console.log(result);
 *   }
 * });
 * // logs { input: 5, output: 120 }
 */
function factorialWithCallback(n, callback) {
  try {
    const result = {
      input: n,
      output: factorial(n),
    };
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}

La @typeetiqueta JSDoc se usa para especificar el tipo de una variable, función o expresión. Proporciona información adicional sobre el tipo de entidad que se está documentando y ayuda con la legibilidad y el mantenimiento del código.

La @typedefetiqueta JSDoc, por otro lado, se usa para definir un tipo personalizado. Esto puede ser útil en proyectos complejos en los que tiene estructuras o tipos de datos personalizados que deben documentarse. El tipo personalizado definido con @typedefse puede referenciar con la @typeetiqueta en otras partes del código.

En esencia, @typedefse usa para definir un tipo personalizado, mientras que @typese usa para especificar el tipo de una entidad.

La @propertyetiqueta JSDoc se usa para documentar las propiedades de un objeto o clase. Por lo general, se usa junto con @typedefpara documentar la estructura de un objeto o clase. La @propertyetiqueta especifica el nombre y tipo de una propiedad, así como una descripción de su propósito.

Agregar @type, @typedefy @propertyal ejemplo hace que el código se vea así:

/**
 * @typedef {Object} FactorialResult
 * 
 * @property {number} input - The input to the factorial function.
 * @property {number} output - The output of the factorial function.
 */

/**
 * @description A function that calculates the factorial of a number using recursion.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * 
 * @returns {number} The factorial of `n`.
 * 
 * @throws {TypeError} If the input is not a number.
 * 
 * @example
 * factorial(5);
 * // returns 120
 */
function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

/**
 * @description A version of the factorial function that returns the result via a callback.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * @param {function} callback - The function to call with the result of the calculation.
 * 
 * @returns {void}
 * 
 * @example
 * factorialWithCallback(5, (error, result) => {
 *   if (error) {
 *     console.error(error);
 *   } else {
 *     console.log(result);
 *   }
 * });
 * // logs { input: 5, output: 120 }
 */
function factorialWithCallback(n, callback) {
  /**
   * @type {FactorialResult}
   */
  const result = {
    input: n,
    output: factorial(n),
  };
  try {
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}

  • @callback

/**
 * @typedef {Object} FactorialResult
 * 
 * @property {number} input - The input to the factorial function.
 * @property {number} output - The output of the factorial function.
 */

/**
 * @callback FactorialCallback
 * 
 * @param {Error} error - The error that occurred during the calculation, if any.
 * @param {FactorialResult} result - The result of the calculation.
 */

/**
 * @description A function that calculates the factorial of a number using recursion.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * 
 * @returns {number} The factorial of `n`.
 * 
 * @throws {TypeError} If the input is not a number.
 * 
 * @example
 * factorial(5);
 * // returns 120
 */
function factorial(n) {
  if (typeof n !== "number") {
    throw new TypeError("Input must be a number");
  }
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

/**
 * @description A version of the factorial function that returns the result via a callback.
 * 
 * @param {number} n - The number for which to calculate the factorial.
 * @param {FactorialCallback} callback - The function to call with the result of the calculation.
 * 
 * @returns {void}
 * 
 * @example
 * factorialWithCallback(5, (error, result) => {
 *   if (error) {
 *     console.error(error);
 *   } else {
 *     console.log(result);
 *   }
 * });
 * // logs { input: 5, output: 120 }
 */
function factorialWithCallback(n, callback) {
  /**
   * @type {FactorialResult}
   */
  const result = {
    input: n,
    output: factorial(n),
  };
  try {
    callback(null, result);
  } catch (error) {
    callback(error);
  }
}

FactorialCallbackLuego se hace referencia al tipo en la @parametiqueta para el parámetro de devolución de llamada de la factorialWithCallbackfunción.

Ejemplo/Demostración

Se puede consultar una vista previa del fragmento anterior y la documentación que genera usando JS Doc aquí .

Tenga en cuenta que el ejemplo es una implementación muy simplificada para mostrar al lector el potencial que se puede desbloquear una vez que todas estas técnicas se comprendan e implementen en sus proyectos.

Resumen

En un mundo lleno de bases de código complejas e intrincadas, los comentarios de código son un amigo que todos necesitamos. Con beneficios que van desde la mejora de la legibilidad del código hasta la documentación generada automáticamente, las herramientas como JS Doc son esenciales para cualquier conjunto de herramientas de desarrollo.

Tanto si es un desarrollador experimentado como si acaba de empezar, tomarse el tiempo para documentar su código de forma estandarizada no solo beneficiará al producto a corto plazo, sino que también garantizará la longevidad y la capacidad de mantenimiento de la base de código en los años venideros.

Le animo a utilizar estos sistemas en sus proyectos. Confía en mí, ¡tu futuro yo te lo agradecerá!