Catálogo de olores de código
Una referencia completa de los olores de código basados en Martin Fowler Refactorización (2a edición). Los olores de código son síntomas de problemas más profundos, indican que algo podría estar mal con el diseño de tu código.
"Un olor a código es una indicación superficial que generalmente corresponde a un problema más profundo en el sistema." — Martin Fowler
Bloaters
Los olores de código representan algo que ha crecido demasiado grande para ser manejado con eficacia.
Método largo
Señales: - Método supera 30-50 líneas - Necesidad de desplazarse para ver todo el método - Múltiples niveles de anidación - Comentarios que explican lo que hacen las secciones
Por qué es malo: - Difícil de entender - Difícil de probar en aislamiento - Los cambios tienen consecuencias no deseadas - La lógica duplicada se esconde dentro
Refactorización: - Método de extracción - Reemplazar la temperatura con la pregunta - Introduzca el objeto del parámetro - Reemplazar el método con el objeto del método - Descomponer condicional
Ejemplo (Antes):
function processOrder(order) {
// Validate order (20 lines)
if (!order.items) throw new Error('No items');
if (order.items.length === 0) throw new Error('Empty order');
// ... more validation
// Calculate totals (30 lines)
let subtotal = 0;
for (const item of order.items) {
subtotal += item.price * item.quantity;
}
// ... tax, shipping, discounts
// Send notifications (20 lines)
// ... email logic
}
Ejemplo (después):
function processOrder(order) {
validateOrder(order);
const totals = calculateOrderTotals(order);
sendOrderNotifications(order, totals);
return { order, totals };
}
Clase grande
Señales: - Clase tiene muchas variables de instancia (>7-10) - Clase tiene muchos métodos (>15-20) - Nombre de clase es vago (Gestor, Manejador, Procesador) - Los métodos no utilizan todas las variables de instancia
Por qué es malo: - Viola el principio de la responsabilidad única - Difícil de probar - Cambios en las características no relacionadas - Difícil de reutilizar piezas
Refactorización: - Clase de extracción - Subclase de extracción - Interfaz de extracción
Detección:
Lines of code > 300
Number of methods > 15
Number of fields > 10
Obsesión primitiva
Señales: - Uso de primitivos para conceptos de dominio (cadena para correo electrónico, int por dinero) - Arranca de primitivos en lugar de objetos - Constantes de cuerdas para códigos de tipo - Números mágicos/cadenas
Por qué es malo: - No hay validación a nivel de tipo - Lógica dispersa en la base de código - Fácil de pasar valores incorrectos - Faltan conceptos de dominio
Refactorización: - Reemplazar Primitivo con Objeto - Reemplazar Código de Tipo con Clase - Reemplazar Código de Tipo con Subclases - Reemplazar Código de Tipo con Estado/Estrategia
Ejemplo (Antes):
const user = {
email: 'john@example.com', // Just a string
phone: '1234567890', // Just a string
status: 'active', // Magic string
balance: 10050 // Cents as integer
};
Ejemplo (después):
const user = {
email: new Email('john@example.com'),
phone: new PhoneNumber('1234567890'),
status: UserStatus.ACTIVE,
balance: Money.cents(10050)
};
Lista de parámetros largos
Señales: - Métodos con 4+ parámetros - Parámetros que siempre aparecen juntos - Banderas booleanas cambiando el comportamiento del método - Nulo/indefinido pasado con frecuencia
Por qué es malo: - Difícil de llamar correctamente - Confusión de orden de parámetros - Indica método haciendo demasiado - Difícil de añadir nuevos parámetros
Refactorización: - Introducir el objeto parámetro - Conservar el objeto completo - Reemplazar el parámetro con la llamada del método - Eliminar el argumento de la bandera
Ejemplo (Antes):
function createUser(firstName, lastName, email, phone,
street, city, state, zip,
isAdmin, isActive, createdBy) {
// ...
}
Ejemplo (después):
function createUser(personalInfo, address, options) {
// personalInfo: { firstName, lastName, email, phone }
// address: { street, city, state, zip }
// options: { isAdmin, isActive, createdBy }
}
Clumps de datos
Señales: - Los mismos 3 + campos aparecen juntos repetidamente - Parámetros que siempre viajan juntos - Clases con subconjuntos de campo que pertenecen juntos
Por qué es malo: - Lógica de manipulación duplicada - Abstracción faltante - Más difícil de extender - Indica clase oculta
Refactorización: - Clase de extracción - introducir el objeto del parámetro - preservar el objeto entero
Ejemplo
// Data clump: (x, y, z) coordinates
function movePoint(x, y, z, dx, dy, dz) { }
function scalePoint(x, y, z, factor) { }
function distanceBetween(x1, y1, z1, x2, y2, z2) { }
// Extract Point3D class
class Point3D {
constructor(x, y, z) { }
move(delta) { }
scale(factor) { }
distanceTo(other) { }
}
Abusadores de la orientación de objetos
Huele a uso incompleto o incorrecto de los principios OOP.
Cambiar las declaraciones
Señales: - Conmutador largo/case o si/o otras cadenas - El mismo interruptor en varios lugares - Encender códigos de tipo - Agregar nuevos casos requiere cambios en todas partes
Por qué es malo: - Viola Principio Abierto/Cerrado - Cambia la onda a todas las ubicaciones del interruptor - Difícil de extender - A menudo indica el polimorfismo faltante
Refactorización: - Reemplazar condicional con polimorfismo - Reemplazar código de tipo con subclases - Reemplazar código de tipo con Estado/Estrategia
Ejemplo (Antes):
function calculatePay(employee) {
switch (employee.type) {
case 'hourly':
return employee.hours * employee.rate;
case 'salaried':
return employee.salary / 12;
case 'commissioned':
return employee.sales * employee.commission;
}
}
Ejemplo (después):
class HourlyEmployee {
calculatePay() {
return this.hours * this.rate;
}
}
class SalariedEmployee {
calculatePay() {
return this.salary / 12;
}
}
Campo temporal
Señales: - Variables de instancia sólo utilizadas en algunos métodos - Campos configurados condicionalmente - Inicialización compleja para ciertos casos
Por qué es malo: - Confusar—el campo existe, pero podría ser nulo - Difícil de entender el estado del objeto - Indica ocultamiento de lógica condicional
Refactorización: - Clase de extracción - Introducir objetos nulos - Reemplazar campo temporal con local
Petición rechazada
Señales: - Subclase no utiliza métodos/datos heredados - Subclase anula para no hacer nada - Herencia utilizada para la reutilización de código, no la relación IS-A
Por qué es malo: - Abstracción errónea - Viola el principio de sustitución de Liskov - Jerarquía engañosa
Refactorización: - Empujar hacia abajo método / campo - Reemplazar subclase con delegado - Reemplazar herencia con delegación
Clases alternativas con diferentes interfaces
Señales: - Dos clases que hacen cosas similares - Diferentes nombres de métodos para el mismo concepto - Se podría utilizar indistintamente
Por qué es malo: - Implementaciones duplicadas - No hay interfaz común - Difícil de cambiar entre
Refactorización: - Método de cambio de nombre - Método de movimiento - Extraer Superclass - Interfaz de extracción
Preventores del cambio
Los olores que dificultan los cambios—cambiar una cosa requiere cambiar muchas otras.
Cambio divergente
Señales: - Una clase cambiada por múltiples razones diferentes - Los cambios en diferentes áreas desencadenan las mismas ediciones de clase - La clase es una "clase de Dios"
Por qué es malo: - Viola la responsabilidad única - Alta frecuencia de cambio - Fusionar conflictos
Refactorización: - Clase de Extracto - Superclase de Extracto - Subclase de Extracto
Ejemplo A User cambios de clase para: - Cambios de autenticación - Cambios de perfil - Cambios de facturación - Cambios de notificación
→ Extracto: AuthService, ProfileService, BillingService, NotificationService
Cirugía de escopeta
Señales: - Un cambio requiere ediciones en muchas clases - Pequeñas características necesitan tocar más de 10 archivos - Los cambios están dispersos, difícil de encontrar todo
Por qué es malo: Los cambios son propensos a errores.
Refactorización: - Método de movimiento - Campo de movimiento - Clase en línea
Detección: Busque: añadir un campo requiere cambios en los archivos >5.
Jerarquías de herencia paralelas
Señales: - Crear subclase en una jerarquía requiere subclase en otra - Prefijos de clase coinciden (por ejemplo, DatabaseOrder, DatabaseProduct)
Por qué es malo: - Doble mantenimiento - Acoplamiento entre jerarquías - Fácil de olvidar un lado
Refactorización: - Método de movimiento - Campo de movimiento - Eliminar una jerarquía
Dispensables
Algo innecesario que debería ser eliminado.
Observaciones (exceso)
Señales: - Comentarios explicando qué código hace - Código comentado - TODO/FIXME que se queda para siempre - Disculpas en los comentarios
Por qué es malo: - Los comentarios mienten (salir de la sincronización) - El código debe ser auto-documentación - Código muerto causa confusión
Refactorización: - Método de extracto (nombre explica qué) - Renombrar (claridad sin comentarios) - Eliminar código comentado - Introducir Aserción
Comentarios buenos contra malos:
// BAD: Explaining what
// Loop through users and check if active
for (const user of users) {
if (user.status === 'active') { }
}
// GOOD: Explaining why
// Active users only - inactive are handled by cleanup job
const activeUsers = users.filter(u => u.isActive);
Código duplicado
Señales: - El mismo código en varios lugares - Código similar con pequeñas variaciones - Copia-pega patrones
Por qué es malo: - Corrección de errores necesarios en varios lugares - Riesgo de inconsistencia - Base de código hinchada
Refactorización: - Método de extracción - Clase de extracción - Método de extracción (en jerarquías) - Método de plantilla de formulario
Regla de detección: Cualquier código duplicado 3+ veces debe ser extraído.
Clase perezosa
Señales: - La clase no hace lo suficiente para justificar la existencia. - Envoltorio sin valor añadido.
Por qué es malo: - Mantenimiento de los gastos generales - Incidencia innecesaria - Complejidad sin beneficio
Refactorización: - Clase en línea - Jerarquía de colapso
Código muerto
Señales: - Código inalcanzable - Variables/métodos/clases no utilizadas - Código comentado - Código tras condiciones imposibles
Por qué es malo: - Confusión - Carga de mantenimiento - Reduce la comprensión
Refactorización: - Eliminar código muerto - Eliminar seguro
Detección:
# Look for unused exports
# Look for unreferenced functions
# IDE "unused" warnings
Generalidad especulativa
Señales: - Clases abstractas con una subclase - Parámetros no utilizados "para uso futuro" - Métodos que sólo delegan - "Marco" para un caso de uso
Por qué es malo: - Complejidad sin beneficio - YAGNI (No vas a necesitarlo) - Más difícil de entender
Refactorización: - Jerarquía de colapso - Clase en línea - Eliminar parámetro - Cambiar el nombre del método
Acopladores
Los olores que representan el acoplamiento excesivo entre las clases.
Envidia de la característica
Señales: - Método utiliza más datos de otra clase que los suyos propios - Muchas llamadas de getter a otro objeto - Los datos y el comportamiento están separados
Por qué es malo: - Ubicación incorrecta para el comportamiento - Mal encapsulado - Difícil de mantener
Refactorización: - Método de movimiento - Campo de movimiento - Método de extracción (entonces muévase)
Ejemplo (Antes):
class Order {
getDiscountedPrice(customer) {
// Uses customer data heavily
if (customer.loyaltyYears > 5) {
return this.price * customer.discountRate;
}
return this.price;
}
}
Ejemplo (después):
class Customer {
getDiscountedPriceFor(price) {
if (this.loyaltyYears > 5) {
return price * this.discountRate;
}
return price;
}
}
Intimidad inadecuada
Señales: - Las clases acceden a las partes privadas del otro - Referencias bidireccionales - Las subclases saben demasiado sobre los padres
Por qué es malo: - Alto acoplamiento - Cambios en cascada - Difícil de modificar uno sin otro
Refactorización: - Método Mover - Campo Mover - Cambiar Bidireccional a Unidireccional - Clase de extracción - Ocultar Delegado
Cadenas de mensajes
Señales: - Cadenas largas de llamadas de métodos: a.getB().getC().getD().getValue() - El cliente depende de la estructura de navegación - Código "Arruinamiento de trenes"
Por qué es malo: - Frágil - cualquier cambio rompe cadena - Viola la Ley de Demeter - Acoplamiento a la estructura
Refactorización: - Esconder Delegado - Método de extracción - Método de movimiento
Ejemplo
// Bad: Message chain
const managerName = employee.getDepartment().getManager().getName();
// Better: Hide delegation
const managerName = employee.getManagerName();
Hombre de medio
Señales: - Clase de que sólo los delegados a otro - La mitad de los métodos son delegaciones - No hay valor añadido
Por qué es malo: - Indirectión innecesaria - Mantenimiento de los gastos generales - Arquitectura confusa
Refactorización: - Quitar el hombre medio - Método en línea
Guía de la gravedad del olor
| Severidad | Descripción | Medidas adoptadas |
|---|---|---|
| Crítico | Bloquea el desarrollo, causa errores | Arreglar inmediatamente |
| Alta | Carga de mantenimiento significativa | Fijar en el sprint actual |
| Medio | Notable pero manejable | Plan para un futuro próximo |
| Baja | Incomodidad leve | Arreglar oportunistamente |
Lista de comprobación de detección rápida
Utilice esta lista de verificación al escanear el código:
- [ ] ¿Algún método > 30 líneas?
- [ ] ¿Alguna clase > 300 líneas?
- [ ] ¿Algún método con > 4 parámetros?
- ¿Algún bloque de código duplicado?
- [ ] ¿Algún interruptor/case en códigos de tipo?
- ¿Algún código no utilizado?
- [ ] ¿Algún método usando los datos de otra clase pesadamente?
- [ ] ¿Alguna cadena larga de llamadas de método?
- ¿Algún comentario explicando "qué" no "por qué"?
- ¿Algún primitivo que deba ser objeto?
Lectura adicional
- Fowler, M. (2018). Refactorización: mejorar el diseño del código existente (2a edición)
- Kerievsky, J. (2004). Refactorización a patrones
- Plumas, M. (2004). Trabajar eficazmente con el código de legado
Última actualización: 4 de agosto de 2026 Versión de Claude Code: 2.1.220 Fuentes: - https://code.claude.com/docs/en/skills Modelos compatibles: Claude Fable 5, Claude Opus 5, Claude Sonnet 5, Claude Sonnet 4.6, Claude Opus 4.8, Claude Haiku 4.5