Introducción: ¿Por qué todavía tienes miedo de presionar "Deploy"?
\n\nEscribiste el código. Lo probaste en tu máquina local. Las pruebas pasaron. Todo perfecto. Presionas el tan esperado botón Deploy... y después de 5 minutos ves cómo aparece el error 500, y los usuarios en pánico escriben al soporte. ¿Te suena familiar?
\n\nLas estrategias de despliegue tradicionales (blue-green, rolling update) son buenas, pero tienen un inconveniente: o incluyen todo el código nuevo de una vez, o no lo incluyen en absoluto. ¿Y si pudieras enviar una nueva funcionalidad solo al 1% de los usuarios, observar las métricas, y solo después, asegurándote de la estabilidad, liberarla al 100%?
\n\nAsí es exactamente como funcionan los canary deployments (lanzamientos canary). El nombre proviene de una antigua práctica minera: llevar un canario a la mina para detectar a tiempo una fuga de gas. Si el pájaro se callaba, los mineros sabían: era hora de subir. En el mundo de la TI, el "canario" es un pequeño grupo de usuarios en los que se prueba el nuevo código. Si todo va bien, el lanzamiento continúa. Si no, reviertes más rápido de lo que dices "hotfix".
\n\nEn este artículo, escribiremos un sistema completo de canary deployment en TypeScript. Aprenderás cómo configurar el balanceo de tráfico, cómo automatizar el proceso a través de CI/CD y cómo monitorear la "salud" de tu canario. ¡Vamos allá!
\n\n¿Qué es un Canary Deployment y por qué es necesario?
\n\nCanary deployment es una estrategia de lanzamiento de nuevo código en la que la versión actualizada de la aplicación se envía primero a un pequeño porcentaje de usuarios (generalmente del 1 al 10%). El resto continúa trabajando con la versión anterior y estable. Si la nueva versión no presenta problemas (errores, caídas de rendimiento, aumento de latencia), aumentas gradualmente el porcentaje de tráfico hacia ella hasta que reemplace por completo a la anterior.
\n\nVentajas clave:
\n- \n
- Minimización de riesgos — los errores afectan solo a una pequeña parte de la audiencia. \n
- Reversión rápida — basta con redirigir el tráfico de vuelta a la versión estable. \n
- Pruebas A/B en condiciones reales — puedes comparar las métricas de la versión antigua y la nueva. \n
- Despliegue gradual — no sobrecargas el servidor con un aumento repentino de carga. \n
Ahora pasemos de la teoría a la práctica. Construiremos un sistema que consta de tres partes:
\n- \n
- Backend en TypeScript (Express.js). \n
- Balanceador de tráfico en Nginx. \n
- Automatización a través de GitHub Actions. \n
Paso 1. Escribir una aplicación en TypeScript con soporte de versionado
\n\nPara empezar, crearemos una aplicación Express simple que pueda funcionar en dos modos: stable y canary. La diferencia estará solo en la respuesta al endpoint raíz, pero en un proyecto real podría ser cualquier nueva funcionalidad.
\n\n// src/app.ts\nimport express, { Request, Response } from 'express';\nimport dotenv from 'dotenv';\n\ndotenv.config();\n\nconst app = express();\nconst PORT = process.env.PORT || 3000;\nconst VERSION = process.env.VERSION || 'stable'; // 'stable' o 'canary'\n\napp.get('/', (req: Request, res: Response) => {\n res.json({\n message: 'Hello from API!',\n version: VERSION,\n timestamp: new Date().toISOString(),\n });\n});\n\n// Endpoint para health check\napp.get('/health', (req: Request, res: Response) => {\n res.status(200).send('OK');\n});\n\napp.listen(PORT, () => {\n console.log(`Server running on port ${PORT} in ${VERSION} mode`);\n});\n\n\nAhora crearemos un Dockerfile para empaquetar la aplicación en un contenedor. Esto simplificará el despliegue y el balanceo.
\n\n# Dockerfile\nFROM node:18-alpine AS builder\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci\nCOPY . .\nRUN npm run build\n\nFROM node:18-alpine AS runner\nWORK