El code smell es una in­di­ca­ción de la mala calidad del código. Si hay code smells, quien lea el código tendrá la sensación de que algo está mal. Te mostramos di­fe­re­n­tes code smells y te ex­pli­ca­mos cómo puedes des­ha­ce­r­te de ellos.

¿Qué es el code smell?

Un code smell es una ca­ra­c­te­rí­s­ti­ca del código que permite a los pro­gra­ma­do­res ex­pe­ri­me­n­ta­dos intuir que el código no está limpio. Tener un buen “olfato” para los code smells es co­m­pa­ra­ble a la intuición de un maestro artesano. Si ves una maraña de cables o unas tuberías mal colocadas, enseguida te das cuenta de que algo no funciona bien. En el caso del código fuente estos patrones re­ve­la­do­res tienen un aspecto visual diferente.

Martin Fowler, un conocido pro­gra­ma­dor británico, apunta a su compañero Kent Beck como el que creó el término. En el blog de Fowler aparece la siguiente de­fi­ni­ción:

Cita

“A Code smell is a surface in­di­ca­tion that usually co­rre­s­po­n­ds to a deeper problem in the system.” – Fuente: https://ma­r­ti­n­fo­w­ler.com/bliki/CodeSmell.html
Tra­du­c­ción: “El code smell es una in­di­ca­ción su­pe­r­fi­cial que suele hacer re­fe­re­n­cia a un problema más profundo en el sistema.” (traducido por IONOS)

Por lo tanto, un code smell indica un problema sistémico. Puede que el código lo es­cri­bie­se un pro­gra­ma­dor sin los co­no­ci­mie­n­tos su­fi­cie­n­tes o que di­fe­re­n­tes pro­gra­ma­do­res hayan ido tu­r­ná­n­do­se. En este último caso, las di­fe­re­n­cias en el nivel de co­m­pe­te­n­cia, el co­no­ci­mie­n­to de la base de código y la fa­mi­lia­ri­dad con las di­re­c­tri­ces y las normas de cada pro­fe­sio­nal conducen a una mala calidad del código. Es decir, el código puede empezar a “oler mal”.

No hay que confundir el code smell con las de­fi­cie­n­cias puntuales, que son de esperar en toda base de código. En primer lugar, el code smell es úni­ca­me­n­te una in­di­ca­ción. Si un pro­gra­ma­dor detecta un code smell, debe comprobar más a fondo si realmente hay un problema sistémico.

Si resulta evidente que el código “huele lo su­fi­cie­n­te mal” por ser de­fi­cie­n­te, hay di­fe­re­n­tes maneras de limpiarlo. A menudo se puede recurrir a enfoques como la re­fa­c­to­ri­za­ción, que sirven para mejorar la es­tru­c­tu­ra del código ma­n­te­nie­n­do su fu­n­cio­na­li­dad. Sin embargo, de­pe­n­die­n­do del tamaño y la co­m­ple­ji­dad del código, puede ser imposible eliminar los code smells. En ese caso, la única opción es el “Re-write”, es decir, re­es­cri­bir el código desde cero.

¿Cuáles son los di­fe­re­n­tes tipos de code smells?

El código es un medio que existe a di­fe­re­n­tes niveles de ab­s­tra­c­ción. Por ejemplo, en las apli­ca­cio­nes el código define una multitud de piezas in­di­vi­dua­les: variables, funciones, clases y módulos. Para co­n­si­de­rar los di­fe­re­n­tes code smells, hay que di­s­ti­n­guir entre los niveles de ab­s­tra­c­ción:

  1. Code smells de forma general
  2. Code smells a nivel de función
  3. Code smells a nivel de clase
  4. Code smells a nivel de la apli­ca­ción

Code smells de forma general

Los code smells, por lo general, pueden en­co­n­trar­se en todos los niveles de una apli­ca­ción. La mayoría de las veces no son errores graves, sino patrones que tienen un impacto negativo. En pro­gra­ma­ción hay una regla general que establece que el código debe estar pri­n­ci­pa­l­me­n­te escrito para los usuarios. Sobre todo en el caso de los pro­gra­ma­do­res in­e­x­pe­r­tos, el código ofrece el resultado deseado, pero no es in­co­m­pre­n­si­ble.

