Railsのマイグレーションに対するいらだち

2025/7/24

ローカルでのスキーマの試行錯誤に弱い

このフラストレーションが一番大きい。

特に新しい機能を開発しているような場合、コードを書きながらスキーマを微調整していくことがある。マイグレーションファイルを編集しながら db:migrate:redo を繰り返していくことになるのだが、これが全然ロバストでなく、行き詰まりやすすぎる。

たとえば、こんなマイグレーションを書いて、 db:migrate する。

add_column :recipes, :calories

直後、ああ calories ではなく energy のほうが良かったな、と思う。理由はなんでもいい。とにかくカラム名を変えたい。マイグレーションを編集して db:migrate:redo しよう。

-add_column :recipes, :calories
+add_column :recipes, :energy

さてどうなるだろうか。 db:migrate:redo は down → up と等価なので、まずは energy カラムの削除が試みられ、当然 Unknown column 'column_bar' エラーが起こる。南無。

いったん calories に戻してdownしたのち、再び energy に変更してupする手順を踏む必要がある。もしくは手動でALTER TABLEして直すか。その場合は db:schema:dump して schema.rb を更新するのをお忘れなく。


もっと悪いことになる例。こんなマイグレーションを書く。2行目の remove_column だけが失敗したとする。

add_column :table, :column_foo
remove_column :table, :colmun_bar # typo があり、存在しないカラムになる

このとき、マイグレーション全体としては成功していないので、未実行状態になる。 remove_column を修正して再実行したときは add_column から走り直すが、 column_foo の作成自体は成功しているので、今度は Duplicate column name 'column_foo' エラーで失敗になる。南無。

こうなるとupもdownもできない。手動でALTER TABLEで column_foo を消して整合性を取るか、あるいは一時的に add_column の行を消してmigrationを成功させ、その後行を戻す対処しか思いついていない。

これを確実に回避するためには、1 migrationごとに流すDDLは一つだけにするしかない。常に change_table(bulk: true) だけを使えばいい話ではある、のだが……。1つのmigrationファイルで実行されるDDLの数、常に意識していますか?

複数ブランチで開発を同時進行していると、schema.rbの生成結果が混ざる

ブランチAで以下のマイグレーションを流して

add_column :users, :column_a, :integer

ブランチBに切り替え、以下のマイグレーションを流すと

add_column :users, :column_b, :integer

schema.rbはこうなる。schema.rbは常に db:schema:dump から生成されるので、現在のブランチにはないマイグレーションの結果であれ、データベースに存在するカラムはすべて記述されてしまう。

 create_table "users" do |t|
   t.integer "id"
+  t.integer :column_a # このブランチには存在しない migration の結果!
+  t.integer :column_b
 end

schema.rb は常に git add -p でチェックしているけれど、これが幸せかというと、どうだろう。

(ぼやいていたら、この問題に自動で対処できる widefix/actual_db_schema というツールを教えてもらいました。便利!)

git revertによるロールバックが困難

こちらは本番環境での話。

マイグレーションした結果、何かマズいことが起こり git revert をしたとき、migrationファイルは消え去る。消え去ったmigrationファイルに記述されていたALTER TABLEは取り消されるわけでもなく、ただファイルが消える。こうなると db:rollback (down) もできない。

障害が起きているようなシーンでは、行動の択を絞ることが重要。正確なタイミングで db:rollback することを意識することはけっこう難しい。

なお、本番環境でなくてもgit revertされたケースが積み重なってくると、schema.rbの正しさも信用できなくなってくる。"正しい" スキーマが何か分からなくなるのは困る。

比較的ささいな話

ではどうするのか

Ridgepoleとかsqldefみたいな、宣言的にスキーマを管理できるやつと併用するのがいいと思う。