05 de ago. de 2025·8 min de leitura

Reconstruir app sem alterar o banco de dados: um plano de cutover que funciona

Aprenda a reconstruir a app sem alterar o banco de dados com um plano de cutover seguro que preserva contas e histórico, usando rollout em etapas e rollback.

Reconstruir app sem alterar o banco de dados: um plano de cutover que funciona

O que pode dar errado quando você reconstrói mantendo o mesmo banco de dados

Manter seu banco de dados atual pode parecer mais seguro do que uma migração completa. Mas uma reconstrução ainda pode falhar de formas que parecem “dados faltando” para os usuários. A maioria dos problemas vem de pequenas incompatibilidades entre o que a app antiga assumia e o que a nova realmente faz.

As falhas mais dolorosas aparecem como perda de contas ou históricos “vazios”. As linhas podem até estar lá, mas a nova app lê de forma diferente. Causas comuns incluem alteração no hash das senhas, normalização diferente de email (sensibilidade a maiúsculas/minúsculas e trimming), novos formatos de ID de usuário ou um novo provedor de autenticação que não mapeia bem para identidades existentes.

Uma reconstrução sem mudar o banco também corre risco de corrupção sutil durante o cutover. Se a app antiga e a nova escreverem ao mesmo tempo, você pode ramificar a realidade: registros duplicados, atualizações sobrescritas ou eventos registrados em um sistema mas não no outro.

Um plano de cutover precisa proteger algumas coisas acima de tudo: logins e mapeamento de identidade, ordem de gravações (sem gravações duplas nem atualizações perdidas), trilhas de auditoria e histórico (timestamps, mudanças de status, faturas, mensagens) e jobs em background (emails, webhooks, rotinas de cobrança) que possam reprocessar dados antigos.

Às vezes manter o banco de dados é a escolha errada. Se o esquema é inseguro (segredos expostos, evidência de SQL injection, constraints faltando) ou o modelo de dados é tão inconsistente que cada tela precisa de lógica especial, você pode gastar mais tempo remendando do que reconstruindo.

Um exemplo simples: sua reconstrução muda as regras de “membro da organização”. Dados antigos permitem múltiplos papéis por usuário, mas a nova app espera apenas um. Usuários de repente “perdem acesso” porque o novo código escolhe o papel errado. Detectar isso cedo é para isso que servem as verificações de compatibilidade.

Defina objetivos e escolha a abordagem de cutover

Comece escrevendo as promessas que você se recusa a quebrar. Isso evita que a reconstrução vire uma reescrita arriscada onde pessoas perdem acesso, histórico ou confiança.

A maioria das equipes termina com promessas como: contas existentes continuam funcionando, dados históricos permanecem intactos, e downtime é quase zero ou agendado e curto. Se alguma promessa puder ceder (por exemplo, uma manutenção de 10 minutos é aceitável), decida isso agora, não na noite do lançamento.

Transforme “sucesso” em checagens simples com as quais toda a equipe concorde. Por exemplo: usuários conseguem entrar e ver o mesmo papel e permissões; páginas-chave mostram os mesmos totais (pedidos, faturas, mensagens); novas ações geram exatamente uma gravação no banco; jobs críticos ainda rodam; e suporte tem um roteiro claro para os problemas mais prováveis.

Dois estilos comuns de cutover

Uma troca agendada (big-bang) move todo o tráfego para a nova app num momento planejado. É mais simples de raciocinar, mas o risco é maior se algo quebrar.

Uma mudança gradual desloca o tráfego por etapas (por porcentagem, por grupo de usuários ou por recurso). Leva mais tempo, mas reduz risco porque você aprende com uso real antes de ir até o fim.

Defina rollback como algo que você pode decidir rápido e executar sem suposições. Seja explícito sobre o que os usuários experimentarão (um período somente leitura vs. retorno completo à app antiga) e o que acontece com dados criados durante a tentativa.

Mapeie os dados atuais e as jornadas de usuário ligadas a eles

Tenha uma visão clara do que realmente está em produção hoje, não apenas do que o ERD diz. Reconstruções costumam quebrar porque uma tabela “pequena” ou um job em background estava fazendo muito trabalho silenciosamente.