Mala no­me­n­cla­tu­ra en la es­tru­c­tu­ra del código

Utilizar la no­me­n­cla­tu­ra correcta de las co­n­s­tru­c­cio­nes de código, como las variables y las funciones, es todo un arte. La­me­n­ta­ble­me­n­te, a menudo aparecen en el código nombres sin sentido, absurdos o incluso co­n­tra­di­c­to­rios. El mejor ejemplo lo co­n­s­ti­tu­yen los nombres de variables que constan de una sola letra: x, i, n, etc. Como estos nombres no dan ningún contexto, el si­g­ni­fi­ca­do y la finalidad no son obvios.

Es mejor utilizar nombres ex­plí­ci­tos. De esta forma, el código se lee de forma natural, como un idioma, y es más fácil tanto seguir el flujo del programa como reconocer las in­cohe­re­n­cias del mismo.

Falta de uni­fo­r­mi­dad en el estilo del código

Un código limpio parece haber sido escrito de una sola tirada; se puede ver de inmediato que se creó a partir de ciertas reglas. Si falta dicha uni­fo­r­mi­dad, se trata de un code smell.

La falta de ho­mo­ge­nei­dad en la forma de nombrar variables sugiere que varias personas es­cri­bie­ron partes del código de forma in­de­pe­n­die­n­te. Si se trata de un equipo, puede re­su­l­tar­les útil tener unas normas pre­de­fi­ni­das.

Consejo

¿Curioso? Lee también nuestro artículo “¿Qué es el clean code?”.

Variables sin definir

Algunos lenguajes como C++, Java y Ja­va­S­cri­pt permiten declarar variables sin darles un valor inicial. La variable existe desde su creación, pero no está definida, lo que puede dar lugar a pequeños errores. Este code smell es es­pe­cia­l­me­n­te crítico en los lenguajes dé­bi­l­me­n­te tipados. El hecho de no definirla ini­cia­l­me­n­te, junto a que el lenguaje no controla los tipos de variables, hace imposible saber qué tipo de variable se ha creado.

Valores co­di­fi­ca­dos en el código

Este error lo suelen cometer los pro­gra­ma­do­res in­e­x­pe­r­tos: para comparar una variable con un valor concreto, in­tro­du­cen el valor di­re­c­ta­me­n­te (se denomina “valores co­di­fi­ca­dos”). Los valores co­di­fi­ca­dos son pro­ble­má­ti­cos si aparecen en varios lugares del mismo programa, pues las copias in­di­vi­dua­les del valor tienden a mutar de forma in­de­pe­n­die­n­te con el tiempo.

Es mejor definir una constante para el valor. Esto crea una “Single Source of Truth” (SSOT): todo el código que necesita el valor accede a la misma constante definida en un único lugar.

Números mágicos en código

Un número mágico es un caso especial de valores co­di­fi­ca­dos. Imagina que el código funciona con valores limitados a 24 bits. 24 bits co­rre­s­po­n­den a 16 777 216 valores posibles o a los números de 0 a 16 777 215.

De­s­gra­cia­da­me­n­te, falta añadir un co­me­n­ta­rio, por lo que no está claro, a po­s­te­rio­ri, cómo se ha llegado a ese valor. Resultado: ha surgido un número mágico. El de­s­co­no­ci­mie­n­to del origen del valor aumenta el riesgo de que se modifique por error.

Estos errores pueden evitarse al incluir la de­fi­ni­ción del valor como una expresión al asignar la constante.

Se­n­te­n­cias de control pro­fu­n­da­me­n­te anidadas

