Claude Code em migrations: o que dá para delegar no banco de dados e o que nunca aplicar sem revisão?

fluxo de Claude Code migrations mostrando arquivo de migration em revisão antes de aplicar no banco
Resposta rápida

Em Claude Code migrations a fronteira é simples: o agente escreve, você aplica. Delegar a escrita do arquivo de migration, o rollback junto, o mapa dos pontos do código afetados e o rascunho do expand and contract sai barato, porque a saída é arquivo e arquivo se revisa em diff. Executar contra banco com dado real é outra história: dois incidentes públicos de fevereiro de 2026 mostram agente rodando drizzle-kit push --force em produção e terraform destroy levando RDS e snapshots junto. Trava disso com plan mode, regras deny no .claude/settings.json e hook PreToolUse

Schema errado no código tu reverte com um commit

Dado apagado no banco, às vezes, não volta nunca

Essa assimetria é o coração do assunto. A pergunta que importa não é "a IA sabe escrever migration?", porque sabe, e escreve rápido. A pergunta é outra: até onde ela pode ir sozinha?

O post inteiro gira em torno de uma fronteira só: gerar é uma coisa, aplicar é outra. Bora separar as duas?

Por que a fronteira existe: dois casos reais de destruição em produção

Não é papo de segurança teórica, tem registro público disso acontecendo

A issue #27063 do repositório anthropics/claude-code, de 19/02/2026, relata que um agente do Claude Code executou por conta própria drizzle-kit push --force contra um Postgres de produção na Railway, apagando as tabelas do banco

E o banco não tinha backup automático nem point-in-time recovery

O segundo caso está na base pública de incidentes: em 26 de fevereiro de 2026, um agente do Claude Code rodando Terraform destruiu a infraestrutura de produção do DataTalks.Club depois que um state file desatualizado foi restaurado e um terraform destroy foi autorizado

Foram removidos VPC, cluster ECS, load balancers, bastion host, banco RDS e os snapshots automáticos

Segundo a reportagem da Tom’s Hardware, o responsável pelo DataTalks.Club admitiu ter dependido demais do agente para rodar comandos Terraform, e mudou o processo: passou a revisar manualmente cada plano apresentado pelo Claude e a executar ele mesmo as ações destrutivas

Repara na lição, que é bem específica: o que matou não foi a escrita

Ninguém perdeu banco porque o modelo escreveu um ALTER TABLE feio. Perdeu porque a execução foi autorizada

Domine o Claude Code do básico ao avançado
Pré-inscrição Formação Claude Code

Domine o Claude Code do básico ao avançado

Você vai aprender a criar sistemas completos com Claude Code, sem precisar ser programador. Inscreva-se para ter acesso a um desconto de lançamento e bônus especiais!

O que delegar com tranquilidade em mudança de schema:

Agora a parte boa, porque tem MUITA coisa que compensa jogar pro agente

A régua é sempre a mesma: se a saída é arquivo, tu revisa em diff antes de qualquer coisa tocar o banco. Custo do erro perto de zero

  • Escrever o arquivo de migration: descreve a mudança que tu quer, ele monta o arquivo no formato da tua ferramenta. Se estiver errado, tu lê e joga fora, ninguém morre
  • Gerar o rollback junto (down/reverse): essa é a que o dev humano mais deixa pela metade por preguiça, e é justamente onde o agente é bom. Peça o down no mesmo passo do up, sempre
  • Varrer o repositório e listar tudo que a mudança afeta: queries, tipos, serializers, seeds, testes, fixtures. Renomear uma coluna é fácil, achar os 14 lugares que citam ela é chato pra caramba, e é aí que ele brilha. Vale lembrar do que vive FORA do repo também, tipo automações low-code com n8n e Supabase plugadas direto na tabela: elas quebram junto e ninguém percebe no diff
  • Rascunhar o plano em três fases do expand and contract: o padrão expand and contract divide a mudança em expand (adiciona as estruturas novas sem remover as antigas), migrate (mantém as duas em sincronia enquanto a aplicação transiciona, com backfill em lotes) e contract (remove o schema antigo só depois que todo o tráfego migrou)
  • Revisar se a migration é retrocompatível: em deploy contínuo, versões diferentes da aplicação convivem com o mesmo banco. Então cada migration tem que rodar com sucesso enquanto a versão ANTIGA ainda está no ar, e só depois entra o código novo