Comece inventariando os dados por trás dos fluxos mais usados. Foque nas tabelas que decidem quem é o usuário, o que ele pode acessar e pelo que você recebe pagamento. Na prática, isso costuma significar tabelas de identidade e acesso, de billing/assinatura, seus registros centrais de domínio e configurações, e as tabelas de logs e integração que explicam o que aconteceu (e por quê).

Em seguida, mapeie os relacionamentos que você não pode quebrar. Procure chaves estrangeiras (ou “links” suaves impostos só pelo código), constraints de unicidade e registros “que devem existir” (como uma org padrão ou um papel dono). Anote o que precisa permanecer verdadeiro após o cutover, por exemplo: cada assinatura mapeia para exatamente uma conta ativa.

Por fim, documente onde os dados são criados ou editados. Mantenha simples: formulários UI, ferramentas admin, jobs em background, imports e webhooks. Capture as particularidades das quais a produção depende, mesmo que sejam bagunçadas.

Exemplo: se a app atual grava org_id de duas formas diferentes (cadastro web vs. onboarding com suporte de vendas), sua nova app precisa lidar com ambas.

Torne a autenticação e identidade de usuário compatíveis

Autenticação é normalmente a primeira coisa que quebra em produção. Pequenas incompatibilidades em IDs de usuário, associação a orgs ou papéis podem bloquear pessoas ou, pior, logá-las na conta errada.

Confirme o que o sistema trata como identidade “oficial”. É um user_id numérico, um UUID, um email ou um ID de provedor externo (Google, GitHub)? Escolha a chave estável e use-a da mesma forma em todo lugar: associação a org, permissões, propriedade de billing e logs de auditoria.

Rode uma verificação de compatibilidade que capture a maioria dos problemas:

  • O mesmo ID de usuário mapeia para o mesmo email e org em ambas as apps.
  • Tabelas de org e papéis significam a mesma coisa (não apenas têm os mesmos nomes).
  • Regras de unicidade batem com a realidade (especialmente unicidade de email).
  • Usuários deletados ou desabilitados se comportam da mesma forma.
  • Campos de auditoria como created_at e last_login são preservados.

O tratamento de senhas pede cuidado extra. Não re-hashe as senhas a menos que seja realmente necessário. Se a app antiga usa bcrypt e a nova espera argon2, continue verificando com o hash antigo e migre no próximo login (armazene o novo hash após um login bem-sucedido). Isso evita resets forçados, tickets de suporte e churn.

Sessões e tokens são a próxima armadilha. Durante um rollout por etapas, sessões antigas podem ainda existir. Decida se irá honrá-las, expirálas ou executar ambos validadores de token por uma janela curta. Se rotacionar chaves de assinatura, planeje isso como uma release, não uma surpresa.

Casos de borda sempre aparecem: emails duplicados de imports antigos, usuários em múltiplas orgs e nomes de papéis antigos como "owner" vs "admin". Corrija com uma camada de mapeamento, não com edições ad hoc no banco.

Desenhe mudanças de esquema e API para compatibilidade retroativa

Compatibilidade retroativa é o que mantém o cutover calmo. Comece concordando sobre o que não pode mudar porque a app antiga ainda depende disso.

Trate o banco de dados como um contrato: nomes de tabelas, colunas-chave, chaves primárias e o significado de valores existentes. Se o código antigo espera users.id como UUID e users.email como único, mantenha isso estável durante a transição.

Depois separe mudanças seguras das arriscadas.

Mudanças do tipo apenas adicionar costumam ser seguras: novas colunas nullable, novas tabelas e novos índices.

Mudanças que quebram (renomear colunas, trocar tipos, apertar constraints) devem esperar até que a nova app sirva todo o tráfego.

Um padrão prático é expandir primeiro, trocar depois e contrair por último. Exemplo: adicione users.timezone como nullable, lance a nova app para escrever nele, backfille linhas antigas e só então considere torná-lo NOT NULL.

Checklist prático de compatibilidade

