Use Cases (Atualizado: 02/06/2026)

Migração de banco de dados com Claude Code em produção

Use Claude Code em migrações DB com segurança: expand/contract, Prisma, CI, backfill, locks e limites de rollback.

Migração de banco de dados com Claude Code em produção

Migração de banco de dados em produção não deve começar com um prompt vago como “mude o schema”. O risco real não está apenas na sintaxe SQL. Ele está na ordem das operações: deploy da aplicação, locks do banco, backups, backfill de dados, feature flags, checks de CI e plano de rollback.

Claude Code é útil quando atua primeiro como revisor e depois como assistente de implementação. Ele vira um risco quando gera uma migração grande e a equipe aplica sem dividir as etapas. Este guia é voltado para times que usam PostgreSQL e Prisma Migrate, mas o mesmo modelo serve para migrações SQL manuais.

Durante a revisão, mantenha as fontes oficiais abertas: documentação do Claude Code, PostgreSQL sobre explicit locking e ALTER TABLE, Prisma sobre development and production e CLI reference, além da GitHub Actions workflow syntax.

Modelo expand/contract

O modelo central é expand/contract. Expand significa ampliar o banco para que a versão antiga e a versão nova da aplicação possam rodar ao mesmo tempo. Contract significa remover colunas antigas, leituras antigas ou constraints temporárias somente depois que o novo caminho estiver estável em produção.

flowchart LR
  A["Backup and review"]
  B["Expand: add nullable column or new table"]
  C["Deploy code with dual write or feature flag"]
  D["Backfill data in small batches"]
  E["Validate staging and production metrics"]
  F["Contract: add NOT NULL, remove old path"]
  A --> B --> C --> D --> E --> F

O erro comum é pedir para Claude Code adicionar uma coluna, copiar dados, aplicar NOT NULL e remover a coluna antiga em uma única migration. Isso pode funcionar em um banco local pequeno. Em uma tabela users ou orders com milhões de linhas, pode causar locks longos, escrita bloqueada, timeout e perda de dados.

Alguns termos precisam ficar claros. Lock é o mecanismo usado pelo banco para impedir operações concorrentes incompatíveis. Backfill é o processo que preenche novas colunas para linhas existentes. Shadow database é um banco temporário que o Prisma usa em desenvolvimento para reproduzir o histórico de migrations e detectar drift. Não é uma garantia automática para produção.

Comece pedindo revisão

O primeiro prompt deve pedir revisão, não edição de arquivos. Informe tamanho das tabelas, estilo de deploy, ORM e expectativas de recuperação.

Review this database migration plan before editing files.

Context:
- Production database: PostgreSQL
- ORM: Prisma Migrate
- Hot tables: users has about 8 million rows, orders has about 25 million rows
- Deploy style: blue/green app deploy, database migration runs in CI/CD
- Requirement: split users.name into users.full_name and users.display_name

Check:
1. Can old and new app versions run at the same time?
2. Which SQL statements may take strong locks or scan the whole table?
3. Which steps must be expand, backfill, validate, and contract?
4. What backup or point-in-time recovery check is needed before deploy?
5. What can be rolled back by app deploy, and what can only be rolled forward?

Return a migration plan first. Do not edit files yet.

A última frase importa. Claude Code consegue modificar arquivos rápido, mas migração de banco pede uma pausa de desenho. Se o primeiro plano mistura passos perigosos, peça uma versão mais conservadora.

Rewrite the plan so that no step drops a column, rewrites a large table, or sets NOT NULL before the backfill is verified. Include a staging rehearsal and a production abort condition.

Assim, Claude Code vira um revisor de migration. A pessoa decide se o risco é aceitável, e o agente ajuda a listar arquivos, comandos, SQL, checks de CI e sinais de observabilidade.

SQL de expand

Imagine que users.name será substituído por full_name e display_name. A migration de expand só adiciona colunas nullable e um índice. Ela não faz backfill, não coloca NOT NULL e não remove users.name.

-- 20260602090000_expand_users_names.sql
-- Keep this migration small. Do not backfill and do not drop users.name here.

ALTER TABLE users
  ADD COLUMN full_name text,
  ADD COLUMN display_name text;

