Dos personas mirando código de software en una pantalla

¿Qué es la calidad del código?

Calidad del código y cómo mejorarla

La calidad del código se refiere a la robustez del mismo, más allá de si simplemente se ejecuta y realiza la función deseada. El código de alta calidad se distingue por su eficiencia, facilidad de mantenimiento, legibilidad y reutilización, mientras que el código de baja calidad es frágil, difícil de analizar y propenso a acumular deuda técnica con el tiempo.

Unos altos estándares de codificación son para el proceso de desarrollo de software lo que una mise en place adecuada y ”trabajar limpio” son para el funcionamiento de una cocina profesional. Las prácticas que mejoran la calidad de su código pueden aportar una mejor funcionalidad a corto plazo, pero sus beneficios más importantes son menos problemas, un progreso más rápido y menores costes de mantenimiento a largo plazo.

A veces, a los programadores les puede resultar difícil comunicar los beneficios a largo plazo de un código de mayor calidad a una dirección menos versada en los entresijos del ciclo de vida del desarrollo de software. Equilibrar los beneficios holísticos del código óptimo con las presiones inmediatas de las prioridades empresariales a menudo implica complejas concesiones. Dicho esto, un estudio de 2022 sobre 39 bases de código de producción propietarias afirmó, entre otras conclusiones1, que: 

  • La deuda técnica derivada de un código elaborado con prisas supone una pérdida de hasta el 42 % del tiempo de los desarrolladores.

  • El código de baja calidad genera 15 veces más defectos que el código de alta calidad.

  • Resolver los problemas en un código de baja calidad lleva (de media) 124 % más tiempo que resolverlos en un código de alta calidad.

Un código de mayor calidad aumenta la facilidad y la rapidez de comprensión, refactorización, depuración y adición de nuevas características a una base de código. Un código claro, coherente y bien escrito facilita una coordinación más fluida entre los equipos de desarrollo y reduce la complejidad y las complicaciones de los cambios en el código. Impulsa no solo una sólida calidad del software, sino también una sólida experiencia del desarrollador y experiencia del usuario.

El hecho de que un fragmento de código se compile y ejecute con éxito su propósito en tiempo de ejecución no es suficiente para determinar su calidad general. Escribir código no es como completar un crucigrama, en el que existe una única forma de completar correctamente la tarea: a menudo hay innumerables soluciones para un problema de codificación determinado. Por lo tanto, la funcionalidad representa el mero  punto de partida para un código aceptable. El valor del código de alta calidad se manifiesta en sus efectos secundarios sobre el contexto que lo rodea.

¿Qué define la alta calidad del código?

El código de alta calidad es un concepto relativamente abstracto. La calidad general de un fragmento de código viene definida tanto por cómo se ha creado y cómo interactúa con la base de código más amplia en la que se inscribe, como por cualquier conjunto específico de métricas de calidad del código discretas y objetivas (aunque existen muchas de este tipo).

Entre las características comunes del código de alta calidad se incluyen:

  • Legibilidad: la legibilidad del código es esencial para el mantenimiento, la depuración y la coordinación entre equipos y a lo largo del tiempo. ¿Puede otro miembro del equipo comprender fácilmente su código? ¿Puede otro programador, que trabaje dentro de unos años, interpretar con precisión su código sin que usted esté presente para proporcionar el contexto?

  • Mantenibilidad: la complejidad del código suele estar inversamente correlacionada con la mantenibilidad del mismo. ¿Es su código fácilmente comprobable, lo que permite a su equipo evaluarlo de manera eficiente en busca de vulnerabilidades de seguridad y oportunidades de optimización? ¿Es lo suficientemente robusto como para añadir nuevas características sin romper la funcionalidad principal, o es demasiado frágil como para ajustarlo sin requerir una refactorización importante? Dar prioridad a un código mantenible puede suponer una molestia inicial adicional, pero ahorra una cantidad significativa de tiempo y energía en el futuro.

  • Eficiencia: un código bien escrito puede reducir la latencia y el consumo de recursos de un sistema. Por ejemplo, unas estructuras de datos cuidadosamente elegidas pueden minimizar el número de operaciones de CPU necesarias para una función determinada. Unas estrategias de almacenamiento en caché de datos bien pensadas reducen las costosas operaciones de entrada/salida (E/S) al eliminar consultas redundantes a la base de datos y las solicitudes de red, mientras que liberar rápidamente la memoria no utilizada evita un aumento innecesario del consumo de RAM.

  • Fiabilidad: un menor número de defectos y estructuras robustas ante los cambios en el código se traducen en fallos y tiempos de inactividad menos frecuentes. La fiabilidad es esencial para la experiencia y la confianza del usuario, así como para el estado de los sistemas críticos de los que depende su empresa.

