Hay una categoría de fallas que no se detectan en revisión de código, no las atrapan las pruebas automatizadas y no aparecen en los registros del servidor. El código está bien. El despliegue salió bien. Y aun así, el usuario ve el sistema roto.
Son las fallas que dependen de que una persona recuerde hacer algo.
Hace poco nos topamos con una de manual mientras trabajábamos en la reestructuración de un sistema administrativo. Vale la pena contarla, no por lo ingenioso de la solución, sino por lo contrario: porque la solución correcta resultó ser la más aburrida de las tres que teníamos sobre la mesa.
El síntoma
El escenario es conocido para cualquiera que haya desplegado una aplicación web. Se corrige un error en un archivo JavaScript, se sube el cambio a producción, se verifica en el navegador y todo funciona. Al día siguiente, un usuario reporta que el error sigue ahí.
No sigue ahí. Lo que pasa es que su navegador guardó una copia del archivo viejo y no tiene ninguna razón para pedir una nueva. Para el navegador, app.js es app.js: mismo nombre, misma dirección, no hay motivo para volver a descargarlo.
La solución clásica es agregarle un número de versión a la dirección del archivo, algo como cart.js?v=1.0.0. Cuando el archivo cambia, se sube el número, la dirección deja de ser la misma y el navegador descarga la copia nueva.
Eso es exactamente lo que el sistema hacía. Y ahí estaba el problema.
El mecanismo existía y no servía
Al revisar el código encontramos tres cosas:
Uno de los archivos llevaba ?v=1.0.0. Otro llevaba ?v=1.0.2. Un tercero, tan crítico como los anteriores, no llevaba nada.
El número se subía a mano. Es decir, el mecanismo funcionaba siempre y cuando quien modificara el archivo se acordara de abrir otro archivo distinto, buscar la línea correcta y cambiar un dígito. En la práctica, casi nunca pasaba. Uno de los archivos había cambiado varias veces desde la última vez que alguien tocó su número de versión.
Esto es lo interesante del caso: el mecanismo no faltaba. Estaba ahí, escrito, visible, aparentemente funcionando. Y era completamente inútil, porque su correcto funcionamiento dependía de la memoria de una persona en un momento en que esa persona está pensando en otra cosa.
Un mecanismo de seguridad que depende de que alguien se acuerde no es un mecanismo de seguridad. Es una sugerencia.
Las tres opciones
Al plantear la corrección aparecieron tres caminos.
El sofisticado. Incorporar los archivos al proceso de compilación del framework, que genera nombres con una huella digital del contenido, resuelve el problema de raíz y de paso minifica los archivos. Es la respuesta que da cualquier guía moderna.
El intermedio. Compilar los archivos, pero seguir publicándolos como hasta ahora, con el número de versión resuelto por otro medio.
El aburrido. Dejar los archivos exactamente donde están y calcular el número de versión a partir de la fecha de última modificación del propio archivo. Una línea de código.
La primera opción era la recomendación inicial. La descartamos por razones que no tienen que ver con la elegancia técnica.
Por qué ganó el aburrido
La compilación agrega un paso del que depende que el sitio no se caiga. Si el proceso de compilación no se ejecuta en un despliegue, o se ejecuta mal, el sistema no encuentra los archivos y devuelve una pantalla en blanco. No en una sección: en todas, porque estos archivos se cargan desde una plantilla compartida. Cambiábamos un problema silencioso y acotado por uno ruidoso y total.
El archivo compilado es ilegible. Ese proyecto se estaba revisando pieza por pieza antes de aprobar cada cambio, y cada entrega habría traído un bloque de miles de caracteres en una sola línea junto al cambio real. Cuando revisar se vuelve difícil, se revisa menos. Ese costo no aparece en ninguna estimación y se paga durante meses.
Incorporar un solo archivo a un proceso que ningún otro usa deja el sistema más inconsistente de lo que estaba. Esa migración tiene sentido cuando se hace completa, y ese momento tenía que ver con otras prioridades del proyecto, no con este problema.
La solución que quedó calcula la fecha de modificación del archivo y la usa como número de versión. Cuando el archivo cambia, la fecha cambia, la dirección cambia y el navegador descarga la copia nueva. Cuando no cambia, todo sigue igual y el navegador usa lo que ya tiene.
Sin proceso nuevo. Sin dependencias nuevas. Sin nada que recordar.
La verificación fue igual de directa: modificar uno de los archivos y comprobar en el navegador que efectivamente se descargó una copia nueva, con la herramienta de rendimiento del navegador confirmando que el archivo bajó de la red y no de la caché. También que modificar un archivo no alteraba el número de versión de los otros dos, para asegurar que cada uno llevara su propio control.
El bloque completo se había estimado entre cuatro y ocho horas. Cerró en aproximadamente una.
Lo que nos llevamos
Buscar los mecanismos que dependen de la memoria de alguien. En cualquier sistema con algo de historia hay varios. Se reconocen por una pregunta: si la persona que hace este cambio estuviera distraída, ¿el sistema fallaría en silencio? Si la respuesta es sí, eso no está resuelto, está pendiente.
Que un mecanismo exista no significa que funcione. Ver ?v=1.0.0 en el código da una falsa sensación de que el tema está atendido. La pregunta útil no es si el mecanismo está, sino si alguien lo ejerció la última vez que debía.
La solución correcta no siempre es la más avanzada. La compilación de assets es una buena práctica y en muchos proyectos es lo que corresponde. Aquí habría resuelto el problema y creado uno mayor. La pregunta no es cuál es la mejor herramienta en abstracto, sino cuál resuelve este problema sin traer otros en este contexto y en este momento del proyecto.
Y la más barata merece evaluarse en serio, no descartarse por simple. Una línea de código que resuelve el problema completo, sin agregar dependencias ni pasos de despliegue, es un resultado mejor que una arquitectura correcta que nadie mantiene.







Deja un comentario