Explicación de "Code smells"

Publicado el 13 de julio de 2026
Primer plano extremo del rostro de un hombre enojado
By Dave Bergmann

"Code smell" (código que huele mal) es un término informal que usan los programadores para describir patrones de diseño de software que son comunes al código incorrecto. Para entender mejor, los "code smells" no son errores o defectos en sí mismos, sino señales de advertencia de la mala calidad del código.

El término “code smell” fue acuñado por Kent Beck y popularizado en el libro seminal de 1999 que escribió como coautor con Martin Fowler titulado: Refactoring: Improving the Design of Existing Code específicamente en el capítulo “Bad Smells in Code”. El propio Fowler explica de forma concisa que un "code smell" es "un indicio superficial que normalmente corresponde a un problema más profundo del sistema".1  

A diferencia de un verdadero error de software, un "code smell" no impide que el código fuente se compile, ejecute y realice la función deseada, ni su presencia siempre indica un problema real. Imagine entrar en una casa y oler algo raro o a moho. Ese olor puede provenir de alimentos caducados en el refrigerador o basura que debe sacarse; más en serio, podría deberse a moho o algo que se pudre en sus paredes, pero también podría deberse a un queso muy oloroso que es perfectamente seguro para comer. De todos modos, el olor amerita una inspección más detallada.

Para un ingeniero de software experimentado, ciertos patrones en el código simplemente no “huelen” bien. Con el tiempo, uno podría asociar instintivamente su presencia con ciertas complicaciones, ineficiencias o problemas futuros. Nombrar y popularizar "code smells" específicos facilita la familiaridad con estos patrones contraproducentes (o “antipatrones”) y los principios de diseño que violan. Esa familiaridad, a su vez, hace que estos antipatrones sean más propensos a ser notados y remediados durante la revisión de comentarios. Como dice Fowler, “un olor es, por definición, algo que es rápido de ver o olfatear”.

Si los "code smells" no se abordan, son los principales contribuyentes a la deuda técnica. Como afirma un estudio, el 75 % de los defectos detectados en las revisiones de código no afectan la ejecución del programa, pero “impactan en la capacidad de evolución del software”.² Incluso si un cierto antipatrón o mal hábito no causa un error hoy en día, aumenta significativamente el riesgo de errores, bloqueos o vulnerabilidades de seguridad en su base de código que surja más adelante. Como mínimo, la presencia de "code smells" conocidos reduce la legibilidad y la capacidad de mantenimiento de su código, lo que dificulta su comprensión, actualización y mejora.

Tipos de "code smells"

Aunque muchos "code smell" comunes continúan siendo conocidos por los nombres acuñados por Beck y Fowler en Refactoring, no existe una única lista canónica y universalmente aceptada de "code smells". Pero si un "code smell" determinado debe denominarse con un nombre u otro, o categorizarse como su propio antipatrón en lugar de un subtipo de otro, es irrelevante en gran medida. Lo que importa es si el nombre y la descripción de un "code smell" son lo suficientemente claros e intuitivos como para ayudar a los equipos de desarrollo de software a compartir e implementar principios de diseño productivos.

Asimismo, no existe una taxonomía universalmente aceptada para las diferentes categorías de "code smells". Este artículo se basa principalmente en la taxonomía propuesta por Jerzyk y Madeyski en 2023.3 Sus hallazgos se publicaron en el útil catálogo que se mantiene en codesmells.org, el cual proporciona contexto adicional, ejemplos de código y técnicas de refactorización adecuadas para cada uno.

Jerzyk y Madeyski señalan que la taxonomía más comúnmente referenciada de "code smells" sigue las cinco agrupaciones propuestas por Mäntylä y Lassenius en 2006, quienes categorizan los 22 "code smells" introducidos por Fowler y Beck (y uno más propio) en cinco agrupaciones distintas: "Bloaters", "Object-Orientation Abusers", "Change Preventers", "Dispensables" y "Couplers". 4 Después de su propio intento de realizar una revisión exhaustiva tanto de la literatura publicada formalmente como del "material gris" (como blogs, foros y wikis), Jerzyk y Madeyski catalogaron 56 "code smells" diferentes en 9 grupos (que incluyen los 5 mencionados anteriormente en este párrafo).

Aunque esta sección sigue mayormente la estructura de la 9 categorías que propusieron, es importante señalar que tales agrupaciones son informales y subjetivas: la taxonomía más útil es la que te resulte más intuitiva. Comprender los diferentes tipos de complicaciones o ineficiencias que pueden introducir los "code smells" puede proporcionar una concepción más significativa de los "code smells" que la memorización de olores individuales. Como articula Mäntylä, “con una larga lista plana de "code smells" es fácil perder el panorama general”.5 

Bloaters

Los "bloaters" son "code smells" que a menudo hacen que métodos, clases o bloques de código crezcan tanto que se vuelven inmanejables. Esto disminuye la legibilidad y hace que el código sea más difícil de mantener o modificar.