En la mayoría de los lenguajes, es posible cambiar las co­n­s­tru­c­cio­nes de código a cualquier pro­fu­n­di­dad. Por desgracia, esto también aumenta su co­m­ple­ji­dad, lo que hace más difícil en­te­n­de­r­lo cuando se lee. Un code smell es­pe­cia­l­me­n­te común es el de las se­n­te­n­cias de control pro­fu­n­da­me­n­te anidadas, como los bucles y las ramas.

Hay varias maneras de resolver una anidación como, por ejemplo, uti­li­za­n­do el operador booleano AND para colocar ambas co­n­di­cio­nes dentro de una misma sentencia if:

Otro método que funciona con cualquier número de co­n­di­cio­nes utiliza “Guard Clauses”.

Aquí un ejemplo más complejo. Si quieres analizar los datos que se puedan necesitar preparar primero, como parte de la pre­pa­ra­ción, debes no­r­ma­li­zar algunos elementos de datos. El primer enfoque contiene cuatro niveles de se­n­te­n­cias de control anidadas:

Nota

La ca­li­fi­ca­ción de “pro­fu­n­da­me­n­te anidado” obedece a una medida subjetiva. Como regla general se establece que las se­n­te­n­cias de control deben estar anidadas a una pro­fu­n­di­dad máxima de tres niveles, en alguna excepción un máximo de cuatro. Más profundo casi nunca es aco­n­se­ja­ble o siquiera necesario. Si te tienta anidar a una mayor pro­fu­n­di­dad, deberías co­n­si­de­rar el re­fa­c­to­ri­ng.

El code smell a nivel funcional

En la mayoría de los lenguajes, las funciones son la unidad básica de ejecución del código. Hay piezas de código más pequeñas, como las variables y las ex­pre­sio­nes, pero estas son in­de­pe­n­die­n­tes. Escribir funciones sencillas y claras requiere cierta ex­pe­rie­n­cia. Mostramos algunos de los code smells más comunes a nivel de función.

Falta de atención a las ex­ce­p­cio­nes

Las funciones reciben ar­gu­me­n­tos a modo de valores de entrada. En muchos casos, solo son válidos ciertos valores o rangos de valores. Es re­s­po­n­sa­bi­li­dad de los pro­gra­ma­do­res comprobar la au­te­n­ti­ci­dad de los valores in­tro­du­ci­dos y gestionar las ex­ce­p­cio­nes en co­n­se­cue­n­cia.

Acceso a la variable desde un nivel superior de la función

Las áreas de validez son una ca­ra­c­te­rí­s­ti­ca fu­n­da­me­n­tal de la mayoría de los lenguajes de pro­gra­ma­ción. De­te­r­mi­nan qué nombres se definen en qué lugares del código. Los ámbitos su­bo­r­di­na­dos heredan de los su­pe­rio­res. Si se accede a una variable definida ex­te­r­na­me­n­te dentro de una función, se convierte en un code smell. Esto se debe a que el valor puede haber cambiado entre que se definió la variable y cuando se llama a la función.

Lo ideal es que solo se acceda a los valores que aparecen como se­n­te­n­cias dentro de las funciones. Se utilizan pa­rá­me­tros por defecto para evitar tener que pasar el mismo valor repetidas veces:

Nota

El acceso a una variable desde fuera del ámbito de una función que la encierra crea un “Closure”. No se trata de code smell. Los cierres son un concepto im­po­r­ta­n­te en la pro­gra­ma­ción de Ja­va­S­cri­pt.

Code smells a nivel de clase

La pro­gra­ma­ción orientada a objetos (OOP) puede ayudar a aumentar la re­uti­li­za­ción del código y reducir su co­m­ple­ji­dad. De­s­gra­cia­da­me­n­te, hay muchas ocasiones en las que se pueden cometer errores en el diseño de las clases que luego des­en­ca­de­nan en un code smell.

Uno de los code smells más comunes a nivel de clase es el “God Object” o su co­rre­s­po­n­die­n­te “Clase Dios”. Un god object combina todo tipo de fu­n­cio­na­li­da­des que no­r­ma­l­me­n­te no van juntas y, por tanto, violan el principio de “se­pa­ra­ción de intereses”.