Repara que nenhum desses itens produz efeito no banco

Todos produzem texto que tu lê antes

Se tu quer ir mais fundo na mecânica de mexer em banco com agente e ferramentas conectadas, tem um post aqui sobre migração de banco de dados segura que ataca o mesmo problema por outro ângulo

Gerar, revisar ou nunca: a tabela de decisão por tipo de operação

Montei a régua por operação, que é como a decisão aparece no dia a dia

<table> <thead> <tr> <th>Operação</th> <th>Delegar ao Claude Code</th> <th>Revisão humana obrigatória</th> <th>Quem executa</th> </tr> </thead> <tbody> <tr> <td>Criar tabela ou coluna nullable</td> <td>Sim, escrita completa com rollback</td> <td>Leitura em diff, rápida</td> <td>Humano, ou pipeline de deploy</td> </tr> <tr> <td>Adicionar índice</td> <td>Sim, incluindo o <code>down</code></td> <td>Sim, olhar tamanho da tabela e janela</td> <td>Humano</td> </tr> <tr> <td><code>ADD COLUMN NOT NULL</code> em tabela com linhas</td> <td>Só como proposta, sinalizando o risco</td> <td>Sim, linha a linha</td> <td>Humano, depois de reescrever em fases</td> </tr> <tr> <td>Backfill de dados</td> <td>Sim, o script em lotes</td> <td>Sim, amostra e critério de parada</td> <td>Humano</td> </tr> <tr> <td><code>DROP COLUMN</code> / <code>DROP TABLE</code></td> <td>Só o rascunho da fase contract</td> <td>Sim, com prova de que nada mais lê aquilo</td> <td>Humano</td> </tr> <tr> <td>Alteração de tipo com conversão</td> <td>Só o plano, nunca o comando pronto pra rodar</td> <td>Sim, incluindo o que acontece com valor inválido</td> <td>Humano</td> </tr> <tr> <td>Rodar a migration em produção</td> <td>Não</td> <td>Sim</td> <td>Humano</td> </tr> <tr> <td>Comandos de sync automático de schema</td> <td>Não</td> <td>Sim</td> <td>Humano, e olhe lá</td> </tr> <tr> <td>Comandos destrutivos de infraestrutura</td> <td>Não</td> <td>Sim</td> <td>Humano</td> </tr> </tbody> </table>

A coluna da direita conta a história toda, né? 🙂

Geração é do agente, aplicação em ambiente com dado real é humana

Como configurar a trava para o Claude Code não aplicar nada sozinho:

Boa intenção não é trava. Trava é configuração

  1. Comece em plan mode. O Shift+Tab cicla entre default, acceptEdits e plan no CLI. No plan mode o Claude pesquisa e propõe: lê arquivos, roda comandos de exploração e escreve um plano, mas não edita o código-fonte
  1. Entenda o que o acceptEdits libera de verdade. Ele deixa o Claude editar arquivos e rodar comandos comuns de sistema de arquivos (tipo mkdir e mv) sem perguntar, e continua pedindo permissão pros demais comandos. O erro comum aqui é ligar o acceptEdits achando que é "modo autônomo total" ou, pior, achando que é "modo seguro": ele é nem um nem outro
  1. Escreva as regras deny no .claude/settings.json da raiz do projeto. As regras seguem o formato Tool(especificador) e a ordem de avaliação é deny, depois ask, depois allow, onde a primeira correspondência nessa ordem decide o resultado
{
  "permissions": {
    "deny": [
      "Bash(drizzle-kit push --force)"
    ]
  }
}

O erro comum deste passo é contar com allowlist pra abrir exceção dentro de um deny. Não rola: uma regra deny ampla bloqueia todas as chamadas que casam com ela, inclusive as que também casam com uma regra allow mais específica. Deny não aceita exceção, e a especificidade da regra não muda a ordem de avaliação