Antes de aplicar qualquer migration, garanta:

  • Novas colunas começam como nullable ou têm default.
  • Novos enums aceitam valores antigos (ou você faz um mapeamento seguro).
  • Constraints são apertadas gradualmente (validar depois, impor depois).
  • Antigas APIs ainda funcionam mesmo que a nova app adicione campos.
  • Você tem um plano para backfill e verificação das linhas existentes.

Linhas antigas são a armadilha. Se novas regras de validação rejeitarem dados legados (telefone ausente, estados inválidos, nomes em branco), não "conserte" bloqueando logins. Use leituras tolerantes, scripts de migração e prompts direcionados (peça ao usuário para atualizar campos após login).

Plano de rollout em etapas (somente leitura até tráfego total)

Evitar Bloqueios de Login
Fazemos identidades, funções e sessões baterem com seu banco de dados atual.

Trate o cutover como uma série de pequenas apostas. Cada etapa deve ser reversível e ter um teste claro que prove que a nova app se comporta como a antiga.

Etapas (da mais segura à mais arriscada)

Comece apontando a nova app para os dados de produção, mas limitando o que ela pode fazer:

  • Rode a nova app em modo somente leitura. Deixe os usuários navegar, buscar e ver histórico, mas bloqueie ações que escrevem.
  • Adicione tráfego sombra para alguns endpoints chave. A app antiga serve respostas enquanto a nova executa as mesmas requisições em background e você compara os outputs.
  • Habilite um pequeno conjunto de ações de escrita para um grupo mínimo usando feature flags (usuários internos, depois uma pequena porcentagem).
  • Aumente o tráfego gradualmente (10%, 25%, 50%, depois 100%), com gates de monitoramento em cada passo.

Entre as etapas, pause e reveja o que aprendeu. Se algo parecer errado, pare o rollout e corrija antes de aumentar o risco.

Critérios para avançar

Escreva checks de “vai/não vai” antes para que decisões não sejam tomadas sob pressão:

  • Taxa de erro e latência dentro de um pequeno limiar comparado à app antiga.
  • Respostas sombra batem em campos chave (permissões, totais, preços, status).
  • Nenhuma escrita inesperada ocorre em modo somente leitura (confirme pelos logs do DB).
  • Tickets de suporte e reclamações não disparam após cada aumento.
  • Um runbook de rollback está testado e pode restaurar a app antiga em minutos.

Planeje as escritas durante a transição (evite forks de dados)

A forma mais rápida de quebrar um cutover é deixar ambas as versões da app escreverem nos mesmos registros de maneiras diferentes. Você precisa de uma regra clara: em cada etapa de rollout, quem pode escrever o quê.

Escolha uma fonte única de verdade para escritas. Frequentemente, a app antiga continua escrevendo enquanto a nova roda em somente leitura. Então você inverte: a nova app vira a escritora e a antiga passa a ser somente leitura ou tem endpoints de escrita bloqueados. Isso evita um "split brain" silencioso.

Gravações duplas (dual writes) parecem seguras, mas muitas vezes criam forks que você só percebe dias depois, como status de assinatura divergente ou faturas duplicadas. Faça dual writes só se puder provar que é seguro.

Se precisar suportar dual writes por uma janela curta, torne conflitos previsíveis:

  • Faça cada escrita idempotente usando um request ID estável.
  • Defina regras de conflito previamente (last-write-wins para campos de perfil, mas nunca para pagamentos).
  • Adicione uma tabela pequena de log de escrita (request ID, user ID, timestamp, entidade principal tocada).
  • Rejeite campos desconhecidos ou mapeamentos “palpiteiros”.
  • Coloque feature flags estritas em novos caminhos de escrita para desligá-los rápido.

Uma abordagem prática: durante a migração de cadastros, deixe a nova app criar contas, mas roteie resets de senha e mudanças de cobrança pela app antiga até verificar todos os caminhos de escrita.

Monitoramento e validação de dados durante o cutover

Cutover não é só um deploy. É um experimento ao vivo, especialmente enquanto você move o tráfego em etapas.

