La deuda técnica que nadie menciona: actualizar sin tests es solo retrasar el desastre
Hace algunos años actualizaba mis dependencias religiosamente una vez al mes. Creía que estaba haciendo lo correcto: seguir las mejores prácticas, mantener el código seguro, estar "al día" con las librerías.
Entonces un cambio minor en una librería de autenticación rompió silenciosamente la forma en que se validaban tokens en producción. No falló con un error estrepitoso. Simplemente dejó pasar requests que debería haber rechazado. Lo descubrimos tres días después, cuando las métricas de acceso se comportaron de forma extraña.
Ese momento me enseñó algo fundamental que casi nadie dice en voz alta:
Actualizar dependencias sin tests es solo retrasar el desastre algunos meses.
El problema es más profundo de lo que parece
Cuando hablamos de deuda técnica en actualización de dependencias, la gente piensa en "mantener paquetes al día" o "aplicar patches de seguridad". Eso es verdad, pero es la mitad superficial del problema.
La otra mitad —la que causa el verdadero daño— es que la mayoría de los equipos actualiza sin poder validar que nada se rompió.
Por qué la cadencia estándar no funciona
Si tu arquitectura es simple, actualizar mensualmente puede funcionar. Haces un test rápido, publicas, y listo.
Pero si tienes (como yo en Deeditt):
- Múltiples microservicios (Node.js, Python, Go)
- Orquestación de workflows (Prefect, n8n)
- Múltiples bases de datos (PostgreSQL, InfluxDB, Redis)
- Servicios de infraestructura (Keycloak, Prometheus, Loki)
- Lógica distribuida compleja
Entonces una actualización minor en Prefect podría afectar notificaciones. Un cambio en PostgreSQL podría cambiar el comportamiento de queries que llevan meses en producción. Una versión nueva de InfluxDB podría hacer que tus analyticals se comporten diferente sin fallar explícitamente.
Y aquí está lo insidioso: podrías no darte cuenta por semanas.
La pregunta incómoda que nadie hace
Antes de decidir cuándo actualizar, necesitas responder una pregunta mucho más importante:
¿Cómo validas que los cambios no rompieron nada?
Y no, "ejecuté la app y funcionó" no cuenta.
Los tipos de tests que importan en actualización
Si solo tienes unit tests básicos, actualizar es peligroso:
- Unit tests ✓ (el módulo en aislamiento funciona)
- Integration tests ✓ (dos componentes hablan bien)
- E2E tests ✓ (flujo usuario completo)
- Data consistency tests ✗ (falta usualmente)
- Performance benchmarks ✗ (falta usualmente)
- Idempotency tests ✗ (para operaciones repetibles)
- Scenario tests bajo escala real ✗ (falta usualmente)
Muchos proyectos tienen los primeros, muy pocos tienen los últimos. Y es en los últimos donde los cambios te muerden.
El ejemplo real: orquestación de workflows
En Deeditt uso Prefect para tareas críticas: disparar notificaciones, limpiar bases de datos, preparar emails personalizados. La lógica es compleja. Si actualizo Prefect sin validar que esas operaciones aún se ejecutan idénticamente, estoy jugando a la ruleta rusa.
¿Qué necesito para actualizar con confianza?
- Tests que ejecuten workflows completos
- Tests que validen que si el mismo workflow se ejecuta dos veces, no hay efectos secundarios duplicados
- Validación de que después de la limpieza de base de datos, los datos están en estado válido
- Confirmación de que los emails se generan con la estructura esperada
Sin eso, la cadencia de actualización es irrelevante. Podrías hacerlo anual y seguiría siendo peligroso.
Entonces, ¿cuál es la estrategia correcta?
No es una cadencia única, es capas de riesgo o al menos es la forma en que empecé a hacerlo, porque también es importante encontrar el balance entre el código que se tiene, lo que se mantiene y los recursos y energía de uno mismo.
Capa 1: Crítica (patching + security)
Lenguajes base, autenticación, datos.
- Node.js, Python, Go: revisión semanal de advisories, patches inmediatos si hay CVE
- Keycloak: patches mensualmente, minors trimestralmente
- PostgreSQL: patches mensualmente, minors anualmente
- Redis: patches mensualmente, minors trimestralmente
Acción: pequeños cambios constantes, bajo riesgo porque el testing es más simple.
Capa 2: Alta (cambios operacionales)
Orquestación y observabilidad.
- Prefect: trimestral, porque cambios acá impactan workflows críticos
- Prometheus + Loki + Grafana: trimestral como grupo
- InfluxDB: trimestral, revisar primero cambios en API
Acción: actualizaciones coordinadas, testing exhaustivo antes de mergear.
Capa 3: Media (servicios de soporte)
Automatización y herramientas periféricas.
- n8n: semestral, es más aislado
- Docker: semestral
Acción: menos urgente, bajo impacto en core.
Capa 4: Especial (major versions)
Cambios conceptuales que requieren refactoring.
- Evalúa según impacto, no por calendario
- Planifica específicamente
La automatización es tu mejor aliado
De nada sirve tener una cadencia perfecta si los cambios los haces manualmente.
Necesitas:
1. Dependabot o Renovate
Que te avise automáticamente cuando hay updates. No gastes energía en buscar; deja que herramientas te notifiquen.
2. CI/CD que corra todos los tests contra nuevas versiones
Antes de que toques nada manualmente, la máquina debería haber corrido:
- Unit tests
- Integration tests
- E2E tests
- Data consistency checks (si tienes)
- Performance benchmarks (si tienes)
Si pasan, confía. Si fallan, tienes información clara.
3. Staging environment con datos realistas
Donde pruebes servicios antes de tocamiento producción, especialmente para:
- Actualización de bases de datos
- Cambios en Prefect o n8n
- Cambios en autenticación
4. Rollback rápido
Si algo explota, ¿cómo vuelves atrás en minutos, no en horas?
El costo real de la deuda técnica
Si tienes 50 dependencias y actualizas "cuando haya tiempo", dentro de 6 meses probablemente tengas:
- 40 updates pendientes
- 10 de ellas con conflictos de versión
- 3 con breaking changes
- Y cuando finalmente intentes actualizar todo, te encuentras con un desastre que toma semanas.
Vs:
Si actualizas mensualmente con buenos tests, cada cambio es pequeño, predecible, y si algo falla, sabes exactamente cuál fue el culpable.
La deuda técnica no es sobre estar "al día". Es sobre poder dormir de noche sabiendo que tu sistema es predecible.
En Deeditt: cómo lo manejo
Tengo un stack grande y complejo para ser una aplicación simple a la vista de quienes no conocen lo que hay detrás: Node.js + Python + Go, Prefect + n8n, PostgreSQL + InfluxDB, Keycloak, Prometheus + Loki + muchas otras aplicaciones y servicios, que sin testing exhaustivo, sería un caos. Tampoco es que todo tiene un test y esta automatizado, la deuda técnica siempre llega, es como el polvo, simplemente aparece y se quedará ahí acumulándose.
Mi enfoque:
- Semana 1 de cada mes: security scanning automatizado (npm audit, pip audit, Go vulns)
- Semana 2 de cada mes: patches y minors sin breaking changes
- Mes 1, 4, 7, 10: revisión trimestral de majors, evaluación de impacto
- Semestre: evaluación architectural completa
Pero el verdadero diferenciador no es la cadencia. Es que antes de cualquier cambio, el sistema valida automáticamente que nada se rompió.
Entre el poco tiempo libre que tengo he empezado a construir un sistema automatizado que hace esta tarea, y aunque no es perfecto por ahora, alcanza a darme un reporte y resolver cosas muy simples. Quizás algún dia me anime a liberarlo para que otros lo usen y porque no hasta puedan mejorarlo.
La verdad incómoda
La mayoría de los incidentes de producción no vienen de zero-days sofisticados. Vienen de cambios que "debería haber probado mejor".
La solución no es "actualizar más lentamente". Es "estar seguro de que lo que actualizas no rompe nada".
Y eso requiere inversión en tests. Desde el comienzo.
Porque esperar a tener 100 microservicios en producción para empezar a escribir tests es como conducir a 200 km/h y entonces preocuparte por los frenos.
Por si te interesa, Deeditt fue creado para documentar historias reales: fracasos incluidos. Si te interesa cómo otros desarrolladores han aprendido a manejar estos problemas, quizás puedas encontrar historias valiosas ahí.
Y si tienes tu propia batalla con deuda técnica, considera documentarla. Ese conocimiento tiene valor para otros.
Happy coding! 🙂
Photo by Nicole Moore on Unsplash
Written with StackEdit.