El principio YAGNI nos recuerda que no debemos implementar una funcionalidad hasta que exista una necesidad comprobada. En proyectos desarrollados con Java este recordatorio es esencial para evitar capas de código anticipadas que consumen tiempo, generan deuda técnica y complican la evolución.
Aplicar YAGNI no significa ignorar la arquitectura, sino diseñar soluciones que resuelvan el problema actual y dejen abierta la posibilidad de extenderlas cuando las evidencias lo justifiquen. Veamos cómo incorporarlo en la toma de decisiones diaria.
Construir funcionalidades especulativas suele derivar en tres problemas: mayor complejidad accidental, retrasos en la entrega de valor y mantenimiento innecesario. Cada módulo anticipado requiere pruebas, documentación y soporte aunque nadie lo utilice.
YAGNI orienta los esfuerzos hacia las necesidades presentes del negocio. Al posponer las extensiones hasta tener una solicitud real, evitamos invertir recursos en hipótesis y mantenemos la base de código más pequeña y comprensible.
Al planificar iteraciones, valida cada funcionalidad con una historia de usuario o un experimento medible. Documenta qué evidencia habilitó la tarea y revisa periódicamente si sigue vigente. Limita el trabajo en progreso para que las funcionalidades se completen antes de iniciar nuevas ideas.
También es útil mantener un registro de supuestos. Si una hipótesis no se valida en un plazo razonable, elimínala o archívala como una nota y no como una tarea activa.
El siguiente ejemplo muestra un módulo de importación que intentó anticipar múltiples fuentes de datos. La versión compleja introduce interfaces, patrones y configuraciones que el negocio aún no necesita.
// Versión especulativa: abstracción prematura sin casos concretos
class ImportManager {
private final Map<String, ImportStrategy> strategies;
private final Executor executor;
ImportManager(Map<String, ImportStrategy> strategies, Executor executor) {
this.strategies = strategies;
this.executor = executor;
}
void importData(String sourceType, Path path) {
ImportStrategy strategy = strategies.getOrDefault(sourceType, strategies.get("default"));
executor.execute(() -> strategy.importFrom(path));
}
}
En la práctica solo se importa desde archivos CSV. Aplicando YAGNI, simplificamos el diseño a una clase que hace su trabajo actual y que podremos extender cuando aparezca una nueva fuente real.
// Versión simple: enfocada en la necesidad validada
final class CsvImporter {
private final Charset charset;
CsvImporter(Charset charset) {
this.charset = charset;
}
List<Registro> importar(Path archivo) throws IOException {
try (BufferedReader reader = Files.newBufferedReader(archivo, charset)) {
return reader.lines()
.skip(1)
.map(this::mapear)
.toList();
}
}
private Registro mapear(String linea) {
String[] campos = linea.split(";");
return new Registro(campos[0], campos[1], new BigDecimal(campos[2]));
}
}
El código resultante es directo, fácil de probar y sin dependencias innecesarias. Cuando el negocio solicite un nuevo origen, podremos introducir una abstracción apoyada en un caso concreto.
YAGNI no implica descartar la planificación. Invertimos en extensibilidad cuando existe una hoja de ruta acordada, métricas que anticipan la demanda o un requisito legal. En esos casos, documenta la razón y limita el alcance para evitar sobreingeniería.
Procura validar las decisiones con datos: métricas de uso, entrevistas con usuarios u objetivos de negocio publicados. Si la información no es concluyente, apuesta por una solución más simple mientras reúnes evidencia.
Adoptar YAGNI mantiene al equipo enfocado, reduce la deuda técnica y deja espacio para responder con agilidad cuando surgen nuevas prioridades. Mantén visible este principio en las retrospectivas y cultiva una cultura que valore entregar lo justo y necesario.