En un artículo publicado en Harvard Data Science Review en 2023, investigadores de la Universidad de Calvin, el Amherst College y la Universidad de Columbia propusieron un marco prescriptivo para un buen código: “las cuatro C”2.

  • Corrección: el código hace lo que se supone que debe hacer. Los autores hicieron hincapié en dos corolarios de esta obvia inclusión: ”En primer lugar, la corrección es una métrica necesaria, pero insuficiente, para un buen código. En segundo lugar, los demás objetivos respaldan y promueven la corrección”.

  • Claridad: cualquiera que lea y escriba el código puede saber qué se pretende que haga e intuitivamente realizar las modificaciones necesarias.

  • Contención: evite la dispersión, la redundancia y las dependencias innecesarias. Una contención adecuada implica, entre otras cosas, ”utilizar funciones para contener código reutilizable y mantener el código utilizado en archivos o proyectos dentro de un módulo o paquete”.

  • Coherencia: una base de código debe mantener la coherencia en cuanto a estilo, convenciones de nomenclatura, comentarios, sangría y otras prácticas.

A medida que el desarrollo de software moderno sigue estando cada vez más impulsado por asistentes de codificación agéntica como IBM® Bob, la contención y la coherencia resultan especialmente útiles para maximizar la eficacia y la precisión de las herramientas automatizadas. Dicho esto, la capacidad de plataformas de próxima generación como IBM Bob para identificar oportunidades de refactorización mediante IA en tiempo real puede reducir el esfuerzo necesario para imponer ese nivel de disciplina estilística.

Buenas prácticas para escribir código de alta calidad

Aunque cada lenguaje de programación y caso de uso tiene sus propios matices específicos y consideraciones detalladas, existen algunas buenas prácticas universales para lograr un código de calidad en cualquier situación.

Una forma de conceptualizar la alta calidad del código consiste simplemente en considerarlo como aquel código que evita, en la medida de lo posible, los indicadores de código deficiente (que se explorarán más adelante en este artículo).

Para un enfoque más normativo, el mencionado artículo de Harvard Data Science Review (HDSR) esboza una serie de directrices para garantizar la calidad del código. Aunque estas directrices están aparentemente optimizadas para las necesidades de los científicos de datos, la mayoría son aplicables a cualquier disciplina de codificación.

Elija buenos nombres

Las convenciones de nomenclatura sólidas son esenciales para la legibilidad y la coherencia del código. Los autores sugieren las siguientes prácticas:

  • La longitud de los nombres debe ser proporcional a su ámbito de aplicación. Cuanto mayor sea la distancia (ya sea en términos de tiempo, líneas de código o estructura organizativa) entre la definición inicial de un término y su uso, más esencial es que su nombre comunique claramente su función.

  • Mantenga un resumen de abreviaturas. Los nombres de variables cortos o incluso de un solo carácter pueden ayudar a despejar el código, pero también pueden resultar incomprensibles para las personas menos familiarizadas con el proyecto. Mantener una especie de ”glosario” mitiga esa concesión.

  • Utilice las mayúsculas de forma coherente. También es aconsejable evitar casos en los que dos nombres se diferencien únicamente por el uso de mayúsculas.

  • Evite los nombres poco descriptivos. Estos aumentan significativamente la dificultad de interpretación del código.

  • Elija convenciones de nomenclatura de archivos que se ordenen de forma natural. Por ejemplo, puede adoptar la norma ISO 8601 para la fecha y la hora, o rellenar los números con ceros para garantizar que todos tengan la misma cantidad de dígitos.

Unas convenciones de nomenclatura claras y coherentes también pueden ayudar a simplificar la tarea de dar instrucciones a los asistentes de codificación con IA y aumentar la precisión de sus resultados. Por ejemplo, en lugar de solicitar a un agente que inspeccione ciertos tipos de variables o explore todos los archivos de un intervalo de fechas determinado (lo que podría requerir que su agente de IA deduzca de forma probabilística a partir del contexto qué variables o archivos cumplen los criterios), puede darle una instrucción explícita para que explore todos los archivos que comiencen con un número específico o todas las variables con un nombre determinado.

Siga una guía de estilo de forma coherente

Además de codificar convenciones de nomenclatura sólidas, lo ideal es que una guía de estilo completa estandarice los elementos de formato, incluyendo el uso de espacios en blanco y sangrías, los comentarios y los tipos de datos, así como los ”dialectos de codificación”. En GitHub se puede encontrar una amplia variedad de guías de estilo contrastadas para diversos lenguajes de programación, como las incluidas en esta lista seleccionada.