Algunos ejemplos de "bloaters" incluyen:

  • Agrupaciones de datos: grupos de variables que suelen aparecer juntas en todas partes.

  • Clase grande: una clase que intenta hacer demasiado y contiene demasiadas variables, sin cohesión.

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

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

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

Agentes que impiden el cambio

Los "change preventers" (factores que impiden el cambio) como su nombre indica, son "code smells" que dificultan la capacidad de modificar, agregar o desarrollar aún más software. Una instancia típica de un mecanismo de prevención de cambios es una estructura de código que le obliga a realizar una serie de ediciones en varios lugares solo para implementar una única y simple modificación.

Este tipo de "code smells" violan el principio de responsabilidad única (SRP) de Robert C. Martin, que afirma que los cambios en cualquier módulo de código solo deberían poder originarse en un solo lugar. Como sugiere Martin en una entrada en el blog sobre SRP: “Reúna las cosas que cambian por las mismas razones. Separar esas cosas que cambian por diferentes razones”.6

Algunos ejemplos de factores que impiden el cambio son:

  • "Shotgun surgery": Implementar un solo cambio requiere modificaciones en muchos módulos dispersos simultáneamente (lo cual es esencialmente lo opuesto a una clase grande).

  • Cambio divergente: Al igual que la inversa de la cirugía de escopeta, implementar un solo cambio requiere muchas modificaciones dentro de una sola clase.

  • Infierno de callback: Estructura de código en la que muchos métodos están anidados profundamente unos dentro de otros a través de muchas pestañas sangradas y corchetes, ocultando causa y efecto y dificultando la lectura y el mantenimiento del código.

Acopladores

Los acopladores son "code smells" que violan el principio de diseño central de acoplamiento bajo al crear dependencias excesivas entre diferentes clases. Esto reduce la reutilización del código y complica (o incluso impide) las pruebas unitarias independientes.

Algunos ejemplos de couplers son:

  • Envidia de las características: Un método accede excesivamente a los datos de otro objeto.

  • Uso de información de usuario interno (también conocido como intimidad inapropiada): Una clase utiliza los campos internos o métodos de otra clase.

  • Cadenas de mensajes: Una larga secuencia de llamadas a métodos encadenadas entre objetos.

Distribuidores de datos

Los operadores de datos hacen pasar los datos por muchas más clases o funciones de las necesarias. Esto crea dependencias y complejidad innecesarias, lo que a menudo da como resultado elementos de código que simplemente contienen o distribuyen datos sin proporcionar ningún comportamiento significativo.

Algunos ejemplos de distribuidores de datos son:

  • Intermedio: Una clase cuyo único trabajo es delegar a otros.

  • Datos de tramp: Cuando los datos “hacen autostop” a través de una cadena de métodos que no hacen ningún uso de ellos.

  • Datos globales: Estructura en la que las variables pueden ser modificadas desde cualquier lugar del código base, haciendo que cada función del código base sea sospechosa cuando algo se rompe.

Dispensables

Los dispensables son, intuitivamente, elementos de código prescindibles: su eliminación haría que la base de código sea más limpia y más fácil de leer sin ningún impacto significativo en la funcionalidad general.

Ejemplos de dispensables incluyen:

  • Comentarios: Si bien los comentarios son generalmente algo bueno, a veces se usan como un “desodorante” para los "code smells", explicando en lugar de mejorar. Por ejemplo, si un comentario describe lo que sucede en una sección particular del código, se está empleando para ocultar código que no es lo suficientemente intuitivo o legible por sí mismo. Los comentarios individuales también pueden volverse redundantes u obsoletos con el tiempo. Los comportamientos de comentarios más específicos aparecen en otros "code smells".

  • Clase de datos: Clases que contienen únicamente campos y métodos de acceso, sin un comportamiento significativo.

  • Código muerto: Elementos de código que ya no se ejecutan porque, con el tiempo, la refactorización y otras modificaciones los han hecho obsoletos.

  • Código duplicado: Estructuras de código idénticas o muy similares en múltiples lugares.

  • Clase perezosa (también conocida como elementos perezosos): Clases o funciones que hacen muy poco para existir.

  • Generalidad especulativa: Se agregó código innecesario para soportar características futuras hipotéticas.

Abusadores funcionales

Los abusadores funcionales son "code smells" que evitan los principios de programación orientada a objetos forzando patrones funcionales a una base de código orientada a objetos.

Algunos ejemplos de abusadores funcionales son:

  • Bucles: Uso de bucles tradicionales en lugar de operaciones modernas de pipelines. Aunque Fowler consideraba que casi todos los bucles eran obsoletos, Jerzyk planteó que los bucles imperativos, en particular, son el problema principal.

  • Datos mutables: Variables cuyo estado cambia inesperadamente, causando efectos secundarios impredecibles.

  • Efectos secundarios (también conocido como funciones impuras): Métodos que hacen más de lo que su nombre y propósito primario implicaría.

Abusadores léxicos

