29. Anti-patrones: God Object, Spaghetti Code, Golden Hammer

Los anti-patrones describen soluciones recurrentes que deterioran la calidad de un sistema. Reconocerlos permite prevenir deuda técnica y mejorar la mantenibilidad. En esta sección analizaremos tres anti-patrones clásicos descritos por Andrew Koenig y popularizados por trabajos como Anti-pattern: God Object, Spaghetti Code y Golden Hammer.

29.1 Comprender los anti-patrones

Un anti-patrón no es simplemente un error aislado, sino una respuesta repetida a un problema que inicialmente parece conveniente pero produce consecuencias negativas a largo plazo. Identificarlos exige evaluar el contexto, detectar señales de alerta y contar con estrategias de refactorización.

29.2 God Object

El anti-patrón God Object aparece cuando una clase o módulo concentra demasiadas responsabilidades, conoce datos de todo el sistema e interviene en cualquier operación relevante. A menudo surge por crecimiento incremental sin arquitectura clara.

29.2.1 Señales de alerta

  • Clases con miles de líneas, docenas de propiedades y métodos con responsabilidades dispares.
  • Dependencias unidireccionales: todos los componentes consultan o modifican el God Object.
  • Dificultad para escribir pruebas unitarias sin dependencias extensas.

29.2.2 Consecuencias

Genera acoplamiento extremo, inhibe la extensión modular, introduce puntos únicos de fallo y reduce la cohesión. Cambiar cualquier comportamiento obliga a modificar la misma clase omnipotente, incrementando el riesgo de regresiones.

29.2.3 Estrategias de corrección

  • Aplicar el principio de responsabilidad única: extraer funcionalidades en servicios o agregados coherentes.
  • Introducir patrones que repriman el acoplamiento: Facade para exponer APIs acotadas, Mediator para coordinar flujos, Strategy para separar políticas.
  • Adoptar diagramas de dominio que revelen bounded contexts y límites de responsabilidad claros.

29.2.4 Ejemplo en C#

public class SistemaVentas
{
    private readonly ConexionDb _conexion;
    private readonly ServicioEmail _email;
    private readonly MotorPrecios _motor;
    private readonly Reporteador _reporte;

    public void ProcesarPedido(Pedido pedido)
    {
        _motor.Calcular(pedido);
        _conexion.Guardar(pedido);
        _reporte.GenerarCsv(pedido);
        _email.EnviarConfirmacion(pedido);
    }

    public void SincronizarInventario() { /* 200 lineas mas... */ }
    public void Auditar() { /* ... */ }
    public void ExportarFinanzas() { /* ... */ }
}

Esta clase centraliza persistencia, logística, reporting y comunicación. La refactorización consiste en extraer servicios especializados (ServicioPedidos, SincronizadorInventario, ServicioReportes) y coordinar desde una fachada ligera, reduciendo acoplamiento.

29.2.5 Métricas orientativas

  • Complejidad ciclomática elevada (> 15) en numerosos métodos.
  • Clases con más de 1000 líneas sin pruebas asociadas.
  • Número de dependencias (imports) significativamente superior al promedio del proyecto.

29.3 Spaghetti Code

Spaghetti Code describe programas con flujo de control enredado: saltos arbitrarios, funciones largas, ausencia de modularidad y dependencias cruzadas. El nombre evoca un plato de spaghetti donde los hilos se entrelazan sin orden aparente.

29.3.1 Señales de alerta

  • Uso intensivo de goto, flags globales o retornos anticipados que generan flujos no lineales.
  • Métodos gigantes que mezclan lógica de negocio, acceso a datos y gestión de interfaz.
  • Escasez de pruebas automáticas debido a la dificultad de aislar componentes.

29.3.2 Consecuencias

Dificulta la lectura del código, retrasa la detección de errores y complica el mantenimiento. Los equipos invierten la mayor parte del tiempo intentando comprender el flujo para realizar cambios mínimos.

29.3.3 Estrategias de corrección

  • Refactorizar en pasos pequeños extrayendo funciones puras y aplicando patrones como Template Method o Strategy.
  • Agregar pruebas de caracterización para documentar el comportamiento actual antes de refactorizar.
  • Establecer guías de arquitectura (capas, hexagonal, DDD) que definan límites y responsabilidades.

