rails-migration-safety

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Rails 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

快速参考

OperationSafe Pattern
Add columnNullable first, backfill later, enforce NOT NULL last
Add index (large table)
algorithm: :concurrent
(PG) /
:inplace
(MySQL)
Backfill dataBatch job, not inside migration transaction
Rename columnAdd new, copy data, migrate callers, drop old
Add NOT NULLAfter backfill confirms all rows have values
Add foreign keyAfter cleaning orphaned records
Remove columnRemove code references first, then drop column
操作安全模式
新增列先设为可空,后续回填数据,最后强制非空约束
大表新增索引使用
algorithm: :concurrent
(PG)/
:inplace
(MySQL)参数
数据回填使用批量任务,不要放在迁移事务中执行
重命名列新增列→复制数据→迁移调用方→删除旧列
新增非空约束确认所有行数据回填完成后再执行
新增外键清理完孤立记录后再执行
删除列先移除所有代码引用,再删除列

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

评审顺序

  1. Identify the database and table-size risk.
  2. Separate schema changes from data backfills.
  3. Check lock behavior for indexes, constraints, defaults, and rewrites.
  4. Plan deployment order between app code and migration code.
  5. Plan rollback or forward-fix strategy.
  1. 识别数据库和表规模对应的风险。
  2. 拆分模式变更与数据回填操作。
  3. 检查索引、约束、默认值、表重写操作的锁行为。
  4. 规划应用代码与迁移代码的部署顺序。
  5. 制定回滚或正向修复策略。

Safe Patterns

安全模式

  • Add nullable column first, backfill later, enforce
    NOT NULL
    last.
  • 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
strong_migrations
, follow it. If it does not, apply the same safety rules manually.
  • 先新增可空列,后续回填数据,最后强制
    NOT NULL
    约束。
  • 支持并发索引的场景下优先使用并发索引。
  • 数据量大时在长事务外批量执行回填。
  • 过渡阶段部署同时兼容新旧模式的应用代码。
  • 重命名、类型变更、唯一约束添加使用多阶段上线方案。
如果项目使用
strong_migrations
,遵循其规则;如果未使用,手动套用同等安全规则。

Examples

示例

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
undefined

Step 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:**

```ruby
change_column_null :orders, :status, false change_column_default :orders, :status, from: nil, to: 'pending'

**大表添加索引:**

```ruby

PostgreSQL: 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
undefined
add_index :orders, :processed_at, algorithm: :inplace
undefined

Common Mistakes

常见错误

MistakeReality
"Table is small, no need for phased migration"Tables grow. Build the habit for all migrations.
Schema change + backfill in one migrationLong transaction, long lock. Always separate them.
Column rename with immediate app cutoverApp will crash during deploy. Use add-copy-migrate-drop.
add_index
without
algorithm: :concurrent
Exclusive lock on large PostgreSQL tables blocks writes.
Adding NOT NULL before backfill completesMigration fails or locks table waiting for backfill.
Removing column before removing code referencesApp crashes when accessing the missing column.
错误认知实际情况
"表很小,不需要分阶段迁移"表规模会增长,对所有迁移养成规范习惯
同一次迁移同时执行模式变更和数据回填长事务、长锁,必须拆分两类操作
重命名列同时直接切换应用代码部署过程中应用会崩溃,使用「新增-复制-迁移-删除」流程
添加索引不带
algorithm: :concurrent
参数
大型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

集成使用

SkillWhen to chain
rails-code-reviewWhen reviewing PRs that include migrations
rails-background-jobsFor backfill jobs that run after schema change
rails-security-reviewWhen migrations expose or move sensitive data
技能触发场景
rails-code-review评审包含迁移的PR时
rails-background-jobs处理模式变更后执行的回填任务时
rails-security-review迁移涉及敏感数据暴露或迁移时