Migrar uma plataforma em produção para uma nova stack sem downtime
Como migrar uma plataforma em produção para uma nova stack sem downtime: sistemas lado a lado, uma jornada por vez e cada passo reversível.
Nesta página · 5 seções
Principais pontos
- Nunca tenha um dia em que só o sistema novo exista: passe toda requisição por um switch que pode mandá-la para a stack antiga ou para a nova.
- Migre jornadas de usuário, não camadas, para que cada passo seja entregue, medido e possa ser revertido de forma independente.
- Combine os números que significam “seguro” antes que o primeiro usuário real seja migrado.
Na avanzzada, liderei a migração completa de uma plataforma que se candidata automaticamente a vagas de emprego em nome dos seus usuários, levando toda a arquitetura de uma stack legada para uma nova enquanto as pessoas continuavam usando o produto todos os dias. Parar o produto para uma virada no fim de semana nunca foi uma opção: para os usuários, um dia perdido significa candidaturas perdidas.
Este post é a abordagem que tornou isso possível. Ela não é específica daquela plataforma. O mesmo padrão funciona para migrar um monólito para serviços, um app PHP para Node ou Go, ou um frontend antigo para Next.js, desde que o produto precise continuar rodando enquanto você trabalha.
Por que migrar, afinal?
Uma reescrita raramente é a resposta certa, então o motivo precisa ser concreto antes de você começar. Os motivos honestos são quase sempre um destes:
- Toda mudança é lenta. Funcionalidades pequenas levam semanas porque ninguém consegue prever o que elas vão quebrar.
- Releases são arriscados. Deploys acontecem raramente, à noite, com alguém de plantão.
- A plataforma limita o produto. A próxima coisa de que o negócio precisa (escala, novas integrações, um app mobile) é difícil ou impossível na stack atual.
- Ninguém quer trabalhar nela. Contratar e reter engenheiros para uma stack ultrapassada fica mais difícil a cada ano.
“O código é feio” não está nessa lista. Se o único problema é estético, refatore no lugar. Uma migração só vale a pena quando o sistema antigo está travando o negócio, e você deveria conseguir explicar como, em uma frase, para quem está pagando por ela.
Como rodar dois sistemas lado a lado?
A primeira decisão é a que torna todo o resto possível: nunca tenha um dia em que só o sistema novo exista. Toda requisição passa por um switch que decide, por usuário, qual stack vai atendê-la. Esse é o padrão strangler fig, com uma feature flag como switch.
// one switch, per user, reversible without a deploy
const useNew = await flags.isEnabled('new-apply-flow', { userId });
return useNew ? newApi.apply(job, user) : legacyApi.apply(job, user);Esse switch te dá três coisas que você não consegue de nenhum outro jeito:
- Usuários internos na stack nova primeiro, depois uma pequena parcela de usuários reais, depois todo mundo.
- O mesmo tipo de requisição atendido pelos dois sistemas, para que os resultados possam ser comparados.
- Um rollback que é uma mudança de flag, não um deploy de emergência.
O switch tem um custo. Por um tempo, você roda e paga dois sistemas, e alguns dados precisam ser legíveis pelos dois. Planeje esse período de forma explícita: decida onde fica a fonte da verdade de cada dado, e qual sistema o escreve, antes de mover qualquer tráfego.
Por que migrar um fluxo de cada vez?
Em vez de migrar camadas (primeiro o banco de dados, depois o backend, depois o frontend), migre jornadas de usuário: cadastro, depois a ação principal, depois a cobrança, e assim por diante. Cada jornada migra de ponta a ponta, é entregue sozinha e tem as próprias métricas e o próprio rollback.
Migrações camada por camada parecem organizadas no plano e não entregam nada até o final. Quando a última camada chega, meses de trabalho encontram usuários reais de uma vez, e se algo der errado você não consegue saber qual camada causou o problema.
Migrações jornada por jornada entregam valor desde o primeiro fluxo, e te ensinam. O primeiro fluxo que você migrar vai expor problemas no seu plano: dados que você não sabia que existiam, integrações que se comportam de outro jeito, casos de borda que ninguém documentou. Melhor aprender isso em um fluxo do que em todos.
Escolha o primeiro fluxo com cuidado. Ele precisa ser real o bastante para provar a stack nova sob tráfego de produção, mas não deve ser o que mais gera dinheiro. Uma jornada secundária com tráfego constante é o ideal.
Uma migração é um lançamento de produto que ninguém deveria perceber.
Filipe Eduardo
Medindo que nada quebrou
Para cada fluxo, combine os números antes de qualquer usuário ser migrado:
- Taxa de sucesso da ação principal (para uma plataforma de candidaturas: candidaturas enviadas com sucesso).
- Latência no percentil 95, comparada entre o antigo e o novo para o mesmo tipo de requisição.
- Taxa de erro no caminho novo, com um alerta que dispara antes de os usuários reclamarem.
Depois, mova o tráfego em etapas (usuários internos, 5%, 25%, 50%, 100%) e só avance quando os números se mantiverem por tempo suficiente para você confiar neles. Se não se mantiverem, volte o switch, corrija e tente de novo. Ninguém fora do time precisa saber.
Quanto tempo leva uma migração como essa?
Depende do número de jornadas, não do tamanho da base de código. Um fluxo pequeno pode ser migrado em poucas semanas quando o switch e o monitoramento já existem; uma plataforma inteira normalmente leva meses. O primeiro fluxo é sempre o mais lento, porque ele paga a infraestrutura que todos os fluxos seguintes reutilizam: o switch, os dashboards, a sincronização de dados.
Duas coisas encurtam o prazo mais do que qualquer outra: congelar funcionalidades novas na stack antiga (trabalho novo vai só para a stack nova) e apagar o código antigo assim que um fluxo chega a 100%. Cada semana em que as duas versões de um fluxo continuam vivas, você paga para manter as duas.
O que eu faria de novo?
Se eu tivesse que reduzir a abordagem a três regras:
- Coloque um switch na frente de tudo antes de escrever código novo.
- Migre jornadas, não camadas.
- Combine os números que significam “seguro” antes que o primeiro usuário seja migrado.
O resto é disciplina: avançar em passos pequenos, acompanhar os números e resistir à tentação de “simplesmente passar todo mundo” quando o primeiro fluxo parece bom.
Perguntas frequentes
Como migrar um sistema em produção sem downtime?
Rode o sistema antigo e o novo lado a lado atrás de um switch, normalmente uma feature flag, e mova o tráfego para a stack nova aos poucos. Compare taxa de sucesso, latência e erros para o mesmo tipo de requisição, e faça rollback mudando a flag em vez de fazer deploy. Os usuários nunca veem uma virada, porque não existe uma.
É melhor migrar por camada ou por funcionalidade?
Por funcionalidade, ou por jornada de usuário. Migrar uma jornada de ponta a ponta permite entregar, medir e reverter cada passo de forma independente, e você aprende com o primeiro fluxo antes de migrar o resto. Migrações camada por camada só entregam valor no final, quando todas as mudanças encontram usuários reais de uma vez.
O que medir durante uma migração?
Os números que os usuários sentem: a taxa de sucesso da ação principal, a latência no percentil 95 e a taxa de erro, comparadas entre a stack antiga e a nova para o mesmo tráfego. Combine os limites que significam “seguro” antes de migrar qualquer pessoa, e só avance para a próxima etapa de tráfego quando eles se mantiverem.
