14. Riesgos del desarrollo basado en suposiciones futuras

Cuando construimos software sobre hipótesis no validadas, nos alejamos del principio YAGNI y comprometemos la capacidad de respuesta del equipo. En sistemas desarrollados con Java, estas suposiciones suelen traducirse en módulos abstractos, configuraciones extensas o integraciones a medio terminar que rara vez llegan a usarse.

Este capítulo expone los riesgos más comunes de anticipar funcionalidades, cómo se manifiestan en el código y qué estrategias ayudan a mitigarlos antes de que se conviertan en deuda técnica crónica.

14.1 Riesgos principales de trabajar sobre suposiciones

  • Deuda técnica oculta: componentes que nadie utiliza, pero que requieren mantenimiento continuo.
  • Pérdida de foco: se desviaron recursos de las prioridades con impacto comprobado.
  • Complejidad accidental: se introducen abstracciones y configuraciones que dificultan el onboarding.
  • Regresiones inesperadas: el código especulativo se degrada o interfiere con funcionalidades activas.
  • Costos operativos: monitoreo, alertas e infraestructura para características nunca habilitadas.

14.2 Señales de que el equipo está desarrollando para un futuro incierto

  • Historias de usuario sin métricas ni feedback real, basadas en suposiciones del equipo.
  • Diseños que contemplan múltiples escenarios, pero con un caso de uso actual muy acotado.
  • Feature flags permanentes o ramas largas que no llegan a producción.
  • Dependencias externas instaladas "por si acaso" y que nunca se invocan.
  • Revisiones de código donde se aprueban funcionalidades sin consumidores definidos.

14.3 Costos directos e indirectos

El software anticipado suele convertirse en deuda técnica difícil de justificar. Los costos directos incluyen tiempo de desarrollo, pruebas y despliegue. Los indirectos abarcan complejidad en los pipelines, mayor superficie de ataque de seguridad y esfuerzo adicional de soporte.

Además, mantener código especulativo fomenta decisiones futuras basadas en la falsa premisa de que "ya existe la infraestructura". Con el tiempo, los supuestos se vuelven dogma y el equipo pierde la flexibilidad para replantear soluciones.

14.4 Ejemplo en Java: integración fantasma

Analicemos un caso habitual: se crea un cliente para un servicio externo que todavía no fue contratado. Al adelantarse, se incorporaron configuraciones, clientes HTTP y controles de errores que nadie utiliza.

// Código especulativo: cliente API sin fecha de uso definida
class LoyaltyIntegration {
    private final HttpClient httpClient;
    private final URI endpoint;
    private final CircuitBreaker breaker;

    LoyaltyIntegration(HttpClient httpClient, URI endpoint, CircuitBreaker breaker) {
        this.httpClient = httpClient;
        this.endpoint = endpoint;
        this.breaker = breaker;
    }

    Optional<LoyaltyResponse> fetchPoints(String customerId) {
        return breaker.executeSupplier(() -> {
            HttpRequest request = HttpRequest.newBuilder(endpoint.resolve("/points/" + customerId))
                .header("X-App", "store")
                .GET()
                .build();
            HttpResponse<String> response = httpClient.send(request, BodyHandlers.ofString());
            if (response.statusCode() == 200) {
                return Optional.of(parse(response.body()));
            }
            return Optional.empty();
        });
    }

    private LoyaltyResponse parse(String body) {
        // Parser anticipado
        return new LoyaltyResponse();
    }
}

La integración nunca se habilitó, pero el equipo debe mantener dependencias y pruebas que fallan cada tanto. Siguiendo YAGNI, el código se reemplaza por un adaptador mínimo que registra la intención y pospone la implementación real hasta contar con un contrato firmado.

// Código alineado a YAGNI: placeholder controlado y sin dependencias pesadas
final class LoyaltyIntegration {
    LoyaltyResponse fetchPoints(String customerId) {
        throw new UnsupportedOperationException("Integración de fidelidad pendiente de contrato");
    }
}

Con esta simplificación se evita arrastrar dependencias complejas. Cuando llegue el momento, la integración se desarrollará con requerimientos concretos y pruebas orientadas al uso real.

14.5 Estrategias para reducir el trabajo basado en suposiciones

  • Adoptar desarrollo guiado por evidencias: datos de uso, pilotos controlados o experimentos A/B.
  • Limitar el trabajo en progreso y revisar periódicamente las iniciativas en curso.
  • Documentar cada decisión con la fuente de la necesidad y una fecha de revalidación.
  • Aplicar feature toggles solo cuando exista un plan explícito para activarlos y medirlos.
  • Fomentar revisiones de arquitectura ligeras para desafiar suposiciones no verificadas.

14.6 Checklist para evaluar una funcionalidad antes de implementarla

  • ¿El problema fue confirmado por usuarios, métricas o requisitos regulatorios?
  • ¿La solución propuesta entrega valor en la próxima iteración?
  • ¿Existen alternativas más simples que puedan evolucionar luego?
  • ¿Las dependencias nuevas tienen responsables claros y mantenimiento asegurado?
  • ¿Hay un plan para medir el impacto y decidir si se continúa o se elimina?

Cuestionar las suposiciones futuras protege al equipo de desperdiciar esfuerzo y mantiene la capacidad de responder al cambio. Cada vez que evitemos una funcionalidad especulativa, liberamos tiempo para entregar valor real y reducimos la deuda técnica del proyecto.