Use Cases (更新: 2026/6/2)

Claude Code 数据库迁移实战:生产团队安全指南

用 Claude Code 安全处理生产数据库迁移:expand/contract、Prisma、CI、回填与回滚边界。

Claude Code 数据库迁移实战:生产团队安全指南

生产数据库迁移不能只对 Claude Code 说一句“帮我改 schema”。真正危险的部分不是 SQL 语法,而是顺序:应用发布、数据库锁、备份、数据回填、功能开关、CI 检查和回滚策略会互相影响。Claude Code 很有用,但它应该先当迁移审查员,再当代码生成助手。

这篇文章面向使用 PostgreSQL 和 Prisma Migrate 的生产团队。即使你的团队使用纯 SQL migration,也可以采用同样的思路:先让数据库同时兼容旧代码和新代码,再分批移动数据,验证通过后,最后再删除旧路径。

审查时请打开官方文档。Claude Code 的基础参考见 Anthropic Claude Code 文档,PostgreSQL 锁与 DDL 参考 explicit lockingALTER TABLE,Prisma 生产迁移参考 development and productionCLI reference,CI 配置参考 GitHub Actions workflow syntax

迁移模型

生产迁移的核心是 expand/contract。expand 指“扩展”,让数据库先接受旧应用和新应用都会使用的数据结构。contract 指“收缩”,在新版本稳定、数据回填完成之后,再删除旧列、旧读取逻辑或旧约束。用更直白的话说,就是先加新路,再切流量,最后清旧路。

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

最常见的错误,是让 Claude Code 在一个 migration 里完成加列、复制数据、设置 NOT NULL、删除旧列。这个方案在本地小数据库可能成功,但在几百万行的 usersorders 表上,可能造成长时间锁等待、写入阻塞、超时,甚至不可恢复的数据丢失。

先统一几个术语。锁是数据库为了避免并发操作破坏同一张表而设置的等待机制。回填是把已有行的数据填入新列的批处理工作。shadow database 是 Prisma 在开发阶段用来重放迁移历史并检测 drift 的临时数据库,它不是生产环境的自动保险。

先让 Claude Code 做审查

第一个 prompt 不要让 Claude Code 直接改文件,而是让它审查迁移计划。把表规模、部署方式、ORM、回滚要求都写清楚。

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.

最后一句很重要。Claude Code 的速度很快,数据库迁移却需要刻意暂停。如果它给出的方案把危险步骤混在一起,再追问一次:

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.

这样使用时,Claude Code 的价值会更稳定。人来判断风险是否可接受,Claude Code 负责把相关文件、命令、SQL、CI 检查和监控项整理出来,减少遗漏。

Expand 阶段的 SQL

假设我们要把 users.name 拆成 full_namedisplay_name。expand migration 只添加 nullable 新列和索引,不回填,不设置 NOT NULL,也不删除旧列。

-- 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);

PostgreSQL 的 ALTER TABLE 文档说明,不同子命令需要的锁级别不同。如果文档没有说明更轻的锁,就要按保守方式评估。让 Claude Code 审查时,不要让它凭经验猜锁级别,而要让它把判断连接到官方文档。

如果使用 Prisma,先生成 migration,不要直接应用到数据库。

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

Prisma 官方生产指南说明,测试和生产环境使用 npx prisma migrate deploy 应用迁移。它会应用待执行 migration,但不会检测 drift,也不依赖 shadow database。因此,deploy 命令成功不等于生产风险已经被完整验证。

npx prisma migrate deploy

应用层切换

expand 之后,应用必须同时兼容旧列和新列。读取逻辑需要 fallback,写入逻辑在过渡期应同时写旧字段和新字段。

// 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,
  };
}

功能开关可以把数据库结构变更和用户可见行为分开。数据库先支持两条路径,回填验证通过后再打开新读取路径。如果新路径出错,可以先关闭开关,而不是立刻回滚数据库。功能开关的实现可以参考站内的 Claude Code feature flags 指南