Detalhe que confunde: quando tu escolhe a opção de não perguntar de novo e a aprovação é salva de forma permanente (caso dos comandos Bash), o Claude Code grava a regra no .claude/settings.local.json da raiz do repositório git. Vale dar uma olhada nesse arquivo de vez em quando pra ver o que tu foi liberando no piloto automático 😛

  1. Não confie em regra de Bash pra comando composto. O Claude Code reconhece os separadores de shell &&, ||, ;, |, |&, & e quebras de linha, e cada subcomando precisa casar com uma regra por conta própria. O erro comum é achar que barrar o comando isolado resolve, esquecendo que ele pode aparecer no meio de uma linha maior
  1. Ponha um hook PreToolUse como segunda camada. Num hook PreToolUse, sair com código 2 bloqueia a chamada da ferramenta, e o que for escrito em stderr chega ao Claude como feedback (ou seja, ele entende o motivo e não fica tentando de novo às cegas). Sair com código 0 significa nenhuma decisão, e o fluxo normal de permissão se aplica. A própria documentação de hooks traz como exemplo um script que inspeciona o comando Bash e, ao encontrar drop table, escreve o motivo em stderr e sai com código 2
# a ideia do exemplo oficial: inspeciona o comando, bloqueia e explica o porquê
echo "comando destrutivo bloqueado por política do projeto" >&2
exit 2

O erro comum deste passo é tratar o hook como substituto das regras deny. São camadas, não alternativas

  1. Saiba o que o sandbox cobre. O sandbox de Bash aplica isolamento de sistema de arquivos e de rede pelo sistema operacional pra todo comando Bash e seus processos filhos, e roda em macOS, Linux e WSL2. Windows nativo não é suportado. A divisão é essa: permissões controlam quais ferramentas o Claude Code pode usar e quais arquivos ou domínios acessa, o sandboxing dá a imposição no nível do SO
  1. Trate o --dangerously-skip-permissions como o que ele é. A flag remove a oportunidade de revisar as chamadas de ferramenta antes de rodarem, o CLI rejeita ela quando iniciado como root, e a documentação de dev containers orienta combinar com restrição de egresso de rede. Sabendo o que aconteceu nos dois incidentes lá de cima, dá pra imaginar o estrago dessa flag apontada pro banco certo, né?

E tem um ponto que fecha o raciocínio: as configurações gerenciadas por servidor são um controle do lado do cliente, não uma fronteira de segurança. Em dispositivo não gerenciado, o usuário não precisa de admin nem de sudo pra contornar

Por isso a documentação trata o assunto como defesa em profundidade, e não como um botão de segurança só

Os erros que aparecem em migration gerada por IA (e como prevenir):

Três padrões que aparecem sempre, com o remédio de cada um

A migration quebra na hora de rodar:

Sintoma: roda o migrate, ele estoura logo no começo

Causa: um erro comum em migration gerada por IA é adicionar coluna NOT NULL sem valor padrão em tabela que já tem linhas. ALTER TABLE ... ADD COLUMN ... NOT NULL sem DEFAULT falha nessa situação

Solução e prevenção: quebra em fases, no estilo expand. Adiciona a coluna nullable primeiro, faz o backfill em lotes na fase migrate, e só depois aperta a restrição. Chato? Um pouco. Mas é a diferença entre uma migration que passa e uma que trava o deploy no meio

A aplicação antiga quebra no deploy:

Sintoma: a migration passou lisa, mas a aplicação começou a dar erro

Causa: a migration não era retrocompatível. Em deploy contínuo, versões diferentes da aplicação convivem com o mesmo banco

Prevenção: a ordem é migration retrocompatível primeiro, deploy do código novo depois. Cada migration tem que rodar com sucesso enquanto a versão antiga ainda está no ar

O contract veio cedo demais:

Sintoma: tudo funcionando, aí some uma parte do tráfego com erro estranho

Causa: o schema antigo foi removido antes de todo o tráfego ter migrado

Prevenção: contract é a ÚLTIMA fase, e ela depende de evidência, não de sensação. Só derruba a estrutura velha quando ninguém mais lê ela

E o desfazer? Onde ele para:

O checkpointing do Claude Code rastreia as edições de arquivo feitas pelo Claude e permite reverter. O menu abre com /rewind ou apertando Esc duas vezes com o campo de prompt vazio

Ele oferece quatro ações: restaurar código e conversa, restaurar só a conversa, restaurar só o código, ou resumir a partir daquele ponto. O Claude Code guarda snapshots de arquivos dos 100 checkpoints mais recentes da sessão e apaga os checkpoints junto com as sessões após 30 dias, período configurável em cleanupPeriodDays

Agora o detalhe que muda tudo: o checkpointing só rastreia arquivos editados dentro da sessão atual. Mudança manual feita fora do Claude Code e edição de outra sessão concorrente normalmente não são capturadas

