En pocas palabras
Relaciona problemas concretos de diseño con refactorizaciones acordadas para orientar a agentes y desarrolladores.
Qué problema resuelve
Cambios genéricos que ignoran estándares del equipo o restricciones de sistemas heredados.
Cuándo utilizarlo
Mantenimiento y modernización. Ejemplo: asociar condicionales duplicados con una estrategia compatible con Java 8.
De la idea a la práctica
Funcionamiento, ejemplos y decisiones para pasar al siguiente nivel.
En el Radar compartido: Trial (valoración de la edición aportada).
Cómo funciona
Un catálogo explica síntoma, contexto, técnica recomendada y comprobación. Puede distribuirse mediante AGENTS.md, skills o reglas conectadas a linters.
Ejemplo de uso
Para una función que mezcla cálculo y persistencia, indicar «extraer el cálculo puro» y proporcionar un ejemplo del repositorio. Conservar la transacción y verificar casos de negocio antes y después.
Catálogo base de olores y refactorizaciones
| Olor | Síntoma | Refactorización habitual |
|---|---|---|
| Función larga | Hay que leer mucho para entender qué hace | Extract Function, Decompose Conditional |
| Clase grande | Muchas responsabilidades y campos | Extract Class |
| Lista larga de parámetros | Llamadas con 6 argumentos posicionales | Introduce Parameter Object, Preserve Whole Object |
| Obsesión por primitivos | String para documento, double para dinero |
Replace Primitive with Object |
| Agrupaciones de datos | Los mismos tres campos viajan juntos siempre | Extract Class, Introduce Parameter Object |
| Envidia de funcionalidad | Un método usa más datos de otra clase que de la suya | Move Function |
| Switch repetidos | El mismo switch (tipoProducto) en varios sitios |
Replace Conditional with Polymorphism |
| Cirugía de escopeta | Un cambio exige tocar muchos archivos | Move Function, Combine Functions into Class |
| Cambio divergente | Una clase cambia por motivos distintos | Split Phase, Extract Class |
| Código duplicado | Mismo fragmento en varios lugares | Extract Function, Pull Up Method |
| Cadenas de mensajes | a.getB().getC().getD() |
Hide Delegate |
| Generalidad especulativa | Abstracciones sin un segundo uso | Inline Function, Collapse Hierarchy |
| Datos mutables globales | Cualquier parte puede cambiar el estado | Encapsulate Variable |
Ejemplo: obsesión por primitivos
// Antes: el dinero es un double y la moneda un String suelto
double calcularCuota(double monto, String moneda, double tasaMensual, int plazo) { … }
// Después: un value object con invariantes y aritmética exacta
public record Dinero(BigDecimal valor, Currency moneda) {
public Dinero {
if (valor.scale() > moneda.getDefaultFractionDigits())
throw new IllegalArgumentException("Demasiados decimales para " + moneda);
}
public Dinero por(BigDecimal factor) {
return new Dinero(valor.multiply(factor).setScale(moneda.getDefaultFractionDigits(), RoundingMode.HALF_EVEN), moneda);
}
}
Dinero calcularCuota(Dinero monto, TasaMensual tasa, Plazo plazo) { … }
Además de eliminar errores de redondeo, el compilador impide pasar un plazo donde se esperaba una tasa.
Formato del catálogo para agentes
## Obsesión por primitivos en importes
- **Detectar**: parámetros o campos `double`/`float` con nombres como monto, saldo, cuota.
- **Aplicar**: usar `Dinero` (`com.krediya.core.Dinero`). Ver ejemplo en `CuotaService`.
- **No aplicar**: DTOs de entrada/salida JSON (se convierten en el borde).
- **Verificar**: pruebas de `CalculoCuotaTest` con importes de 2 decimales y redondeo HALF_EVEN.
- **Tamaño máximo**: un agregado por PR.
Este formato (detectar, aplicar, excepciones, verificación, límite) funciona igual para personas y agentes. Puede vivir en AGENTS.md, en una skill o en reglas de linters.
Detección automática
| Herramienta | Qué aporta |
|---|---|
| SonarQube, PMD, ESLint, Ruff | Reglas de complejidad, tamaño, duplicación |
| CodeScene | Hotspots: olores en código que cambia mucho |
| ArchUnit y similares | Dependencias entre módulos (fitness functions) |
| OpenRewrite | Algunas refactorizaciones automáticas y seguras |
Prioriza los olores en código que cambia a menudo (gestión de deuda técnica): refactorizar código estable rara vez compensa.
Disciplina de refactorización
- Refactorizar es cambiar la estructura sin cambiar el comportamiento: separa los commits de refactorización de los de funcionalidad.
- Pasos pequeños con las pruebas en verde después de cada uno; TCR lleva esta disciplina al extremo.
- Usa las refactorizaciones automáticas del IDE siempre que existan: son más seguras que editar a mano.
Criterios de implementación
Precisar excepciones, versiones del lenguaje y límites de tamaño del cambio. Usar pruebas de caracterización cuando el comportamiento existente no está documentado.
Continúa en las fuentes
Documentación oficial para verificar detalles y explorar más.