Despliegues Canary con JavaScript: cómo lanzar actualizaciones sin miedo a romperlo todo
\n\n¿Alguna vez has lanzado una nueva función a medianoche, rezando para que el servidor no se caiga? ¿O te has despertado con una notificación de "error 500 en todas las páginas"? Te suena, ¿verdad? Los lanzamientos siempre son estresantes. Pero hay una manera de hacerlos tan tranquilos como una mañana de fin de semana: Despliegues Canary (implementaciones canary).
\n\nEl nombre proviene de una antigua práctica minera: llevar canarios a la mina. Si el pájaro se callaba, significaba que el aire estaba envenenado y había que salir urgentemente. En el mundo del desarrollo JavaScript, el principio es el mismo: primero enviamos un pequeño porcentaje de usuarios a la nueva versión, vigilamos la "salud" del sistema, y solo entonces hacemos el lanzamiento completo.
\n\nEn este artículo, veremos cómo implementar un despliegue canary en JavaScript puro (y un poco de Node.js). Sin herramientas corporativas complejas, solo código, lógica y sentido común.
\n\n¿Por qué canary y no azul-verde o actualización continua?
\n\nExisten varias estrategias de despliegue. Azul-verde es cuando tienes dos entornos idénticos y simplemente cambias el tráfico. Actualización continua es el reemplazo gradual de pods en Kubernetes. Pero canary tiene un superpoder: una prueba A/B real con usuarios en vivo.
\n\n- \n
- Menos riesgo: si la nueva versión "mata" la base de datos, solo el 1% de los usuarios se verá afectado, no el 100%. \n
- Retroalimentación rápida: ves las métricas del nuevo código antes de que llegue a todos. \n
- Reversión automática: si el error aumenta, el canary se apaga solo. \n
Arquitectura del despliegue canary en JavaScript
\n\nImagina que tenemos una aplicación Express que sirve páginas HTML. Queremos lanzar un nuevo diseño de la página principal. En lugar de cambiar el enrutador para todos, crearemos una capa intermedia: un middleware canary.
\n\nComponentes del sistema
\n- \n
- Feature flag — una bandera que activa la nueva versión para un grupo específico. \n
- Distribuidor de tráfico — la lógica que decide si mostrar la versión anterior (v1) o la nueva (v2). \n
- Monitoreo — recopilación de métricas (errores, latencia, conversiones). \n
- Reversión automática — si las métricas caen por debajo del umbral. \n
Ejemplo simple con Node.js y Express
\n\nEmpecemos con un servidor básico. Supongamos que tenemos dos manejadores: v1 (antiguo) y v2 (nuevo).
const express = require('express');\nconst app = express();\nconst PORT = 3000;\n\n// Versión antigua\napp.get('/api/v1/home', (req, res) => {\n res.json({ version: 'v1', message: 'El viejo y querido diseño' });\n});\n\n// Nueva versión (canary)\napp.get('/api/v2/home', (req, res) => {\n res.json({ version: 'v2', message: '¡Nuevo diseño brillante!' });\n});\n\napp.listen(PORT, () => console.log(`Servidor corriendo en ${PORT}`));\n\n\nAhora necesitamos decidir a quién mostrarle v2. La forma más sencilla es una selección aleatoria basada en el ID de usuario.
\n\nDistribución de tráfico: aleatoriedad determinista
\n\nNo queremos que un mismo usuario salte entre versiones en cada solicitud. Por eso usamos un hash del ID de usuario:
\n\nfunction getCanaryGroup(userId, percentage = 5) {\n // Hash simple: suma de códigos de caracteres\n const hash = userId.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);\n return (hash % 100) < percentage;\n}\n\n// Ejemplo de uso\nconsole.log(getCanaryGroup('user_123')); // true o false\n\n\nAhora, en el middleware, verificamos si el usuario pertenece al grupo canary:
\n\napp.use('/api/home', (req, res, next) => {\n const userId = req.headers['x-user-id'] || 'anonymous';\n \n if (getCanaryGroup(userId, 5)) { // 5% del tráfico\n req.url = '/api/v2/home';\n } else {\n