Comece com um conjunto enxuto de sinais que digam se usuários ainda conseguem entrar, navegar e salvar mudanças:

  • Taxa de sucesso de login e sucesso em resets de senha
  • Taxa de erro por endpoint (5xx, timeouts, erros de auth)
  • Falhas e retries em gravações (creates, updates, pagamentos, convites)
  • Latência em p95/p99
  • Saúde das filas e jobs em background (emails, webhooks, sincronização de billing)

Números não bastam. Valide os próprios dados em cada passo de tráfego (5% depois 25% depois 100%): compare contagens de linhas e totais para as entidades tocadas, cheque consistência de atividade recente (chaves estrangeiras, timestamps, status) e faça spot-check das últimas gravações para anomalias óbvias.

Some sinais de usuário por cima. Um pico de tickets de suporte, queda súbita após login ou capturas repetidas de “algo deu errado” muitas vezes mostram problemas antes dos dashboards.

Defina limiares de decisão antes de começar. Exemplo: se sucesso de login cair 2% por 10 minutos, ou falhas de escrita excederem 0,5%, pause o rollout e investigue. Se a causa não for óbvia rápido, volte atrás.

Plano de rollback que você consiga executar em minutos

Evitar Forks de Escrita Dupla
Diagnosticamos conflitos de gravação e adicionamos controles seguros para o cutover.

Rollback não é falha. É a válvula de segurança que protege contas e histórico.

Defina gatilhos claros de rollback

Escolha um pequeno conjunto de alarmes que forcem ação, não debate:

  • Falhas de login ou signup acima de um limite (por exemplo, 2% por 5 minutos)
  • Escritas suspeitas (campos obrigatórios ausentes, nulls inesperados, registros duplicados)
  • Regressão de performance grave (timeouts, CPU do BD no máximo, backlog de filas crescendo)
  • Sinais de segurança (bypass de auth, checagens de permissão não funcionando)

Quando um gatilho dispara, uma pessoa deve ter autoridade para acioná-lo. Todo mundo executa.

Passos de rollback em “minutos, não horas”

Escreva isso como um checklist de emergência e ensaie uma vez:

  • Redirecione o tráfego de volta para a app antiga (load balancer, feature flag ou toggle de deploy).
  • Congele novas gravações na app nova (retorne uma mensagem clara de manutenção).
  • Preserve evidências: logs de requisição, traces de erro e um snapshot das tabelas afetadas.
  • Drene ou pause jobs em background para que não continuem escrevendo dados ruins.
  • Verifique: uma pessoa checa logins e fluxos chave, outra checa saúde dos dados.

Para gravações parciais, mantenha uma regra simples de recuperação. Ou marque novas gravações por fonte (antiga vs nova) para isolar, ou enfileire as gravações e reproduza só após validação.

Erros comuns e armadilhas (e como evitá-los)

A maioria dos cutovers falha por motivos entediantes: uma pequena incompatibilidade que só aparece com usuários reais e histórico real. Trate compatibilidade como uma feature de produto, não como uma tarefa de última hora.

Um erro clássico é quebrar identidade de usuário. Se você mudar chaves primárias, renumerar usuários ou trocar formatos de UUID no meio do processo, pode desconectar silenciosamente contas de assinaturas, permissões e trilhas de auditoria. Mantenha os mesmos IDs de usuário de ponta a ponta, ou adicione uma camada de mapeamento estável e teste com um snapshot completo de produção.

Senhas são outra armadilha frequente. Equipes “migram” auth e acidentalmente resetam todo mundo porque o novo serviço não consegue verificar o esquema de hash antigo (ou esquece salt/pepper por usuário). Mantenha a verificação antiga funcionando, re-hasheie no login bem-sucedido e nunca logue ou exponha segredos durante o debug.

Dados de produção vão te surpreender. Dados de teste raramente incluem usuários deletados, emails duplicados de imports antigos, diferenças de timezone, registros parciais ou flags legadas que a UI ocultou anos atrás. Planeje testes em torno de casos de borda, não só caminhos felizes.

Cinco checagens evitam a maioria dos incidentes:

  • Congele formato de ID de usuário e chaves primárias durante o cutover.
  • Confirme hashing de senhas e regras de sessão/token iguais ao comportamento antigo.
  • Rehearse com um snapshot similar à produção e scripts de migração reais.
  • Inventarie writers em background (jobs, webhooks, cron) e limite-os durante o rollout.
  • Atrase mudanças de esquema que quebram até que a nova app detenha 100% do tráfego de leitura e escrita.