这个模式至少适用于三类场景。第一类是用户资料字段的重命名、拆分或规范化。第二类是订单金额、账单余额、搜索标签等由旧数据计算出的新列。第三类是在高流量表上补充索引或外键。共同点都是先兼容,再回填,再验证,最后收缩。

分批回填数据

不要用一个巨大的 UPDATE 完成全部回填。它可能放大锁时间、WAL 写入、复制延迟和运维风险。让 Claude Code 生成可重启的小批量脚本更稳妥。

// 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();
}

上线前要审查四点:脚本是否幂等,失败后能否继续,多个实例并行时是否安全,是否有停止和观察手段。生产回填需要 batch size、批次间隔、日志、停止条件和可重复执行的查询。

CI 与 staging 检查

CI 应该在临时数据库上从零应用 migration history。GitHub Actions 的 workflow 放在 .github/workflows 目录,并用 YAML 编写,非常适合做 PR 级别检查。

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

这个例子使用 Prisma ORM v7 之后的 config datasource 参数。不要直接复制旧文章里的 --from-url--shadow-database-url,先查当前 CLI reference。

staging 要比 CI 更接近真实生产:相似的数据量、索引、超时设置和 migration runner。让 Claude Code 输出演练清单,包含锁等待、复制延迟、查询延迟、错误日志和停止阈值。

Contract 与回滚边界

只有在新应用稳定、回填验证完成之后,才进入 contract。添加 NOT NULL 前,先用验证型约束检查数据。

-- 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;

最大的回滚误区是相信 down migration 一定能还原数据。被删除的列、被覆盖的值、精度丢失的类型转换,不能简单靠反向 SQL 恢复。很多时候可回滚的是应用版本或功能开关,数据库侧则需要备份、point-in-time recovery 或向前修复。

Prisma 的 migrate resolve --rolled-back 也不是撤销已成功 migration 的魔法命令。它用于处理失败 migration 的历史状态。让 Claude Code 写回滚计划时,要明确区分“应用可回滚”“数据库只能向前修复”“必须恢复数据”。

常见失败与团队流程

常见失败有四个。第一,把 rename 当成 drop and add,导致数据丢失。第二,把 schema 变更和大规模数据更新放在同一个 migration 中,失败时难以定位。第三,过度相信 shadow database,它不能模拟生产数据分布、表膨胀、锁队列和复制延迟。第四,备份检查只停留在口头确认,没有明确恢复负责人和恢复时间。

团队使用 Claude Code 时,把数据库规则写进 CLAUDE.md:生产热表不在同一 PR 删除列,大回填不放进 schema migration,Prisma migration 使用 --create-only 后人工审 SQL,审查意见必须附官方文档链接。写法可参考 CLAUDE.md best practices

对于收入敏感的 SaaS,迁移失败会影响计费、注册和客服流程。ClaudeCodeLab 的 products 提供可复用 prompt 和清单,Claude Code training 可以帮助团队把这套流程落到真实仓库。

实际试用这个流程后,最明显的收益不是 SQL 变得更复杂,而是每个 PR 的责任变小了。expand-only PR、独立 backfill job、后续 contract PR 让 Claude Code 的输出更容易审查,也让人类的上线判断更清楚。让 Claude Code 先写停止条件和恢复步骤,再写 SQL,是减少生产事故的关键习惯。

总结

Claude Code 不会自动让生产数据库迁移变安全。它真正有价值的前提,是团队先给出安全模型:expand/contract、分阶段应用发布、小批量回填、CI 检查、staging 演练、功能开关和明确的回滚边界。

下一个 migration 不要先让 Claude Code 写 SQL。先让它审查风险。如果计划能通过审查,再让它生成 SQL、Prisma migration、GitHub Actions 检查和回填脚本。

#Claude Code #数据库迁移 #Prisma #PostgreSQL #CI/CD
免费

免费 PDF: Claude Code 速查表

输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。

我们会妥善保护你的信息,不发送垃圾邮件。

把 Claude Code 变成真正能带来结果的工作流

先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。

Masa

关于作者

Masa

专注 Claude Code 实务流程、团队导入和内容转化的工程师。