Ou seja, gravando na parede: checkpoint desfaz arquivo, checkpoint não desfaz banco

A fronteira em uma frase: o agente escreve, você aplica

Veredito sem enrolação

Delegar a escrita da migration, o rollback e o mapa de impacto no código economiza tempo real, e bastante. É trabalho de leitura e digitação, revisável em diff, com custo de erro baixíssimo. Nessa metade, usar o agente é decisão fácil

Delegar a execução contra banco com dado transfere um risco irreversível pra um processo que não tem como avaliar o custo do que está apagando. Um DROP bem-sucedido e um DROP catastrófico têm exatamente a mesma cara pro executor

E tem uma pré-condição que não é opcional: backup e recuperação testados vêm ANTES, não depois. Nos dois incidentes citados, o banco da Railway não tinha backup automático nem point-in-time recovery, e no caso do DataTalks.Club os snapshots automáticos foram removidos junto com o RDS

Backup não é plano B. É a condição pra tu poder errar

Conclusão

O próximo passo é bem concreto, dá pra fazer hoje

Abre o .claude/settings.json do teu projeto e escreve os deny do que nunca deve rodar por ali. Lembrando da ordem deny, ask, allow, e que deny amplo não abre exceção

Adota o plan mode como padrão em qualquer tarefa que encoste em schema, pra ele propor e tu decidir

E testa a migration proposta em ambiente descartável antes de qualquer ambiente com dado real

Se tu curtiu o assunto, vale seguir pela trilha de permissões e hooks, que é onde essa fronteira deixa de ser combinado e vira configuração de verdade

até o próximo post! =)

Perguntas frequentes

O Claude Code consegue rodar uma migration direto em produção sem eu aprovar?

Só se a configuração de permissão deixar. As regras são avaliadas na ordem deny, depois ask, depois allow, e a primeira que casar decide o resultado, então dá pra exigir aprovação (ou bloquear de vez) qualquer comando que toque o banco. O flag –dangerously-skip-permissions remove essa revisão prévia das chamadas de ferramenta, por isso não é recomendado perto de produção.

O modo acceptEdits do Claude Code é seguro pra deixar ele mexer no schema?

Não é "modo seguro" nem "autônomo total", apesar de parecer. No acceptEdits o Claude edita arquivos e roda comandos comuns de sistema de arquivos, tipo mkdir e mv, sem perguntar, mas continua pedindo permissão pros demais comandos. Rodar a migration em si ainda cai nessa pergunta, então acceptEdits sozinho não libera aplicar schema em produção.

Dá pra bloquear um comando tipo DROP TABLE antes dele rodar?

Dá, com um hook PreToolUse. A própria documentação traz um exemplo oficial de script que inspeciona o comando Bash e, ao encontrar ‘drop table’, escreve o motivo em stderr e sai com código 2, o que bloqueia a chamada da ferramenta. Saindo com código 0 o hook não decide nada e o fluxo normal de permissão segue.

As regras de permissão do Claude Code liberam um comando composto, tipo A && B?

Não. O Claude Code reconhece separadores de shell como &&, ||, ;, |, |& e quebra de linha, e cada subcomando precisa casar com uma regra de forma independente. Uma regra allow pra um comando isolado não libera ele escondido dentro de uma cadeia junto com outro comando.

O /rewind desfaz uma migration que já rodou no banco?

Não. O checkpointing rastreia só as edições de arquivo feitas pelo Claude dentro da sessão atual, e o menu, aberto com /rewind ou Esc duas vezes com o prompt vazio, restaura código, conversa ou os dois. Efeito que já aconteceu no banco fica de fora: reverter o arquivo da migration não desfaz o DROP TABLE que já rodou.

Onde ficam guardadas as regras de permissão que eu aprovo pro Claude Code?

As regras usam o formato Tool(especificador) e ficam em .claude/settings.json, na raiz do projeto. Quando você escolhe não perguntar novamente pra um comando Bash, essa aprovação permanente é gravada em .claude/settings.local.json, também na raiz do repositório git.



Subscribe
Notify of
guest

0 Comentários
Oldest
Newest Most Voted

Formações

Formação Vibe Coding

Formação Vibe Coding

Do Prompt ao Produto: Crie Software Real com IA

  • 474 aulas
  • 20 projetos
  • 39h 27min

Blog | Mais populares