Refactorización, deuda técnica y un defecto intermitente en producción
Un módulo de facturación de 12 000 líneas calcula impuestos con lógica duplicada en 4 lugares y sin pruebas. Produce un defecto intermitente: en ~1 de cada 500 facturas, el total difiere en 0,01 por redondeo, y el cliente lo detecta en conciliación. El equipo quiere reescribir el módulo desde cero en 6 semanas. Hay presión comercial: el cliente exige corrección en 2 semanas o migra. Un desarrollador propone un cambio de 15 líneas que unifica el redondeo en un solo punto con BigDecimal y añade una prueba que reproduce el caso. El arquitecto advierte que 'un parche no resuelve la deuda'.
1. Analiza el defecto: ¿por qué es intermitente y por qué el redondeo con coma flotante lo explica? ¿Qué información falta para confirmarlo?
Read the worked answer
Por qué intermitente: solo se manifiesta cuando los cálculos intermedios caen en valores donde el redondeo binario cruza un umbral (p. ej. x,xx5). La mayoría de facturas no lo tocan; por eso ~1/500.
Por qué coma flotante: double no representa exactamente decimales como 0.1 o 0.005. Sumas e impuestos acumulan error de representación; al redondear a 2 decimales, un valor que 'debería' ser 10.005 puede quedar en 10.004999… y redondear a 10.00 en lugar de 10.01. Con 4 lugares duplicados, cada uno puede redondear en momentos distintos (por línea vs. por total), produciendo diferencias de un céntimo.
Qué falta para confirmarlo:
- Una factura concreta que falló y su traza de cálculo intermedio.
- Saber si los 4 lugares redondean en el mismo punto (por línea o por total) y con qué modo (HALF_UP vs. HALF_EVEN).
- Reproducir con una prueba que fije los valores de entrada.
Sin esto, la hipótesis es plausible pero no verificada; la prueba del desarrollador es justamente el experimento que la confirma o refuta.
2. El arquitecto dice que el parche 'no resuelve la deuda'. Evalúa esa afirmación y decide qué hacer en las 2 semanas de plazo.
Read the worked answer
La afirmación es cierta pero irrelevante para la decisión inmediata. Un parche de 15 líneas no elimina la duplicación ni la falta de pruebas; la deuda sigue. Pero el cliente no exige 'deuda cero', exige que el total sea correcto en 2 semanas.
Decisión en 2 semanas:
- Aplicar el cambio que unifica el redondeo en un punto con
BigDecimaly modo explícito (HALF_UP, escala 2). - Añadir la prueba de regresión con la factura que falla; debe fallar antes y pasar después.
- Verificar contra una muestra de facturas históricas (las 500 que rodean el fallo) para confirmar que no cambia totales correctos.
Por qué: corrige el síntoma real (céntimo incorrecto), es de bajo riesgo, reversible y demostrable en días. La reescritura de 6 semanas no llega a la fecha y sustituye un módulo en producción por uno sin pruebas: riesgo mayor.
Condición: el parche no cierra la deuda; se registra como deuda explícita con plan posterior. Pero no se pospone la corrección por pureza arquitectónica.
3. Diseña el plan posterior a las 2 semanas: cómo reducir la deuda sin una reescritura big-bang y cómo evitar que el defecto reaparezca.
Read the worked answer
Principio: refactorizar por pasos verificables, no reescribir.
Plan (4–6 sprints, incremental):
- Caracterizar el comportamiento actual: pruebas de aceptación sobre facturas reales (incluidas las de borde) que fijen los totales correctos. Red de seguridad antes de tocar nada.
- Extraer el cálculo a un único componente con
BigDecimaly política de redondeo explícita y documentada. Los 4 lugares llaman a él; se elimina la duplicación uno a uno, con prueba tras cada paso. - Pruebas de propiedad: generar entradas aleatorias y verificar invariantes (suma de líneas = total; impuesto dentro de rango; idempotencia). Detectan casos que la muestra histórica no cubre.
- Eliminar el código muerto solo cuando el nuevo componente esté en uso y probado.
Evitar reaparición:
- Una sola función de redondeo; prohibir
doublepara dinero (regla de revisión o lint). - Prueba de regresión permanente del caso del céntimo.
- Monitorización en producción: alerta cuando la conciliación detecte diferencias.
Métrica: duplicación del cálculo de 4 → 1; cobertura del módulo; 0 diferencias de conciliación en 3 meses. La reescritura de 6 semanas se descarta: mismo objetivo, más riesgo y sin red de seguridad.