29.3.4 Ejemplo evolutivo

void procesar() {
    leerArchivo();
    while(condition) {
        if (tipo.equals("A")) {
            // 80 líneas de reglas
        } else if (tipo.equals("B")) {
            // 65 líneas más
        } else {
            // nuevos parches
        }
        if (error) { reiniciar(); continue; }
        actualizarUI();
        grabarBaseDatos();
    }
}

El primer paso de refactorización puede ser extraer métodos por tipo (procesarTipoA, procesarTipoB) y reemplazar condicionales por Strategy o Command, logrando lógica acotada y testeable.

29.3.5 Indicadores automatizables

  • Funciones con más de 100 líneas o profundidades de anidación > 4.
  • Duplicación de código detectada por herramientas como PMD/CPD o SonarQube.
  • Módulos sin cobertura de pruebas o con alta tasa de incidencias.

29.4 Golden Hammer

Golden Hammer alude a la tendencia de utilizar la misma herramienta o tecnología para resolver cualquier problema, ignorando alternativas más adecuadas. El nombre proviene del dicho: “Si todo lo que tienes es un martillo, todo te parece un clavo”.

29.4.1 Señales de alerta

  • Stack tecnológico impuesto en todos los proyectos sin evaluar requisitos específicos.
  • Repositorios con soluciones forzadas: scripts que reemplazan consultas declarativas, uso de ORM para tareas de ETL, etc.
  • Resistencia sistemática a evaluar herramientas nuevas o especializadas.

29.4.2 Consecuencias

Conduce a sub-diseño o sobre-ingenerización, elevando costos de mantenimiento y generando inconsistencias. Las soluciones resultan frágiles porque la herramienta elegida no fue diseñada para ese uso.

29.4.3 Estrategias de corrección

  • Realizar evaluaciones técnicas comparativas (proof of concepts) antes de comprometerse con una herramienta.
  • Fomentar una cultura de aprendizaje continuo y revisiones arquitectónicas participativas.
  • Adoptar una matriz de decisiones que considere requisitos funcionales, no funcionales y costos de adopción.

29.4.4 Caso ilustrativo

Un equipo insiste en usar el mismo framework batch para servicios REST, tareas de ETL y procesamiento de colas. La falta de soporte nativo para HTTP obliga a crear capas adaptadoras complejas y scripts manuales. Evaluar alternativas como Spring Boot, Apache Camel o arquitecturas sin servidor evitaría una solución frágil.

29.4.5 Preguntas de autoevaluación

  • ¿La herramienta elegida fue diseñada para este caso de uso o estamos forzándola?
  • ¿Existen soluciones específicas mejor alineadas con el dominio?
  • ¿Cuál es el costo de oportunidad de no explorar alternativas?

29.5 Tabla comparativa rápida

Anti-patrón Señales clave Impacto Primeros pasos de mitigación
God Object Clase monolítica, dependencia transversal Alto riesgo de regresiones, baja cohesión Dividir responsabilidades, introducir fachadas y servicios
Spaghetti Code Flujo errático, funciones gigantes Dificultad de mantenimiento, bugs recurrentes Refactor incremental, pruebas de caracterización
Golden Hammer Una sola herramienta para todo Soluciones frágiles, sobrecostos POCs comparativas, guías de selección tecnológica

29.6 Prevención y cultura de mejora

Evitar anti-patrones no depende solo de patrones técnicos, sino de gobernanza, revisiones de código, pruebas automatizadas y una cultura que fomente cuestionar decisiones heredadas. Registrar lecciones aprendidas y realizar retrospectivas técnicas ayuda a detectar tendencias peligrosas a tiempo.

29.7 Checklist de detección temprana

  • Monitorear complejidad y tamaño de clases con herramientas estáticas.
  • Incluir preguntas sobre acoplamiento y deuda técnica en revisiones de sprint.
  • Registrar decisiones tecnológicas en ADRs y revisarlas cada cierto tiempo.
  • Fomentar rotación de miembros en módulos críticos para obtener miradas frescas.

29.8 Recursos para profundizar

  • AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis de Brown, Malveau, McCormick y Mowbray.
  • Reportes del Software Engineering Institute sobre deuda técnica y buenas prácticas de mitigación.
  • Tutoriales de refactorización (katas) que permiten practicar la eliminación de code smells.