Blue-green vs canary: mudanças seguras na API e no banco de dados
Blue-green e canary explicados para mudanças de API e banco de dados, com passos práticos para reduzir risco de downtime durante migrações de esquema e atualizações móveis lentas.

Por que implantações ficam arriscadas com mudanças de esquema e atualizações móveis lentas
Um deploy pode parecer perfeito em testes e ainda falhar no momento em que recebe tráfego real. A razão comum é que não é só o código que muda. Seu contrato de API e o esquema do banco de dados também mudam, e raramente na mesma velocidade.
As coisas quebram quando partes diferentes do sistema discordam sobre o que é “correto”. Um backend novo espera uma coluna que ainda não existe. Um backend antigo grava dados num formato que o novo código não entende mais. Mesmo mudanças pequenas, como renomear um campo, apertar validação ou alterar um valor de enum podem causar erros em produção.
Apps móveis aumentam a aposta porque versões antigas permanecem em uso. Alguns usuários atualizam em minutos, outros em semanas. Isso significa que seu backend precisa atender várias gerações de clientes ao mesmo tempo. Se você lançar uma mudança de API que só funciona com o app mais novo, pode quebrar checkout, onboarding ou sync em background para uma parcela de usuários sem perceber imediatamente.
“Risco de downtime” não é só o site cair. Em sistemas reais, frequentemente aparece como falhas parciais:
- um pico de erros 4xx/5xx em endpoints específicos enquanto o resto parece normal
- falha de login porque tokens, papéis ou registros de usuário não batem mais com o esperado
- problemas silenciosos de dados (defaults errados, textos truncados, relacionamentos faltando) que aparecem dias depois
- jobs em background travando e formando uma fila que leva horas para drenar
É por isso que times comparam blue-green vs canary: você está tentando reduzir o raio de impacto quando mudanças não são perfeitamente compatíveis.
Blue-green e canary em linguagem simples
Quando as pessoas comparam blue-green vs canary, geralmente respondem a uma pergunta: você quer uma troca grande e controlada, ou um teste pequeno e cauteloso?
Blue-green: duas versões completas e uma troca de tráfego
Blue-green significa rodar dois ambientes completos ao mesmo tempo. “Blue” é a versão atual servindo usuários. “Green” é a nova versão, implantada e testada em paralelo. Quando estiver pronto, você redireciona o tráfego de blue para green.
Essa abordagem é ótima para previsibilidade. Dá para validar a nova versão com configurações próximas à produção antes de ela atender usuários reais, e então fazer um corte único limpo.
Rollback também é direto: se algo der errado depois da troca, roteie o tráfego de volta para blue. É quase um retorno instantâneo, mas caches, jobs em background e mudanças de dados ainda podem complicar a recuperação.
Canary: libere para uma pequena porcentagem primeiro
Canary significa que a nova versão entra em produção para uma pequena fatia de usuários ou requisições primeiro. Se estiver saudável, você aumenta essa porcentagem passo a passo até atender todo mundo.
Canary é melhor quando você teme comportamentos desconhecidos sob tráfego real. Dá para pegar problemas cedo, antes da maioria dos usuários sentir.
Rollback funciona reduzindo a porcentagem do canary a zero (ou parando o roteamento para a nova versão). Geralmente é rápido, mas nem sempre limpo, porque alguns usuários já podem ter criado dados ou estado que ambas as versões precisam entender.
Um jeito simples de lembrar os trade-offs:
- Blue-green favorece cortes limpos e switchbacks rápidos.
- Canary favorece aprendizado com tráfego real e raio de impacto limitado.
- Nenhuma das duas resolve automaticamente risco de banco de dados. Se mudanças de esquema não forem compatíveis, ambas podem falhar.
- Canary depende de monitoramento, porque a decisão é baseada em sinais ao vivo.
- Blue-green costuma exigir capacidade extra por rodar duas stacks completas.
Exemplo: se você libera uma API que às vezes retorna um campo novo, um canary ajuda a ver se clientes antigos travam com dados inesperados. Se a mudança exige renomear uma coluna que o código antigo não suporta, blue-green não vai salvar você a menos que a migração seja projetada para suportar ambas as versões.
O que torna migrações de banco diferentes de deploys de código
Um deploy de código geralmente é fácil de reverter. Se a nova versão se comporta mal, você redeploya o build antigo e volta ao estado anterior na maior parte dos casos.
Uma mudança no banco é diferente porque altera a forma dos dados. Depois que linhas são reescritas, colunas removidas ou constraints apertadas, voltar atrás raramente é instantâneo. Mesmo revertendo o código, ele pode não entender o novo esquema.
Por isso, o risco de downtime em migrações de esquema tem menos a ver com o método de deploy e mais com como a migração foi desenhada.
Princípios básicos de migração online
As migrações mais seguras funcionam enquanto versões antigas e novas da aplicação rodarem ao mesmo tempo. O padrão é simples: faça uma mudança que seja segura de ignorar, atualize o código para usá-la e depois limpe.
Uma sequência comum expandir-depois-contrair fica assim:
- Mudanças aditivas primeiro: adicione uma coluna nullable, uma nova tabela, um índice sem bloquear escritas.
- Comportamento duplo: escreva tanto no antigo quanto no novo, ou leia do novo com fallback para o velho.
- Backfill separado: migre dados existentes em pequenos lotes.
- Corte: mova a maior parte do tráfego para o novo comportamento.
- Mudanças destrutivas por último: remova colunas antigas, caminhos de código legados, aperte constraints.
“Migrações big bang” combinam os passos mais arriscados numa única release: locks longos, backfills pesados e código que assume que o novo esquema já existe em todo lugar.
Por que atualizações móveis lentas elevam o nível
Clientes móveis podem ficar em versões antigas por semanas. Seu backend precisa aceitar requisições antigas e produzir respostas antigas enquanto o banco evolui.
Se um app antigo envia uma requisição sem um campo novo, seu servidor não pode tornar esse campo obrigatório no banco de repente. Você precisa de um período em que ambos os comportamentos funcionem.
Qual estratégia reduz mais o risco de downtime em migrações
A escolha mais segura depende menos da ferramenta de deploy e mais de uma pergunta: ambas as versões, antiga e nova, conseguem rodar corretamente no mesmo esquema por um tempo?
Se a resposta for sim, blue-green costuma ser a opção de menor downtime. Você pode preparar a mudança no banco primeiro, manter o tráfego no stack antigo e então trocar o tráfego para o novo stack num único corte. Se algo estiver errado, volta rápido.
Blue-green falha quando a nova app exige o novo esquema imediatamente. Exemplos comuns: dropar ou renomear uma coluna que a versão antiga ainda lê, ou adicionar um constraint NOT NULL antes do app começar a gravar o valor. Nesses casos, o rollback pode não ser seguro porque o banco já está incompatível.
Canary é melhor quando você precisa aprender de forma controlada. Uma pequena parte do tráfego bate primeiro na nova versão, ajudando a detectar casos de borda como índices faltantes, padrões de consulta inesperados ou jobs em background se comportando diferente em produção. O trade-off é que você precisa manter ambas as versões funcionando, o que normalmente exige mudanças de banco compatíveis retroativamente.
Regra prática de decisão
Ao avaliar blue-green vs canary para risco de downtime em migrações:
- Escolha blue-green quando puder manter a mudança de esquema aditiva e compatível, e quiser um corte rápido e limpo.
- Escolha canary quando estiver incerto sobre o comportamento em produção ou quando esperar que formas raras de dados importem.
- Se a migração for forçar uma quebra imediata, não escolha entre blue-green e canary — mude o plano para expandir-depois-contrair.
Como “compatível” se parece na prática
Suponha que você esteja adicionando um novo campo à tabela orders. Um caminho seguro é: adicione a coluna como nullable, implante o app que a escreve, backfill as linhas antigas e só então imponha constraints. Nesse cenário, blue-green dá um corte limpo, e canary oferece um aviso cedo se algum caminho de código ainda assume a forma antiga.
Como atualizações móveis lentas mudam a escolha de implantação
Usuários web atualizam a página. Usuários móveis não.
No iOS e Android, pessoas ficam em versões antigas por semanas ou meses. Algumas nunca atualizam até que o app force, e alguns dispositivos ficam offline por longos períodos. Isso torna seu cliente “antigo” um teste permanente de compatibilidade retroativa.
Isso desloca o objetivo de “implantar sem downtime” para “manter várias gerações de clientes funcionando ao mesmo tempo”. Na prática, mobile frequentemente puxa você para uma mentalidade tipo canary para APIs, mesmo que use blue-green na infraestrutura.
Mudanças compatíveis vs versionamento de API
Na maior parte do tempo, você quer mudanças compatíveis retroativamente, porque elas permitem que clientes antigos e novos compartilhem os mesmos endpoints.
Exemplos compatíveis: adicionar novos campos, aceitar tanto formatos antigos quanto novos, manter campos de resposta existentes e evitar mudanças de significado. Versionamento de API vira útil quando o comportamento precisa mudar (não só adicionar dados), ou quando é preciso remover ou renomear campos.
Exemplo: adicionar um campo opcional marketing_opt_in normalmente é seguro. Mudar a forma como price é calculado normalmente não é.
Planejando uma janela de depreciação
Se precisar de uma mudança breaking, trate o fim do suporte como uma decisão de produto. Uma janela de depreciação útil é medida por “usuários ativos ainda em versões antigas”, não por dias no calendário.
Sequência prática:
- Lance o backend que suporta clientes antigos e novos.
- Publique o app novo e monitore adoção por versão.
- Avise ou restrinja somente quando versões antigas caírem abaixo de um limiar seguro.
- Remova o comportamento antigo por último, com plano de rollback.
Passo a passo: um rollout seguro para mudanças de API + banco de dados
Quando você muda API e banco ao mesmo tempo, o plano mais seguro costuma ter duas ou três etapas. Cada passo deve ser seguro para deploy isoladamente, mesmo se usuários manterem apps antigos por semanas.
Um padrão de rollout que evita quebrar clientes antigos
Comece com uma mudança de banco aditiva. Adicione colunas ou tabelas, evite renomear ou dropar, permita nulls quando necessário e use defaults para que código antigo não bata em constraints.
Depois envie código que tolere ambos os formatos de dados. Leitura deve aceitar “campo antigo ausente” e “campo novo presente”. Escritas devem continuar gravando o campo antigo por enquanto e, opcionalmente, gravar o novo também.
Sequência típica:
- Adicione peças novas no esquema (colunas, tabelas, índices) sem remover as antigas.
- Faça deploy do código que lê do novo ou do antigo e não quebra em nulls.
- Backfill linhas existentes em pequenos lotes e verifique contagens, taxas de null e performance de consultas.
- Mude o caminho de escrita para o novo campo, mantendo fallback nas leituras.
- Depois que versões antigas sumirem, remova o campo velho e o código de limpeza.
Backfill e verificação: onde os outages se escondem
Backfills falham com frequência porque são tratados como um script rápido. Rode-os gradualmente, monitorando carga, e verifique resultados. Se o novo comportamento precisa de um índice, adicione-o antes de mudar leituras ou escritas, não depois.
Exemplo: você adiciona phone_country_code para melhorar formatação. Primeiro adicione a coluna (nullable), atualize a API para aceitá-la mas ainda funcionar sem ela, backfill a partir dos números existentes e então comece a gravá-la em novos cadastros. Semanas depois, quando apps antigos sumirem, remova o parsing legado.
Ferramentas que tornam ambas as estratégias mais seguras (sem complicar demais)
Você não precisa de um setup complicado para tornar blue-green ou canary mais seguros. Alguns hábitos reduzem surpresas quando APIs e esquemas se movem em velocidades diferentes.
Dual-read e dual-write (mantenha temporário)
Dual-write significa que a aplicação grava dados tanto no local antigo quanto no novo durante a transição (por exemplo, users.full_name e um novo users.display_name). Dual-read significa ler de um ou outro, preferindo o novo e fazendo fallback para o antigo.
Isso compra tempo para atualizações lentas de clientes, mas deve ser uma ponte curta. Decida como vai remover, monitore qual caminho é usado (novo vs fallback) e adicione checagens básicas para garantir consistência entre as gravações.
Feature flags para mudanças de comportamento
Feature flags permitem implantar código sem ativar o comportamento arriscado. Isso ajuda tanto blue-green quanto canary porque separa “deployar” de “ligar”.
Exemplo: implante suporte a um novo campo de resposta, mas mantenha o servidor retornando o formato antigo até estar pronto. Então habilite para um grupo pequeno, observe erros e faça o ramp-up. Se algo quebrar, desligue a flag sem redeploy.
Mentalidade de contract testing (a API é uma promessa)
Muitos incidentes de migração não são realmente “problemas de banco”. São problemas de expectativa dos clientes.
Trate a API como uma promessa. Evite remover campos ou mudar significados. Faça campos desconhecidos opcionais. Mudanças aditivas (novos campos, novos endpoints) são normalmente seguras. Quebras devem esperar por uma nova versão da API.
Jobs de migração confiáveis
Migrações de esquema muitas vezes precisam de jobs de backfill para copiar dados, computar valores ou limpar. Esses jobs devem ser monótonos e repetíveis: seguros para rodar duas vezes, fáceis de retry, fáceis de rastrear e limitados para não sobrecarregar o sistema.
Erros comuns que causam outages durante migrações
A maioria das quedas por migração acontece quando uma release assume que tudo anda junto: todos os serviços deployam ao mesmo tempo, todos os dados estão limpos e todos os clientes atualizam rápido. Sistemas reais não funcionam assim, especialmente com mobile.
Padrões comuns de falha:
- Dropar ou renomear uma coluna cedo demais. Código antigo, jobs em background ou apps móveis antigos podem ainda usá-la.
- Assumir que clientes atualizam rápido. Releases móveis levam tempo para alcançar usuários.
- Rodar migrações que bloqueiam tabelas em horários de pico. Um índice “simples” pode bloquear writes.
- Testar apenas com dados limpos. Produção tem nulos, formatos estranhos, duplicatas e valores legados.
- Não ter um plano de rollback real para código e dados. “Reimplantar a versão anterior” não basta se o esquema mudou.
Exemplo: você renomeia status para order_status e deploya a nova API. O web app funciona. Apps móveis antigos ainda enviam status e agora a API rejeita as requisições, causando checkouts falhos. Se você dropou a coluna, restaurar o comportamento não é um switch rápido.
Uma abordagem melhor: faça mudanças em passos pequenos e reversíveis, mantenha caminhos antigo e novo funcionando juntos e escreva o que fará se métricas explodirem (como rotear tráfego de volta, qual feature flag desliga o comportamento novo e como validar e reparar dados se um backfill falhar).
Checklist rápido antes de deployar
Logo antes do release, uma checklist curta pega problemas que levam a rollbacks na madrugada. Isso importa muito quando você muda API e banco ao mesmo tempo, especialmente com atualizações móveis lentas.
Cinco checagens que previnem a maioria dos outages
- Compatibilidade: confirme que versões antiga e nova do app funcionam contra o mesmo esquema. Um teste prático é rodar o build de produção atual contra um banco de staging com a migração aplicada.
- Ordem das migrações: garanta que a primeira migração seja aditiva e agende mudanças destrutivas (dropar colunas, apertar constraints) para depois.
- Rollback: defina o undo mais rápido. Para blue-green é trocar o tráfego de volta. Para canary é enviar 100% para a versão estável. Se rollback precisa de outra migração, não é simples.
- Performance: meça latência das queries depois da mudança no esquema, não só correção. Índices faltantes podem fazer um endpoint parecer fora do ar.
- Realidade do cliente: identifique a versão móvel ativa mais antiga ainda chamando sua API. Se uma porcentagem significativa ainda estiver nela, planeje uma janela de compatibilidade maior.
Cenário de sanidade rápido
Se você está adicionando preferred_language, implemente a mudança no banco primeiro como nullable. Depois envie o código do servidor que lê quando presente, mas não exige o campo. Só depois que a maioria do tráfego estiver em apps atualizados torne o campo obrigatório ou remova comportamentos antigos.
Exemplo: adicionar um campo novo sem quebrar apps móveis antigos
Imagine que você adiciona country ao perfil e o negócio quer torná-lo obrigatório. Isso pode quebrar em dois pontos: clientes antigos não enviam o campo e o banco pode rejeitar writes se você aplicar NOT NULL cedo demais.
Uma abordagem segura é fazer duas mudanças separadas: primeiro adicione o campo de forma compatível, depois imponha “obrigatório” quando os clientes já tiverem atualizado.
Como fica com blue-green
Com blue-green você deploya a nova versão ao lado da antiga. Ainda assim, a mudança no banco precisa ser compatível com ambas. Fluxo seguro:
- deploy da migração (adicione
countrycomo nullable) - deploy da versão green que aceita
countryausente e usa fallback - testar fluxos chave contra green (signup, editar perfil, checkout)
- trocar o tráfego
Se algo der errado, volte. O ponto é que voltar só funciona se o esquema ainda suporta a versão antiga.
Como fica com canary
Com canary, expose o comportamento novo para uma pequena fatia (1% a 5%) e monitore erros de validação por campo ausente, mudanças de latência e falhas de banco inesperadas.
Uma surpresa comum é clientes móveis antigos enviando atualizações de perfil sem country. Se a API já tratar como obrigatório você verá 400s. Se o banco aplicar NOT NULL, poderá gerar 500s.
Sequência mais segura:
- adicione
countrycomo nullable (ou com default seguro como "unknown") - aceite
countryausente de clientes antigos - backfill
countrypara usuários existentes em job de background - imponha “obrigatório” depois (primeiro na API, depois no banco)
Depois do release, documente o que clientes antigos podem enviar e o que o servidor garante. Esse contrato escrito evita que a mesma quebra aconteça na próxima migração.
Se você está construindo com AppMaster (appmaster.io), a mesma disciplina de rollout se aplica mesmo que você gere backend, web e apps móveis nativos a partir de um modelo único. Use a plataforma para enviar mudanças aditivas de esquema e lógica de API tolerante primeiro e só aperte restrições quando a adoção acompanhar.
FAQ
Blue-green roda dois ambientes completos e troca todo o tráfego de uma vez. Canary libera a nova versão para uma pequena porcentagem primeiro e aumenta conforme os sinais em produção.
Use blue-green quando quiser um corte limpo e estiver confiante de que a nova versão é compatível com o esquema atual do banco de dados. É especialmente útil quando o risco principal é o código da aplicação, não o comportamento desconhecido em produção.
Escolha canary quando precisar aprender com tráfego real antes de liberar para todos — por exemplo, quando padrões de consulta, dados de borda ou jobs em background podem se comportar de forma diferente em produção. Reduz o raio de impacto, mas exige monitoramento atento e preparo para interromper o rollout.
Não. Se a mudança de esquema quebrar compatibilidade (como remover ou renomear uma coluna que código antigo ainda usa), tanto blue-green quanto canary podem falhar. A solução mais segura é projetar uma migração online que suporte versões antigas e novas ao mesmo tempo.
Usuários móveis podem permanecer em versões antigas por semanas, então seu backend precisa suportar várias gerações de clientes ao mesmo tempo. Isso normalmente significa manter compatibilidade retroativa por mais tempo e evitar mudanças que exijam atualização imediata de todos os clientes.
Comece por mudanças aditivas que o código antigo possa ignorar, como adicionar colunas nulas ou tabelas novas. Depois, implante código que leia ambos os formatos, faça backfills graduais, mude o comportamento e só então remova campos antigos ou aperte restrições.
Liste o que clientes antigos enviam e esperam receber, então evite remover campos ou mudar significados. Prefira adicionar campos opcionais, aceite tanto formatos antigos quanto novos e adie validações “obrigatórias” até a adoção estar alta o bastante.
Dual-write grava em ambos os lugares (antigo e novo) durante a transição; dual-read lê do novo e, se não houver, faz fallback para o antigo. Use temporariamente, monitore qual caminho é usado e tenha um plano claro de limpeza depois que clientes antigos desaparecerem.
Feature flags permitem deploys com o comportamento arriscado desligado, depois ativam gradualmente. Se erros subirem, desligue a flag sem redeploy, o que ajuda tanto em cortes blue-green quanto em ramp-ups canary.
Remover ou renomear colunas cedo demais, aplicar NOT NULL antes dos clientes enviarem o valor e executar migrações que bloqueiam tabelas em horários de pico são causas frequentes. Outra armadilha é assumir que dados de teste são iguais a produção — backfills falham diante de nulos e formatos estranhos.


