Versiones y despliegue gradual: publicar sin apostarlo todo
Separar la subida del código de su publicación con `wrangler versions upload` y `wrangler versions deploy`, repartir un porcentaje del tráfico entre dos versiones, observar cada una por separado con el binding `version_metadata`, y volver atrás con `wrangler rollback` sabiendo qué revierte y qué no.
Durante años el despliegue de un Worker fue un acto binario: ejecutabas wrangler deploy y, en cuestión de segundos, el cien por cien del tráfico mundial pasaba a ejecutar tu código nuevo. Esa velocidad es una maravilla de ingeniería y a la vez un riesgo mal repartido, porque concentra en un único instante toda la incertidumbre de un cambio. El modelo de versiones parte ese acto en dos: subir un artefacto es una cosa, decidir cuánto tráfico lo ejecuta es otra. Con esa separación aparece todo lo demás —previsualizar una versión antes de que exista para nadie, mandarle el uno por ciento del tráfico, comparar su tasa de error contra la anterior y retroceder en segundos si algo huele mal—. Esta lección trata de cómo funciona ese mecanismo y, sobre todo, de sus límites, que son la parte que decide si tu despliegue gradual protege de verdad o solo lo parece.
- Separar la subida de una versión de su publicación con
wrangler versions uploadydeploy. - Repartir el tráfico entre dos versiones por porcentaje y forzar una versión concreta al probar.
- Atribuir logs y métricas a la versión que los produjo con el binding
version_metadata. - Ejecutar un rollback y razonar con precisión sobre qué revierte y qué queda fuera de su alcance.
Subir no es publicar
Una versión es un artefacto inmutable: el bundle de tu código junto con la configuración y los bindings con los que se compiló. Se crea con un comando que, deliberadamente, no toca el tráfico.
# Crea una version y no la publica: cero peticiones la ejecutan
wrangler versions upload --tag rev-carrito --message "nuevo calculo de portes"
# Publica la version indicada al cien por cien
wrangler versions deploy 4f2c1a9e-... --yes
wrangler deploy sigue existiendo y sigue siendo correcto: es exactamente la composición de ambos pasos en uno. La diferencia es que al separarlos ganas un lugar donde mirar. Cada versión recibe un identificador, conserva la etiqueta y el mensaje que le pusiste, y —si tienes habilitadas las URL de vista previa— obtiene una dirección propia con un prefijo derivado de su identificador, alcanzable sin que ningún usuario real llegue a ella. Puedes abrirla, ejercitarla y decidir con datos, no con fe.
La inmutabilidad de la versión es más profunda de lo que parece, porque no congela solo el código: congela también la configuración y los bindings con los que se subió. Dos versiones del mismo Worker pueden apuntar a bases de datos distintas si entre una y otra cambiaste el manifiesto. Eso convierte a la versión en la unidad correcta de razonamiento —es lo que de verdad se ejecuta— y explica por qué wrangler versions list y la vista de detalle de una versión son las herramientas que responden a la pregunta de con qué configuración corría el código que falló ayer.
Aquí conviene una precisión que se pasa por alto: los secretos también generan versiones. wrangler versions secret put crea una versión nueva con el secreto actualizado sin publicarla, lo que significa que rotar una credencial deja de ser un cambio invisible aplicado sobre lo que hubiera desplegado y pasa a ser un artefacto revisable como cualquier otro. Es la misma idea llevada a su conclusión: todo lo que altera el comportamiento del Worker debe ser una versión.
flowchart LR C[commit] --> U[versions upload] U --> V[version inmutable con id y tag] V --> PR[url de vista previa sin trafico real] V --> D[versions deploy] D --> R[reparto de trafico] R --> A[version anterior 90] R --> B[version nueva 10] style B fill:#a6e3a1,color:#11111b style PR fill:#89b4fa,color:#11111b
Repartir el tráfico
El despliegue gradual consiste en publicar dos versiones a la vez con porcentajes que sumen cien. Cada petición entrante se asigna a una de ellas de forma independiente.
# El diez por ciento a la version nueva, el resto a la que ya estaba
wrangler versions deploy 4f2c1a9e-...@10 8d5b3f77-...@90 --yes
Sin argumentos, wrangler versions deploy abre un asistente interactivo que lista las versiones recientes y pide los porcentajes; con identificadores y --yes es apto para un pipeline. wrangler deployments list y wrangler deployments status cuentan qué hay publicado ahora mismo y con qué reparto, y son el comando que de verdad responde a la pregunta de qué está corriendo en producción.
Tres límites gobiernan lo que puedes hacer con esto, y ninguno es negociable:
| Límite | Consecuencia práctica |
|---|---|
| Dos versiones como máximo por despliegue | No hay canarios múltiples; una promoción encadenada se hace en pasos sucesivos de dos en dos |
| El reparto es por petición, no por sesión | Un mismo usuario puede alternar de versión entre dos peticiones consecutivas |
| Los porcentajes deben sumar cien | No existe el estado de tráfico parcial sin destino; siempre hay una versión que recibe el resto |
El segundo límite es el que rompe más despliegues graduales mal diseñados. Si tu cambio altera un contrato entre el cliente y el servidor —un campo nuevo obligatorio, un formato de sesión distinto—, el reparto por petición garantiza que algún usuario ejercitará ambas versiones en la misma sesión y verá el borde. La regla que se deriva es dura y clara: solo se despliega gradualmente lo que es compatible hacia atrás. El despliegue gradual mide riesgo, no lo elimina; la compatibilidad la sigues teniendo que diseñar tú.
Para probar una versión concreta sin depender del azar existe la cabecera Cloudflare-Workers-Version-Overrides, que permite forzar la ejecución de un identificador determinado. Es la herramienta correcta para la verificación dirigida —un test end to end que debe correr contra el candidato— y la incorrecta para dar a un cliente una experiencia estable, porque exige control sobre las cabeceras que envía.
La progresión típica avanza en pasos que multiplican la exposición: uno, cinco, veinticinco, cincuenta y cien por ciento, con un tiempo de reposo entre cada uno. Ese tiempo importa más que los porcentajes. Un canario al uno por ciento sobre un servicio con cien peticiones por minuto recibe una petición cada minuto, así que diez minutos de reposo producen una muestra de diez peticiones: insuficiente para detectar un fallo que ocurre en el cinco por ciento de los casos. El paso de un escalón al siguiente debe decidirse por volumen observado, no por reloj, y esa es la razón por la que las promociones automáticas ciegas dan tan mal resultado en servicios de tráfico bajo.
versions upload
Crea el artefacto y no lo publica. Devuelve un identificador y, con las URL de vista previa activas, una dirección para ejercitarlo.
versions deploy
Decide el reparto de tráfico entre una o dos versiones. Es el único comando que cambia lo que ejecutan los usuarios.
deploy
La composición de ambos en un paso, al cien por cien. Perfectamente válido cuando el cambio no justifica un canario.
rollback
Reapunta el tráfico a una versión anterior que ya existe. No reconstruye nada, y por eso tarda segundos.
Atribuir lo que observas
Un despliegue gradual sin observabilidad por versión es teatro: mandas el diez por ciento del tráfico al código nuevo y luego miras un panel agregado donde ese diez por ciento se diluye. El binding version_metadata resuelve exactamente eso.
{
"version_metadata": { "binding": "CF_VERSION" }
}
export default {
async fetch(req: Request, env: Env): Promise<Response> {
try {
return await manejar(req, env);
} catch (err) {
console.error(JSON.stringify({
error: String(err),
version: env.CF_VERSION.id,
tag: env.CF_VERSION.tag,
}));
return new Response("error", { status: 500 });
}
},
};
Con ese identificador en cada log y en cada punto de datos de Analytics Engine, la comparación se vuelve trivial: tasa de error de la versión nueva frente a la vieja, latencia del percentil noventa y nueve de cada una, uso de CPU. Sin él, la única señal disponible es el agregado, y un agregado dominado por el noventa por ciento sano tarda mucho en delatar que el diez por ciento restante está fallando la mitad de las veces.
Escribe, antes de ejecutar el primer versions deploy, la condición numérica que hará que retrocedas: por ejemplo, que la tasa de error de la versión nueva supere en un punto porcentual a la anterior durante diez minutos seguidos. Decidido de antemano, es una regla; decidido durante el incidente, es una discusión. Y una discusión, mientras el tráfico sigue entrando, siempre tarda más de lo que crees.
Volver atrás y sus límites
El rollback es la operación inversa y es igual de rápida, porque no reconstruye nada: la versión anterior sigue existiendo como artefacto y solo hay que volver a apuntarle el tráfico.
wrangler versions list
wrangler rollback 8d5b3f77-... --message "regresion en calculo de portes"
Lo importante es tener presente qué queda dentro y qué queda fuera de su alcance. El rollback revierte el código, la configuración y los bindings de esa versión. No revierte nada que la versión defectuosa haya escrito fuera de sí misma: filas insertadas en D1, claves puestas en KV, objetos subidos a R2, mensajes encolados o migraciones de esquema ya aplicadas. Los Durable Objects merecen mención aparte, porque una instancia viva mantiene su estado a través del cambio de versión y puede haberlo dejado en una forma que el código antiguo no sabe leer.
De ahí se sigue la disciplina de las migraciones compatibles en ambos sentidos: se despliega primero el esquema que ambas versiones toleran, después el código que lo usa, y solo cuando la versión nueva lleva tiempo estable se retira lo viejo. Esa secuencia de tres pasos es lo que convierte el rollback en una operación realmente segura en lugar de un botón que devuelve el código pero deja los datos en tierra de nadie.
Un último matiz operativo: el rollback no borra la versión defectuosa ni reescribe el historial. Crea un despliegue nuevo que apunta a una versión antigua, de modo que la línea temporal de qué estuvo publicado y cuándo permanece intacta y auditable. Esa propiedad, que parece burocrática, es la que permite reconstruir un incidente después: el intervalo exacto en que la versión mala recibió tráfico está registrado, y con él puedes acotar qué peticiones y qué escrituras hay que revisar.
Es tentador entender el despliegue gradual como una técnica que hace los cambios más seguros, y esa lectura conduce a usarlo mal. Mandar el uno por ciento del tráfico a una versión no hace que esa versión sea menos defectuosa; hace que su defecto se manifieste sobre una centésima parte de tus usuarios y, sobre todo, que se manifieste antes de que lo haga sobre el resto. Lo que compras con un despliegue gradual no es seguridad, es información temprana a cambio de un daño acotado, y ese trato solo es rentable si te preparas para cobrarlo. Un canario al uno por ciento durante treinta segundos, sin métricas segmentadas por versión y sin un umbral escrito de antemano, no ha comprado información: ha comprado la sensación de haber sido prudente, que es peor que no haber sido prudente porque además baja la guardia. La forma correcta de pensarlo es como un experimento: tienes una hipótesis —esta versión no empeora la tasa de error—, una muestra cuyo tamaño eliges con el porcentaje, un tiempo de exposición que debe bastar para que el volumen sea significativo, y una regla de decisión fijada antes de mirar los datos. Si falta cualquiera de esas cuatro piezas, lo que estás haciendo no es un despliegue gradual, es un despliegue normal con un retraso decorativo. Y el corolario incómodo: como el reparto es por petición y el rollback no devuelve los datos, hay clases enteras de cambios —los que rompen compatibilidad, los que migran esquemas de forma destructiva— para las que el despliegue gradual no ofrece ninguna protección real. Reconocerlas y tratarlas con la secuencia de migración compatible es lo que separa a quien usa la herramienta de quien confía en ella.
- Sube dos versiones distintas de un Worker con
wrangler versions upload, cada una con su--tag, y abre la URL de vista previa de la nueva sin publicarla. - Añade el binding
version_metadatay emite en cada respuesta un log con el identificador y la etiqueta de la versión que la atendió. - Publica un reparto de diez y noventa, genera tráfico y comprueba en los logs que ambas versiones aparecen en la proporción esperada.
- Fuerza la versión nueva con la cabecera
Cloudflare-Workers-Version-Overridesy verifica que todas tus peticiones caen en ella. - Ejecuta
wrangler rollbacky escribe después, en dos líneas, qué efecto de la versión revertida sigue presente en tus datos. Si no encuentras ninguno, diseña un cambio que lo produzca y repítelo.