Los abusadores léxicos son "code smells" que se originan en convenciones de nomenclatura deficientes, formato inconsistente o sintaxis críptica. En pocas palabras, son casos en los que la redacción del código no coincide intuitivamente con el comportamiento correspondiente, dificultando la legibilidad.

 Algunos ejemplos de abusadores léxicos son:

  • Número mágico: Números sin nombre insertados en el código sin explicación o contexto adecuados.

  • Comentario falaz: Comentarios que ya no son precisos porque el código a su alrededor ha cambiado. Debido a que los comentarios no se ejecutan en realidad, a menudo escapan de linters y otras comprobaciones automatizadas.

  • Nombre misterioso: Funciones o variables mal nombradas, ocultando su intención real. 

  • Nombre del método falaz: Funciones cuyos nombres son activamente engañosos, basados en convenciones y expectativas comunes. Por ejemplo, una función llamada getSomething que en realidad no devuelve nada.

Ofuscadores

Los ofuscadores son elementos de código escritos de una manera innecesariamente complicada, compleja o “inteligente” que entierra la intención subyacente del código detrás de una abstracción innecesaria. Estos "code smells" hacen que el código fuente sea confuso de leer y, por lo tanto, dificulta que los programadores en el futuro lo comprendan y modifiquen según sea necesario.

Algunos ejemplos de ofuscadores son:

  • Separación vertical: distancias innecesariamente grandes entre elementos de código relevantes relacionados, como una variable declarada en la parte superior de un método que no se usa hasta 50 líneas después.

  • Intento oscurecido: La categoría más amplia de funciones, variables, nombres y números cuyo propósito no es intuitivo ni se deja claro en contexto.

  • Expresión booleana complicada: flujos lógicos enrevesados, como los que contienen dobles negativos o cadenas complejas de IF y IF NOT y AND y AND NOT .

  • Código ingenioso: Código que funciona, pero sustituye a un lenguaje personalizado difícil de seguir en situaciones para las que ya existen soluciones integradas ampliamente entendidas y otras soluciones convencionales.

Abusadores orientados a objetos

Los abusadores orientados a objetos (o abusadores de orientación a objetos) son "code smells" que no aplican completa o correctamente los principios de diseño orientado a objetos. Por ejemplo, las declaraciones de switch son útiles en la programación de procedimientos, pero deben evitarse en la programación orientada a objetos.

Algunos ejemplos de abusadores orientados a objetos incluyen:

  • Clase alternativa con diferentes interfaces: Clases que realizan funciones similares usando nombres de métodos completamente diferentes.

  • Legado rechazado: Subclases que no necesitan ni emplean métodos heredados.

  • Sentencias switch (también conocidas como Complejidad Condicional, también conocidas como Switches Repetidos): La misma sentencia switch exacta duplicada en toda la base de código.

  • Campo temporal: Variable creada donde a menudo no es necesaria, generalmente se usa solo en determinadas circunstancias.

Refactorización de "code smells"

El principal beneficio de entender los "code smells" es que reconocerlos permite una refactorización más eficaz: la práctica de actualizar el código fuente sin modificar su comportamiento o funcionalidad externa. Ordenar el código de forma rutinaria mediante refactorización es esencial para reducir la deuda técnica y facilitar mejoras y adiciones rápidas y efectivas a tu base de código con el tiempo.

La refactorización puede entenderse en gran medida como el proceso de identificar y dirección los "code smells". Su objetivo es solucionar los problemas antes de que afecten negativamente a la funcionalidad, momento en el que el proceso se parece más a la depuración que a la refactorización.

Las plataformas modernas de ingeniería agéntica, como IBM® Bob, suelen ofrecer sugerencias de refactorización automatizadas en tiempo real. A través de una amplia capacitación sobre "code smells" comunes en el contexto de bases de código reales, en lugar de la memorización de definiciones de "code smells", las capacidades de refactorización de código de IA de estas plataformas permiten a los desarrolladores escalar su salida sin un aumento correspondiente en el código generado por IA difícil de usar que será difícil de entender y modificar más adelante.

Autor

Dave Bergmann

Senior Staff Writer, AI Models

IBM Think

Soluciones relacionadas
IBM® Bob

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

Explore IBM Bob
Soluciones de IA para desarrolladores

Desarrolle, despliegue y gestione aplicaciones de IA más rápido con herramientas preparadas para empresas.

Explore la IA para desarrolladores
Servicios de modernización de aplicaciones

Redefina los sistemas heredados con una modernización inteligente de la IA.

Explore los servicios de modernización de aplicaciones
Dé el siguiente paso

Aproveche la IA generativa y la automatización avanzada para entregar código preparado para empresas con mayor rapidez y congruencia. Los modelos Bob potencian las competencias de los desarrolladores, optimizando los flujos de trabajo de modernización y simplificando tareas complejas de desarrollo.

  1. Descubra el agente de programación de IA
  2. Explore soluciones de IA para desarrolladores