Los code smells se en­cue­n­tran a menudo en relación con la jerarquía de la herencia. A veces el código se di­s­tri­bu­ye in­ne­ce­sa­ria­me­n­te a través de múltiples niveles de herencia. A menudo se comete el error de utilizar la herencia en lugar de la co­m­po­si­ción como enfoque principal para componer objetos.

El uso in­vo­lu­n­ta­rio de “Data Classes” también se considera un code smell. Son clases que no im­ple­me­n­tan su propio fu­n­cio­na­mie­n­to. Si no se definen métodos aparte de los getters y setters genéricos, se debe co­n­si­de­rar el uso de una es­tru­c­tu­ra de datos más simple como un Dict o un Struct.

Code smells a nivel de apli­ca­ción

Es la pesadilla de todo pro­gra­ma­dor: que te designen a trabajar en una apli­ca­ción existente y con un primer vistazo al código veas que toda la apli­ca­ción se encuentra en un único archivo enorme, donde no queda claro cómo se re­la­cio­nan los co­m­po­ne­n­tes in­di­vi­dua­les del código. Como un plato de fideos, todo se desmadra en el “Spaghetti Code”.

Faltan todas las ab­s­tra­c­cio­nes, no hay su­b­di­vi­sión entre las clases y, en el mejor de los casos, solo hay unas pocas funciones. En cambio, hay todo tipo de du­pli­ca­cio­nes de código que dan lugar a pequeños bugs. Además, se utilizan mucho las variables globales.

El uso de variables globales es un code smell es­pe­cia­l­me­n­te invasivo, pues estas pueden ser po­te­n­cia­l­me­n­te mo­di­fi­ca­das desde cualquier parte del código, lo que abre la puerta a errores difíciles de corregir. Para limpiar el código, te ves en la obli­ga­ción de buscar errores por todas sus secciones.

¿Cómo se puede evitar un code smell?

En sí, es pre­fe­ri­ble mantener el código limpio en todo momento. La mejor manera de hacerlo es seguir los si­guie­n­tes pasos:

  1. Prototype: crear un diseño
  2. Test: probar su fu­n­cio­na­li­dad
  3. Refactor: limpiar el código
  4. Ship: in­co­r­po­rar el código a su entorno de pro­du­c­ción

De­s­gra­cia­da­me­n­te, el tercer punto no suele tenerse en cuenta, sobre todo cuando la decisión la toma un directivo sin ex­pe­rie­n­cia en co­di­fi­ca­ción. El ra­zo­na­mie­n­to es el siguiente: el código funciona, así que ¿por qué seguir tra­ba­ja­n­do en él? Sin embargo, a la larga, el código empieza a no oler bien y la deuda técnica se acumula.

Si un código fuente que ya existe contiene code smells, no es posible empezar desde cero. Por lo que es necesario limpiar el código. Lo mejor es proceder de forma gradual y delimitar primero las áreas del código fuente que pueden limpiarse con relativa facilidad. Separa los co­m­po­ne­n­tes que funcionan o que son me­jo­ra­bles de los “rincones oscuros” que es mejor no tocar.

Separar las áreas de código “menos apestosas” de las “más apestosas” reduce la co­m­ple­ji­dad del código. Esto facilita la co­m­pro­ba­ción del código y la adopción de medidas de debugging. También pueden ser de gran ayuda las he­rra­mie­n­tas au­to­ma­ti­za­das de code Review que detectan los code smells y ofrecen su­ge­re­n­cias o ayuda para li­m­piar­los.

Los enfoques de re­fa­c­to­ri­za­ción son eficaces para eliminar los code smells. La fu­n­cio­na­li­dad se encapsula en funciones o se dividen las funciones exi­s­te­n­tes. Eliminar las secciones de código que no se utilizan y mejorar la no­me­n­cla­tu­ra del código hace que el código fuente sea más sencillo. Las reglas de oro “Don’t Repeat Yourself” (DRY) y “You ain’t gonna need it” (YAGNI) sirven de guía.

Ir al menú principal