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.
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.
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.
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.