Una guía de estilo también constituye una referencia inestimable para un asistente de codificación con IA, ya que sirve de contexto para tareas específicas o incluso como parte de la instrucción del sistema de su agente de IA.

Elija un kit de herramientas coherente y minimalista (pero adecuado)

El uso de kits de herramientas y bibliotecas supone una ventaja evidente para la reutilización y la eficiencia del código, ya que contribuye a estandarizar los flujos de trabajo y los resultados entre los distintos equipos y a acelerar considerablemente la creación de código. Resultan especialmente útiles cuando el código debe gestionar interacciones con sistemas externos complejos o resolver problemas repetitivos y ya “resueltos”.

Sin embargo, la dependencia excesiva de los kits de herramientas puede añadir una sobrecarga innecesaria e introducir dependencias externas, lo que reduce la solidez y mantenibilidad del código. También tienden a abstraer la lógica subyacente del código, lo que disminuye su legibilidad. Los autores del artículo de HDSR expresan el equilibrio ideal en términos sencillos: ”Queremos que nuestro kit de herramientas sea lo más simple posible, pero no más simple”.

No se repita

Si se encuentra copiando, pegando y modificando con frecuencia los mismos bloques de código, quizá le resulte más útil una función que agrupe el código repetido en un solo lugar. Los parámetros de dicha función pueden reflejar los elementos que varían de una instancia a otra. Esto ordena el código y simplifica su mantenimiento, ya que le permite ajustar todas las instancias de esa función en un solo paso y en un solo lugar (en vez de tener que ajustar individualmente cada duplicado de un bloque de código).

Realice comprobaciones de coherencia

Las comprobaciones de coherencia son validaciones automáticas que verifican si los posibles datos o estados del sistema se ajustan a reglas lógicas y convenciones predefinidas, lo que ayuda a evitar y a tener en cuenta conflictos y contradicciones imprevistos en el código. Estas pruebas automatizadas suelen ser un componente estándar y crítico de los pipelines de CI/CD (integración continua/implementación continua).

Este es un ejemplo paradigmático de la importancia de que el código sea fácil de probar. Resulta difícil, o incluso imposible, diseñar pruebas unitarias que validen exhaustivamente todas las funciones si el código es demasiado complejo o contiene demasiadas dependencias estrechamente entrelazadas.

Aplique el control de versiones

Los sistemas de control de versiones ayudan a fomentar la coherencia, el control de calidad, la coordinación y los procesos de revisión de código entre equipos. Al utilizar marcos de codificación impulsados por IA (especialmente aquellos que podrían ajustar de forma autónoma su base de código), asegúrese de disponer de un medio para revertir fácilmente cualquier cambio no deseado o adverso. IBM Bob, por ejemplo, crea automáticamente versiones de los archivos de su espacio de trabajo como puntos de control para permitir una fácil reversión de los cambios de código cuando sea necesario.

¿Qué caracteriza a un código deficiente?

En términos generales, un código deficiente es difícil de leer y mantener, frágil ante los cambios y las nuevas características, ineficiente y poco fiable. A menudo presenta deficiencias innecesarias, en las que diferentes módulos se entrelazan entre sí y cualquier cambio en uno de ellos requiere trabajo adicional para evitar el otro deje de funcionar. Carece de una documentación adecuada y está mal organizado, desprovisto de una estructura coherente y lógica, una situación a la que a menudo se hace referencia como ”código espagueti”.

El código deficiente suele ser el resultado no (solo) de unas habilidades de codificación deficientes, sino también de incentivos inadecuados y de una estructura organizativa deficiente: dar prioridad de forma excesivamente agresiva al lanzamiento de nuevas características a expensas de la calidad del código suele traducirse en una comercialización más rápida, pero también en mayores complicaciones futuras y en una mayor deuda técnica.

Es importante recordar que el código deficiente suele funcionar, al menos temporalmente. La deuda técnica no se acumularía si no fuera así, ya que el código que sin duda está defectuoso tendría que ser corregido. Refactoring: Improving the Design of Existing Code, el libro de referencia de Martin Fowler y Kent Beck publicado por primera vez en 1999 (y actualizado en numerosas ocasiones desde entonces), utilizó por tanto el término code smells para describir el código defectuoso. No suelen ser errores y no impiden de por sí que un programa funcione, pero indican debilidades de diseño y problemas de calidad del código que podrían ralentizar el desarrollo o provocar errores en el futuro.

