rails-migration-safety
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRails Migration Safety
Rails 迁移安全
Use this skill when schema changes must be safe in real environments.
Core principle: Prefer phased rollouts over one-shot migrations on large or busy tables.
当需要在真实环境中安全执行模式变更时使用本指南。
核心原则: 针对大型或繁忙数据表,优先选择分阶段上线方案,而非一次性迁移。
Quick Reference
快速参考
| Operation | Safe Pattern |
|---|---|
| Add column | Nullable first, backfill later, enforce NOT NULL last |
| Add index (large table) | |
| Backfill data | Batch job, not inside migration transaction |
| Rename column | Add new, copy data, migrate callers, drop old |
| Add NOT NULL | After backfill confirms all rows have values |
| Add foreign key | After cleaning orphaned records |
| Remove column | Remove code references first, then drop column |
| 操作 | 安全模式 |
|---|---|
| 新增列 | 先设为可空,后续回填数据,最后强制非空约束 |
| 大表新增索引 | 使用 |
| 数据回填 | 使用批量任务,不要放在迁移事务中执行 |
| 重命名列 | 新增列→复制数据→迁移调用方→删除旧列 |
| 新增非空约束 | 确认所有行数据回填完成后再执行 |
| 新增外键 | 清理完孤立记录后再执行 |
| 删除列 | 先移除所有代码引用,再删除列 |
HARD-GATE
硬规则
DO NOT combine schema change and data backfill in one migration.
DO NOT add NOT NULL on a column that hasn't been fully backfilled.
DO NOT drop columns before all code references are removed.禁止在同一次迁移中同时执行模式变更和数据回填。
禁止对未完成全量数据回填的列添加非空约束。
禁止在所有代码引用移除前删除列。Review Order
评审顺序
- Identify the database and table-size risk.
- Separate schema changes from data backfills.
- Check lock behavior for indexes, constraints, defaults, and rewrites.
- Plan deployment order between app code and migration code.
- Plan rollback or forward-fix strategy.
- 识别数据库和表规模对应的风险。
- 拆分模式变更与数据回填操作。
- 检查索引、约束、默认值、表重写操作的锁行为。
- 规划应用代码与迁移代码的部署顺序。
- 制定回滚或正向修复策略。
Safe Patterns
安全模式
- Add nullable column first, backfill later, enforce last.
NOT NULL - Add indexes concurrently when supported.
- Backfill in batches outside a long transaction when volume is high.
- Deploy code that tolerates both old and new schemas during transitions.
- Use multi-step rollouts for renames, type changes, and unique constraints.
If the project uses , follow it. If it does not, apply the same safety rules manually.
strong_migrations- 先新增可空列,后续回填数据,最后强制约束。
NOT NULL - 支持并发索引的场景下优先使用并发索引。
- 数据量大时在长事务外批量执行回填。
- 过渡阶段部署同时兼容新旧模式的应用代码。
- 重命名、类型变更、唯一约束添加使用多阶段上线方案。
如果项目使用,遵循其规则;如果未使用,手动套用同等安全规则。
strong_migrationsExamples
示例
Risky (avoid):
ruby
add_column :orders, :status, :string, default: 'pending', null: false
Order.update_all("status = 'pending'")- Risk: Long lock; table rewrite if default is applied on large table.
Safe pattern:
ruby
undefined高风险(避免使用):
ruby
add_column :orders, :status, :string, default: 'pending', null: false
Order.update_all("status = 'pending'")- 风险: 长锁;如果是大表,设置默认值会触发表重写。
安全模式:
ruby
undefinedStep 1: add nullable column
步骤1:新增可空列
add_column :orders, :status, :string
add_column :orders, :status, :string
Step 2 (separate deploy): backfill in batches outside migration
步骤2(单独部署):在迁移外批量回填数据
Step 3 (after backfill): add constraint
步骤3(回填完成后):添加约束
change_column_null :orders, :status, false
change_column_default :orders, :status, from: nil, to: 'pending'
**Index on large tables:**
```rubychange_column_null :orders, :status, false
change_column_default :orders, :status, from: nil, to: 'pending'
**大表添加索引:**
```rubyPostgreSQL: concurrent index (no write lock)
PostgreSQL:并发索引(无写锁)
disable_ddl_transaction!
add_index :orders, :processed_at, algorithm: :concurrent
disable_ddl_transaction!
add_index :orders, :processed_at, algorithm: :concurrent
MySQL: online DDL
MySQL:在线DDL
add_index :orders, :processed_at, algorithm: :inplace
undefinedadd_index :orders, :processed_at, algorithm: :inplace
undefinedCommon Mistakes
常见错误
| Mistake | Reality |
|---|---|
| "Table is small, no need for phased migration" | Tables grow. Build the habit for all migrations. |
| Schema change + backfill in one migration | Long transaction, long lock. Always separate them. |
| Column rename with immediate app cutover | App will crash during deploy. Use add-copy-migrate-drop. |
| Exclusive lock on large PostgreSQL tables blocks writes. |
| Adding NOT NULL before backfill completes | Migration fails or locks table waiting for backfill. |
| Removing column before removing code references | App crashes when accessing the missing column. |
| 错误认知 | 实际情况 |
|---|---|
| "表很小,不需要分阶段迁移" | 表规模会增长,对所有迁移养成规范习惯 |
| 同一次迁移同时执行模式变更和数据回填 | 长事务、长锁,必须拆分两类操作 |
| 重命名列同时直接切换应用代码 | 部署过程中应用会崩溃,使用「新增-复制-迁移-删除」流程 |
添加索引不带 | 大型PostgreSQL表上的排他锁会阻塞写操作 |
| 数据回填完成前添加非空约束 | 迁移失败,或等待回填过程中锁表 |
| 移除代码引用前删除列 | 应用访问缺失列时崩溃 |
Red Flags
风险预警
- Schema change and data backfill combined in one long migration
- Column rename with app code assuming immediate cutover
- Large-table default, rewrite, or NOT NULL change without a phased plan
- Foreign key or unique constraint added before cleaning existing data
- Destructive remove or drop in the same deploy as the replacement path
- No rollback or forward-fix strategy documented
- 同一次长迁移中同时包含模式变更和数据回填
- 重命名列时应用代码假设会立即生效
- 无分阶段方案直接对大表设置默认值、重写表或修改非空约束
- 清理现有数据前添加外键或唯一约束
- 破坏性删除操作与替换路径在同一次部署中执行
- 无回滚或正向修复策略文档
Output Style
输出规范
List risks first.
For each risk include:
- Migration step
- Likely failure mode or lock risk
- Safer rollout
- Rollback or forward-fix note
For complex type changes, multi-step unique constraints, foreign key validation deferral, safe column removal, and multi-database migrations see PATTERNS.md.
优先列出风险点。
每个风险点需包含:
- 迁移步骤
- 可能的失败模式或锁风险
- 更安全的上线方案
- 回滚或正向修复注意事项
复杂类型变更、多步骤唯一约束添加、外键校验延迟、安全列删除、多数据库迁移等场景参考PATTERNS.md。
Integration
集成使用
| Skill | When to chain |
|---|---|
| rails-code-review | When reviewing PRs that include migrations |
| rails-background-jobs | For backfill jobs that run after schema change |
| rails-security-review | When migrations expose or move sensitive data |
| 技能 | 触发场景 |
|---|---|
| rails-code-review | 评审包含迁移的PR时 |
| rails-background-jobs | 处理模式变更后执行的回填任务时 |
| rails-security-review | 迁移涉及敏感数据暴露或迁移时 |