-- Run outside a transaction in PostgreSQL migration tools that support it.
-- CREATE INDEX CONCURRENTLY cannot run inside a transaction block.
CREATE INDEX CONCURRENTLY IF NOT EXISTS users_display_name_idx
  ON users (display_name);

A documentação de ALTER TABLE do PostgreSQL mostra que subcomandos diferentes podem exigir níveis diferentes de lock. Se a documentação não indicar um lock mais leve, revise de forma conservadora. Peça para Claude Code apontar a fonte oficial, não apenas “parece seguro”.

Com Prisma, gere a migration sem aplicar e revise o SQL.

npx prisma migrate dev --name expand-users-names --create-only
npx prisma validate

O guia do Prisma diz que ambientes de teste e produção devem usar npx prisma migrate deploy. Ele também diz que migrate deploy aplica migrations pendentes, mas não detecta drift e não depende de shadow database. Portanto, o comando passar não significa que a produção foi ensaiada.

npx prisma migrate deploy

Mudança segura na aplicação

Depois do expand, a aplicação precisa funcionar com os dois formatos. Leituras precisam de fallback, e escritas devem preencher campos antigos e novos durante a transição.

// src/domain/userName.ts
type UserNameRow = {
  name: string | null;
  fullName: string | null;
  displayName: string | null;
};

export function readDisplayName(user: UserNameRow): string {
  return user.displayName ?? user.fullName ?? user.name ?? "Unknown user";
}

export function buildNameUpdate(input: { name: string }) {
  const normalized = input.name.trim().replace(/\s+/g, " ");

  return {
    name: normalized,
    fullName: normalized,
    displayName: normalized.length > 40 ? `${normalized.slice(0, 39)}...` : normalized,
  };
}

Uma feature flag separa a mudança de banco da experiência do usuário. Mantenha o novo caminho de leitura desligado durante o backfill, ligue depois da validação e desligue se surgirem erros. Para a implementação, veja o guia interno de feature flags com Claude Code.

Esse padrão serve para pelo menos três casos: renomear ou dividir campos de perfil, adicionar colunas calculadas como saldo de fatura ou label de busca, e adicionar índices ou foreign keys em tabelas quentes. Em todos, separar expand, deploy, backfill, validação e contract reduz risco.

Backfill em lotes

Um UPDATE gigante aumenta locks, volume de WAL, lag de réplica e risco operacional. Melhor pedir para Claude Code gerar um script em lotes, reiniciável.

// scripts/backfill-user-names.mjs
import pg from "pg";

const { Client } = pg;
const batchSize = Number(process.env.BATCH_SIZE ?? 1000);
const sleepMs = Number(process.env.SLEEP_MS ?? 200);

const client = new Client({ connectionString: process.env.DATABASE_URL });

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

await client.connect();

try {
  let total = 0;

  while (true) {
    const result = await client.query(
      `
      WITH target AS (
        SELECT id, name
        FROM users
        WHERE full_name IS NULL
          AND name IS NOT NULL
        ORDER BY id
        LIMIT $1
        FOR UPDATE SKIP LOCKED
      )
      UPDATE users AS u
      SET
        full_name = target.name,
        display_name = CASE
          WHEN length(target.name) > 40 THEN substring(target.name from 1 for 39) || '...'
          ELSE target.name
        END
      FROM target
      WHERE u.id = target.id
      RETURNING u.id
      `,
      [batchSize],
    );

    total += result.rowCount;
    console.log(`updated=${result.rowCount} total=${total}`);

    if (result.rowCount === 0) break;
    await sleep(sleepMs);
  }
} finally {
  await client.end();
}

Antes de produção, revise idempotência, falha parcial, execução paralela e controles operacionais. Um bom backfill tem batch size, pausa, logs, condição de parada e consulta que pode rodar novamente sem corromper dados.

CI e staging

CI deve aplicar todo o histórico de migrations em um banco temporário. No GitHub Actions, workflows ficam em .github/workflows e usam YAML.

name: migration-check

on:
  pull_request:
    paths:
      - "prisma/**"
      - "scripts/backfill-*.mjs"
      - ".github/workflows/migration-check.yml"