La lista de “code smells” de los que hay que tener cuidado, elaborada por Fowler y Beck, incluye ejemplos como:

  • Función larga (método largo): un método que contiene demasiadas líneas de código.

  • Clase grande: una clase que intenta abarcar demasiado y contiene demasiadas variables, por lo que carece de cohesión.

  • Obsesión por los primitivos: el uso de tipos de datos primitivos en lugar de pequeños objetos especializados.

  • Nombre misterioso: funciones o variables con nombres poco acertados que ocultan su verdadera intención.

  • Agrupaciones de datos: grupos de variables que aparecen juntas con frecuencia en todas partes.

  • Elementos perezosos: clases o funciones que hacen muy poco para justificar su existencia.

  • Lista larga de parámetros: funciones que requieren demasiados argumentos para funcionar correctamente.

  • “Shotgun surgery”: un cambio requiere modificar muchos módulos dispersos simultáneamente (lo que es esencialmente lo contrario de una clase grande).

  • Código duplicado: estructuras de código idénticas o muy similares en varios lugares.

Puede encontrar una lista exhaustiva de code smells, con explicaciones, ejemplos y citas, aquí. Su presencia suele ser una señal de que es necesaria una refactorización. Una comprensión profunda y generalizada en toda la organización de estos problemas y de las complicaciones que se derivan de ellos resulta útil para establecer una concepción compartida de las normas de calidad entre los equipos de desarrollo.

AI Academy

El auge de la IA generativa para empresas

Conozca el auge histórico de la IA generativa y lo que significa para las empresas.

Medir la calidad del código

Medir la calidad del código siempre debe implicar una evaluación tanto cualitativa como cuantitativa. Si bien las métricas objetivos, como la complejidad ciclomática, pueden resultar útiles, también pueden llevar a conclusiones erróneas si no se sitúan en el contexto adecuado.

Por ejemplo, su equipo podría escribir un conjunto de pruebas automatizadas y su código podría alcanzar una cobertura del 100 % en una batería de pruebas unitarias. Pero si el conjunto de pruebas carece de algunas de las aserciones significativas necesarias para validar genuinamente que su código funciona plenamente según lo requerido, la falsa confianza derivada de esa cobertura de pruebas podría hacer más daño que bien.

Del mismo modo, una estructura de revisión sólida debe incluir tanto la revisión manual del código como la revisión del código mediante IA. Una herramienta de codificación agéntica moderna como IBM Bob puede realizar un análisis estático exhaustivo del código y refactorización en tiempo real, pero se beneficia enormemente de reglas y modos personalizados que transmiten las necesidades y la intención específicas del desarrollador. Los humanos no son exhaustivos y la IA no es infalible, pero reforzar a los unos con lo otro es la forma más segura de confirmar que se han investigado todos los posibles problemas con el contexto adecuado.

Recuerde siempre que la calidad del código depende del contexto. Imagine que un programador de su equipo ha escrito un algoritmo o un bloque de código elocuente, eficiente y perfectamente estructurado que cumple con precisión la función prevista. Si el problema se hubiera podido resolver eficazmente utilizando una función estándar de una biblioteca integrada con la que todo el mundo ya está familiarizado, ese código tan elocuente se convierte, en realidad, en un problema de calidad, ya que añade una complejidad innecesaria y una carga mental adicional.

Autor

Dave Bergmann

Senior Staff Writer, AI Models

IBM Think

Soluciones relacionadas
IBM Bob

Acelere la entrega de software con Bob, su socio de IA para un desarrollo seguro y consciente de la intención.

Explore IBM® Bob
Soluciones de codificación de IA

Optimice los esfuerzos de desarrollo de software con herramientas impulsadas por IA de confianza que minimizan el tiempo dedicado a escribir código, depurar, refactorizar código o completar código y dejar más espacio para la innovación.

Explore soluciones de codificación con IA
Consultoría y servicios de IA

Reinvente las operaciones y flujos de trabajo críticos añadiendo IA para maximizar las experiencias, la toma de decisiones en tiempo real y el valor empresarial.

Explore los servicios de consultoría de IA
Dé el siguiente paso

Aproveche la IA generativa y la automatización avanzada para crear código listo para uso empresarial con mayor rapidez. Los modelos de Bob aumentan las habilidades de los desarrolladores, simplificando y automatizando sus esfuerzos de desarrollo y modernización.

  1. Descubra IBM® Bob
  2. Explore soluciones de codificación con IA
Notas a pie de página

1. “Code red: the business impact of code quality – a quantitative study of 39 proprietary production codebases”. Actas de la International Conference on Technical Debt (consultadas a través de la Association for Computing Machinery Digital Libtary). 16 de agosto de 2022

2. “Fostering Better Coding Practices for Data Scientists”. Harvard Data Science Review. 27 de julio de 2023.