Checklist rápido antes, durante e depois do cutover

Verificação de Risco de Cutover
Revisamos autenticação, caminhos de escrita e armadilhas de esquema antes de você trocar o tráfego.

Trate o cutover como um ensaio, não um flip de chave único.

Antes do cutover (ensaie como se fosse real)

Verifique backups restaurando-os em um ambiente de teste e abrindo a app contra a cópia restaurada. Ensaie migrations end-to-end (aplicar, verificar, rollback) usando volume semelhante ao de produção. Confirme que monitoramento e alertas cobrem erros, latência e locks/queries lentas no banco. Rode fluxos de auth (login, signup, reset de senha, refresh de sessão, acesso por papéis) para alguns papéis reais. Amostre tabelas chave (users, assinaturas/pedidos, auditoria/histórico) e procure nulls inesperados, índices faltando e registros recentes inconsistentes.

Faça um dry run cronometrado com um pequeno grupo interno. Se algo estiver obscuro, transforme em um passo de runbook.

Durante e após o cutover (confie, mas verifique)

Comece com uma porcentagem mínima e aumente em passos. Confirme que o switch de rollback funciona sem deploy de código (pratique antes do dia). Valide gravações para os fluxos mais importantes (perfil, pagamentos, permissões). Cheque jobs em background (filas, cron, emails, webhooks) para garantir que disparam uma vez, não duas. Prepare suporte com um aviso de status curto, lista de problemas conhecidos e um acesso rápido para localizar usuários afetados.

Cenário de exemplo: reconstruindo uma SaaS mantendo todo o histórico

Uma pequena equipe SaaS decide reconstruir a web app porque a atual é lenta e frágil, mas não pode perder nada do banco: contas de usuário, projetos, faturas e status de assinatura. O objetivo é reconstruir a app sem mudar o banco e então trocar o tráfego com segurança.

Primeiro, eles colocam a nova app em modo somente leitura. Usuários continuam editando pela app antiga, mas a nova consegue logar, carregar projetos, mostrar faturas antigas e exibir o plano atual. Internamente, a equipe compara contagens e totais (projetos por usuário, valor da última fatura, próxima data de renovação) entre telas antigas e novas. Clientes não notam mudança, mas a equipe encontra rapidamente onde as suposições diferem.

Em seguida, liberam para uma pequena coorte: 5% dos usuários com baixo risco de suporte e mistura de tipos de plano. Observam sucesso de login, tempo de carregamento de projetos, totais de fatura batendo com a app antiga e novos tickets de suporte mencionando dados faltantes.

No segundo dia, encontram uma discrepância de cobrança em planos anuais. A nova app mostra “pago” corretamente, mas grava um next_billing_date errado quando um usuário atualiza o perfil da empresa. Isso é motivo de rollback. Eles retornam o tráfego para a app antiga, desabilitam gravações na nova e reaplicam o pequeno conjunto de atualizações afetadas usando um script e logs de auditoria. Ninguém perde histórico, e a correção é testada novamente em modo somente leitura antes de reabilitar a coorte.

Próximos passos e quando a FixMyMess pode ajudar

Trate o plano de cutover como um projeto próprio. Comece escrevendo o que não pode quebrar: login, cobrança, relatórios chave e integrações que escrevem no banco.

Traga ajuda cedo se você vê padrões como problemas intermitentes de sessão entre ambientes, um esquema que “cresceu organicamente” com dono de responsabilidade incerto, ou gaps de segurança como segredos expostos e queries SQL suspeitas.

Se você herdou um protótipo gerado por IA que está falhando sob comportamento real de produção, FixMyMess (fixmymess.ai) foca em diagnosticar e reparar essas lacunas — especialmente problemas de auth, padrões de esquema inseguros e lógica frágil — para que seu plano de rollout e rollback combine com o que o código realmente faz. Um ponto de partida comum é uma auditoria de código gratuita para expor os pontos reais de falha antes de você trocar o tráfego.