jobs:
  prisma-migrations:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: app
        ports:
          - "5432:5432"
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    env:
      DATABASE_URL: postgresql://postgres:postgres@localhost:5432/app?schema=public

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: npm
      - run: npm ci
      - run: npx prisma validate
      - run: npx prisma migrate deploy
      - run: npx prisma migrate status
      - name: Detect schema drift after migrations
        run: |
          npx prisma migrate diff \
            --exit-code \
            --from-config-datasource \
            --to-schema=prisma/schema.prisma

O exemplo usa argumentos do Prisma ORM v7 baseados em config datasource. Não copie flags antigas como --from-url ou --shadow-database-url sem conferir a referência atual.

Staging deve ser mais realista que CI: volume parecido de linhas, índices semelhantes, timeouts parecidos e o mesmo runner de migration. Peça para Claude Code criar uma checklist com lock waits, replication lag, latência, logs da aplicação e limites de aborto.

Contract e limites de rollback

Só faça contract depois que a nova aplicação estiver estável e o backfill estiver validado. Para NOT NULL, valide primeiro com uma constraint.

-- 20260602120000_contract_users_names.sql
-- Run only after the new application version has been stable in production.

ALTER TABLE users
  ADD CONSTRAINT users_full_name_present
  CHECK (full_name IS NOT NULL) NOT VALID;

ALTER TABLE users
  VALIDATE CONSTRAINT users_full_name_present;

ALTER TABLE users
  ALTER COLUMN full_name SET NOT NULL;

ALTER TABLE users
  DROP CONSTRAINT users_full_name_present;

-- Drop old columns in a later deploy, not in the same deploy that changes reads.
-- ALTER TABLE users DROP COLUMN name;

O maior engano é acreditar que uma down migration restaura tudo. Colunas apagadas, valores sobrescritos e conversões com perda não voltam apenas rodando SQL ao contrário. Muitas vezes o rollback realista é voltar a versão da aplicação ou desligar uma feature flag. No banco, pense em backup, point-in-time recovery ou correção para frente.

prisma migrate resolve --rolled-back também não desfaz uma migration bem-sucedida. Ele resolve o estado do histórico quando uma migration falha. Peça para Claude Code separar rollback da aplicação, forward fix no banco e restauração de dados.

Falhas comuns e processo de time

Falha um: tratar rename como drop and add e perder dados. O ORM nem sempre entende sua intenção, então leia o SQL gerado. Falha dois: misturar mudança de schema e reescrita massiva de dados. Falha três: confiar demais na shadow database, que não simula distribuição real de dados, bloat, filas de lock ou lag de réplica. Falha quatro: deixar verificação de backup apenas no verbal.

Coloque regras de DB em CLAUDE.md: não remover coluna de tabela quente no mesmo PR, não colocar backfill grande em schema migration, criar migrations Prisma com --create-only para revisão SQL e incluir links oficiais na review. Para estruturar isso, veja CLAUDE.md best practices.

Em SaaS com receita real, uma migration ruim pode parar cobrança, cadastro e suporte. Os produtos ClaudeCodeLab trazem prompts e checklists reutilizáveis, e o Claude Code training ajuda a aplicar o fluxo em um repositório real.

Na prática, o maior ganho veio de reduzir o que cada migration tenta fazer. Um PR só de expand, um job separado de backfill e um PR posterior de contract deixam a saída do Claude Code mais fácil de revisar. Antes de pedir SQL, peça condições de aborto e passos de recuperação.

Resumo

Claude Code não torna migrações de produção automaticamente seguras. Ele fica forte quando o time oferece um modelo seguro: expand/contract, deploy em etapas, backfills pequenos, CI, ensaio em staging, feature flags e limites claros de rollback.

Na próxima migration, peça primeiro uma revisão de riscos. Se o plano resistir, aí sim deixe Claude Code escrever SQL, Prisma migration, workflow de GitHub Actions e script de backfill.

#Claude Code #database migration #Prisma #PostgreSQL #CI/CD
Grátis

PDF grátis: cheatsheet do Claude Code

Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.

Cuidamos dos seus dados e não enviamos spam.

Masa

Sobre o autor

Masa

Engenheiro focado em workflows práticos com Claude Code.