AI 生成的迁移 SQL 语法正确不代表安全——破坏性操作、约束失败、长事务锁、数据假设错误、版本兼容问题可能导致生产事故,且无回滚兜底。
AI 能够写出一条运行干净的迁移语句,却仍然对生产系统造成损害。在将 schema 工作交给任何模型之前,这句话值得反复咀嚼,因为它指向的是真正的失效模式:语法正确的 SQL 并不等于对目标表安全的 SQL。
应用代码有内置的撤销按钮。一次糟糕的发布可以回退到上一个版本,事故基本就算解决了。数据库迁移没有这个选项。它编辑的是已经包含生产数据的持久状态,在实时流量下运行,受锁、权限和回滚约束的制约——这些都是普通部署不需要考虑的。一次中途失败的迁移可以阻塞流量、损坏记录,或让 schema 与应用失去同步,其中有些损害是不可逆的,无论回滚脚本听起来多么可靠。AI 生成迁移的风险通常集中在几类模式上:破坏性操作、约束失败、长时间锁、对现有数据的错误假设,以及新旧应用版本同时运行时的兼容性问题。
这不是对 AI 输出的一般性假设担忧,而是可量化的,数据值得直接引用。Sonar 2026 年《代码开发者状态调查》发现,96% 的开发者并不完全信任 AI 生成的代码在功能上正确,但只有 48% 表示他们在提交前总是审查 AI 辅助代码。这是一个普通代码的验证缺口。Stack Overflow 最新的开发者调查在更大范围内发现了类似的分歧:AI 工具采用率攀升至 84%,而对其输出准确性的信任度降至 29%,低于一年前的 40%。将同样粗放的验证级别扩展到直接针对生产数据库的变更时,这个差距就变得不可接受了。迁移是 AI 输出中跳过审查代价最高的类别,这正是为什么它们需要独立、更严格的工作流,而不是普通 pull request 的默认审查级别。
一个迁移 prompt 需要携带的信息远不止"添加一列"。在生成 SQL 之前,值得包含:数据库引擎和版本、当前表定义、近似的行数、现有索引和外键、读/写流量模式和峰值窗口、部署期间哪些应用版本会上线、已知的数据质量问题、停机容忍度,以及请求的变更是添加型、转换型还是破坏型。具体来说,像这样:
我们使用 PostgreSQL 16。orders 表有 8000 万行并持续接收写入。部署期间应用必须保持可用。添加一个可空的 refunded_at 时间戳,回填现有已退款订单,并避免表重写或长时间阻塞锁。在编写迁移之前,提供迁移计划、SQL、验证查询、回滚限制和上线顺序。
一个获得这些上下文的模型会转而使用 CREATE INDEX CONCURRENTLY 和分批回填,而不是采用在运行全程锁表的操作。prompt 质量上的这一个差异,往往就是能在规模下安全运行的迁移与只在小量行数下测试过的迁移之间的差距。
并非每个 schema 变更都携带相同的风险,一视同仁地对待它们要么让安全变更变慢,要么让危险变更审查不足。低风险变更包括:添加一个未使用的可空列、添加一个新表、添加一个非阻塞或并发索引、添加一个新的枚举值(这里的行为取决于引擎和应用的读取方式)。高风险变更包括:向已有数据的表添加 NOT NULL 约束、添加可能有重复数据的唯一约束、更改列类型、重命名列或表、向外键添加未经验证的数据,以及大型数据回填。最高风险变更包括任何破坏性操作、全表重写、在峰值流量期间安排的变更、任何涉及账单、身份、权限或个人数据的变更,以及需要多个服务同时部署的变更。操作越具破坏性且越难逆转,对 AI 生成版本的态度就应该越谨慎:干运行、分阶段上线和明确的人工签核应该随风险等级升高而加强,而不是一刀切地应用。
这是安全迁移工作流的技术核心,也是最值得作为默认规范而非特殊案例来内化的部分。将数据库 schema 变更和应用代码在单一步骤中完成会移除干净回退的能力,因此将变更拆分为四个阶段。扩展(Expand)添加新的列、表、索引或结构,但不触碰旧的。回填(Backfill)逐步、分批、用检查点填充新结构,而不是在一个长时间事务内完成。切换(Switch)将应用切换为从新结构读写。收缩(Contract)仅在代码库中没有任何内容再依赖旧结构后才移除它。
重命名一列是看清这一重要性最清晰的方式。不安全的单步版本:
ALTER TABLE users RENAME COLUMN full_name TO display_name;
更安全的序列:添加 display_name,同时向 full_name 和 display_name 写入,分批回填现有行,将读取迁移到 display_name,监控缺失或不一致的值,然后只在后续版本中删除 full_name。这些额外步骤的存在是为了让新旧版本的应用在滚动部署期间能够并行运行,而不会因为底层 schema 而出现任何一方崩溃。
这里最重要的控制是程序性的而非技术性的。让 AI 生成并解释迁移,然后在干净的数据库快照上运行,接着在脱敏的生产类似数据上运行,然后在 CI 中执行干运行,审查生成的 SQL 及其查询计划,并在到达 staging 或生产环境之前要求明确批准。生成迁移的 agent 不应该持有不受限制的生产凭据,而迁移本身、其审查者、其批准及其执行结果都值得作为常规记录下来。在实践中:AI 可以提议一次迁移,CI 可以测试它,但人类批准生产变更,每一次都是,无论变更看起来多么常规。
这种将提议与执行分离的模式并非迁移独有,值得注意到更广泛的 AI 开发工具生态中有多少已经独立地收敛到这一模式。架构优先的平台如 8080.ai 在任何代码编写之前先生成系统需求文档和架构计划;围绕 AGENTS.md 等文件构建的 spec 驱动工作流通过在变更应用之前给审查者一个具体的人工制品来检查,做法类似;像 Replit 和 Lovable 这样的构建工具越来越多地将生成的变更放在明确的批准步骤之后,而不是自动应用。它们都不能单独完全弥合上面描述的信任鸿沟——审查仍然必须真正发生——但它们反映了这一工作流所基于的相同底层原则:AI 输出是草稿,直到人类或经过测试的过程说不是。
针对十行干净数据成功的迁移可能以仅在真实规模下才会出现的方式失败。针对脱敏的生产快照测试——保留 null、重复、格式错误的数据、旧记录和不一致格式,而不是干净的合成数据——可以暴露演示数据集隐藏的问题。具体值得衡量的:代表性表大小下的执行时间和锁行为、运行期间的内存和 CPU 使用、变更前后的查询计划、分阶段上线期间的应用行为,以及新约束在开启之前是否真正对现有数据成立。
大型回填是那些在纸面上看起来简单的迁移变成运维事故的地方。用稳定顺序和检查点分批处理行、让操作具有幂等性、避免对大表执行单个大事务、在负载上升时减慢或暂停回填、记录进度以使重试不会重复或部分发布数据——这些细节决定了中断的回填是轻微不便还是数小时的事故。具体来说,幂等迁移是可以安全地再次运行而不会破坏或重复任何已写入内容的迁移——这就是稳定标识符、检查点和事务性写入真正的作用。
对任何声称可逆的 AI 生成迁移保持怀疑态度是值得的。添加型的 schema 变更通常比清理移除更容易保留在原地。破坏性变更可能需要恢复而不是逆转。数据转换通常不是完全可逆的,而"向下迁移"文件的存在并不能保证运行它真的能恢复原始状态。更可靠的方法是在执行前决定:失败迁移的响应是回退、前向修复、从备份恢复,还是停止调查——然后在 staging 中实际测试该恢复路径,包括验证备份和时间点恢复,然后才能让变更接近生产环境。
成功的退出码是观察窗口的开始,而不是结束。迁移持续时间、锁等待、活跃连接、查询延迟、错误率、复制延迟、连接池饱和度、回填进度、约束违反,以及与变更字段相关的应用错误——这些都值得在执行期间和之后监控。对于 PostgreSQL,标准指导在这里仍然很适用:批量处理大更新、在有意义的地方使用事务、在引擎支持的情况下并发创建索引,而不是默认接受阻塞操作。
对于 AI 生成的迁移,真正的问题从来不是 SQL 是否语法正确,而是变更在生产条件下运行后是否保留了数据、可用性、应用兼容性以及真正的恢复路径。这一点无论迁移来自哪里都成立:基于聊天的助手、IDE 中的自动补全建议,还是更结构化、架构优先的平台(如 8080.ai),在任何 SQL 存在之前先生成计划。上述工作流在两种情况下都是一样的——工具变了,审查没有。