Deploy Canary com JavaScript: como lançar atualizações sem medo de quebrar tudo
\n\nVocê já lançou um novo recurso à meia-noite, rezando para o servidor não cair? Ou acordou com uma notificação "erro 500 em todas as páginas"? Familiar, né? Lançamentos são sempre estressantes. Mas existe uma maneira de torná-los tranquilos como uma manhã de fim de semana: Canary deployments (implantações canárias).
\n\nO nome vem de uma antiga prática de mineradores — levar canários para a mina. Se o pássaro silenciasse, significava que o ar estava envenenado e era preciso sair correndo. No mundo do desenvolvimento JavaScript, o princípio é o mesmo: primeiro, enviamos uma pequena porcentagem de usuários para a nova versão, monitoramos a "saúde" do sistema e só então fazemos o lançamento completo.
\n\nNeste artigo, vamos abordar como implementar um canary deployment em JavaScript puro (e um pouco de Node.js). Sem ferramentas corporativas complexas — apenas código, lógica e bom senso.
\n\nPor que canary, e não blue-green ou rolling update?
\n\nExistem várias estratégias de deploy. Blue-green é quando você tem dois ambientes idênticos e simplesmente alterna o tráfego. Rolling update é a substituição gradual de pods no Kubernetes. Mas o canary tem um superpoder: um teste A/B real com usuários reais.
\n\n- \n
- Menos risco: se a nova versão "matar" o banco de dados, apenas 1% dos usuários será afetado, não 100%. \n
- Feedback rápido: você vê as métricas do novo código antes que ele chegue a todos. \n
- Rollback automático: se o erro aumentar, o canary é desligado automaticamente. \n
Arquitetura de canary deployment em JavaScript
\n\nImagine que temos uma aplicação Express que serve páginas HTML. Queremos lançar um novo layout para a página inicial. Em vez de alterar o roteador para todos, criaremos uma camada intermediária — um canary middleware.
\n\nComponentes do sistema
\n- \n
- Feature flag — uma flag que ativa a nova versão para um grupo específico. \n
- Distribuidor de tráfego — a lógica que decide: entregar a versão antiga (v1) ou a nova (v2). \n
- Monitoramento — coleta de métricas (erros, latência, conversões). \n
- Rollback automático — se as métricas caírem abaixo do limite. \n
Exemplo simples com Node.js e Express
\n\nVamos começar com um servidor básico. Suponha que temos dois handlers: v1 (antigo) e v2 (novo).
const express = require('express');\nconst app = express();\nconst PORT = 3000;\n\n// Versão antiga\napp.get('/api/v1/home', (req, res) => {\n res.json({ version: 'v1', message: 'Bom e velho design' });\n});\n\n// Nova versão (canary)\napp.get('/api/v2/home', (req, res) => {\n res.json({ version: 'v2', message: 'Design novo e brilhante!' });\n});\n\napp.listen(PORT, () => console.log(`Servidor rodando na porta ${PORT}`));\n\n\nAgora precisamos decidir para quem mostrar a v2. A maneira mais simples é uma seleção aleatória baseada no user ID.
\n\nDistribuição de tráfego: aleatoriedade determinística
\n\nNão queremos que o mesmo usuário fique pulando entre versões a cada requisição. Por isso, usamos um hash do user ID:
\n\nfunction getCanaryGroup(userId, percentage = 5) {\n // Hash simples: soma dos códigos dos caracteres\n const hash = userId.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);\n return (hash % 100) < percentage;\n}\n\n// Exemplo de uso\nconsole.log(getCanaryGroup('user_123')); // true ou false\n\n\nAgora, no middleware, verificamos se o usuário está no 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% do tráfego\n req.url = '/api/v2/home';\n } else {\n