Banco de dados e migrations quando quem escreve o código é um agente

Drift de nomenclatura entre ORM e Postgres, a regra das duas etapas para mudança destrutiva e o que eu leio linha por linha em toda migration escrita por IA.

Banco de dados e migrations quando quem escreve o código é um agente
Neste artigo
  1. Por que o drift de nomenclatura custa tanto tempo?
  2. Por que a migration é o ponto de revisão?
  3. Como fazer mudança destrutiva sem risco?
  4. Como tratar seed e dado de teste?
  5. Como pedir a migration para o agente?
  6. Perguntas frequentes

Quando o agente escreve as migrations, três coisas mudam: a convenção de nomes precisa estar escrita antes, toda migration destrutiva vira revisão manual obrigatória, e o rollback tem que existir antes de rodar em produção. Código ruim se reescreve numa tarde. Schema ruim com dado de cliente em cima você carrega por anos.

Por que o drift de nomenclatura custa tanto tempo?

O ORM fala uma língua e o Postgres fala outra. Prisma e TypeORM trabalham em camelCase, o Postgres é feliz em snake_case, e existe um mapeamento no meio. Enquanto todo acesso passa pelo ORM, ninguém percebe.

O problema aparece na primeira query crua. O agente escreve um relatório com SQL na mão, usa createdAt porque foi isso que ele viu no modelo, e o Postgres responde que a coluna não existe, ou pior, encontra uma coluna parecida e devolve dado errado sem erro nenhum.

A correção é chata e definitiva: escolher uma convenção, escrever no arquivo de decisões, e nunca mais discutir.

# Convencao de nomes (nao reabrir)

- Postgres: snake_case, tabela no plural (users, project_members)
- Prisma: camelCase no modelo, @map explicito pra toda coluna
- Query crua: SEMPRE snake_case, sempre com o nome real da coluna
- Boolean: prefixo is_ ou has_ (is_active, has_billing)
- Timestamp: created_at, updated_at, deleted_at

Por que a migration é o ponto de revisão?

Eu deixo o agente escrever quase tudo sem ler linha por linha. Migration não entra nessa lista, por um motivo simples: é a única categoria de mudança onde o erro não é reversível por deploy.

Se um endpoint sai errado, você corrige e sobe de novo. Se uma migration apagou uma coluna com dado, o deploy seguinte não traz o dado de volta.

O que eu procuro na leitura, sempre nessa ordem:

O que Por que importa O que eu faço
DROP COLUMN ou DROP TABLE Perda de dado sem volta Só aprovo em duas etapas: para de usar, depois remove
Mudança de tipo em coluna com dado Pode truncar em silêncio Exijo migration de cópia, nunca conversão direta
NOT NULL em tabela populada Quebra na hora se houver nulo Default primeiro, backfill depois, constraint por último
Índice novo em tabela grande Trava escrita durante a criação Criação concorrente
Renomear coluna Quebra o código antigo durante o deploy Adiciona a nova, migra, remove depois

Como fazer mudança destrutiva sem risco?

Quase toda mudança perigosa de schema fica segura se você quebrar em duas migrations separadas por um deploy.

Remover uma coluna vira: primeiro um deploy onde o código para de ler e escrever nela, com a coluna ainda lá; depois, num deploy seguinte, a migration que remove. Entre os dois você tem uma janela onde dá pra voltar atrás sem perder nada.

O agente não faz isso sozinho porque a instrução “remova a coluna X” tem uma implementação óbvia de uma etapa. Quando eu escrevo na spec que mudança destrutiva é sempre em duas etapas, ele passa a fazer certo.

Como tratar seed e dado de teste?

Um detalhe pequeno que evita retrabalho: peça o seed junto com a migration. Sem dado de exemplo, você só descobre que o schema está estranho quando a tela já foi construída em cima dele.

Eu peço seed com pelo menos dois tenants diferentes, sempre. É a forma mais barata de fazer aparecer um vazamento de isolamento entre clientes ainda no ambiente local, em vez de em produção. Mais sobre isso em autenticação e autorização em SaaS multi-tenant.

Como pedir a migration para o agente?

A diferença entre um pedido que dá certo e um que dá trabalho está no que você fecha antes:

Adicione a tabela invoices.

Restricoes:
- snake_case, plural, created_at/updated_at obrigatorios
- tenant_id NOT NULL com FK e indice
- valor em inteiro de centavos, nunca float
- status em enum: draft, open, paid, void
- migration reversivel, com down implementado
- seed com 2 tenants e 3 invoices cada, status diferentes

“Valor em inteiro de centavos, nunca float” é o tipo de linha que parece pedante e economiza um bug de arredondamento em dinheiro que só aparece na conciliação do primeiro mês.

Perguntas frequentes

Posso deixar o agente rodar a migration sozinho?

Em ambiente local, sim, e é até bom, porque ele descobre o erro na hora. Em produção, não. A migration de produção é passo manual no meu fluxo, mesmo quando o deploy é automatizado.

E se o schema já nasceu torto?

Corrige por partes, com a regra das duas etapas, começando pelo que trava mais. Reescrever schema inteiro de uma vez, com dado em cima, é o tipo de operação que estraga o fim de semana.

ORM ou SQL cru?

ORM pro CRUD, SQL cru pro que o ORM faz mal, que costuma ser relatório e agregação. O que não pode é ter os dois com convenções diferentes. Contexto completo em como construir um SaaS com Claude Code e Codex.

WA in X