はじめに
Railsはとても便利なwebアプリケーションフレームワークです。
gitも非常に便利なソースコード管理ツールです。
この2つのツールがあれば、初心者でも効率良くwebアプリケーションを開発できます。
ただし、学習用の小さなアプリケーションを一人で開発しているときはブランチはひとつかふたつで十分ですが、業務などで大きなアプリケーションを複数人で開発する場合は、複数のブランチで並行して開発が進める必要が出てきます。
こういうケースではデータベースマイグレーションを正しく理解し、正しく扱わないと、開発環境やソースコードが混沌としてきます。
Rails初心者の多くはこうした知識を持たないまま、チーム開発に参加しがちです。
この記事では「学習用の小さなRailsアプリケーション」を卒業し、「チームで開発する大きなRailsアプリケーション」に足を踏み入れる前に知っておくべき、データベースマイグレーションに関する知識を説明します。
TL;DR(長すぎるので最初にまとめ)
ちょっと長いので先に要点をまとめておきます。
- gitでブランチを切り替えても、gitはデータベーススキーマの状態までは切り替えてくれない
- ゆえにブランチを切り替えたタイミングで、「そのブランチでは本来存在するべきではないテーブルやカラム」がデータベーススキーマに残った状態になることがある
- その状態で
rails db:migrate
を実行すると、「そのブランチでは本来存在するべきではないテーブルやカラム」がひょっこりdb/schema.rb
に顔を出す -
db/schema.rb
がdiffに含まれているときは、コミットする前にソースコードとdb/schema.rb
がきれいに同期しているか入念にチェックすべき - コードとDBの不整合を避けるという意味で、自分がメインで開発しているブランチ以外では、なるべく
rails db:migrate
を実行しない方よい
上のまとめを読んで「いったい何を言ってるの?」と思った人は、このあとの説明をじっくり読んでみてください。
【重要】大前提として知っておくべきこと
Railsアプリケーションを開発するときは大半の人がgitを使ってコードの変更を管理すると思います。
gitにはブランチという機能があり、ブランチを切り替えるとコードの内容をがらっと切り替えることができます。
たとえば、あなたが「ユーザー情報として電話番号を登録できるようにする」というタスクに取り組んでいたとします。このときに作業していたブランチの名前を"add-phone-number"としましょう。
もし開発の途中で
git checkout main
を実行してmainブランチに切り替えれば、あなたが"add-phone-number"ブランチで変更したコードはきれいさっぱりなくなって、mainブランチのソースコードに切り替わります。
しかし、ここで重要なことを意識しなければなりません。それは、 データベーススキーマ(データベース内のテーブルやカラムの定義)はブランチを切り替えてもまったく変わらない 、ということです。
もしあなたが"add-phone-number"ブランチでusers
テーブルにphone_number
というカラムを追加していたら、mainブランチに切り替えてもDB上にはphone_number
が残ったままになっています。
つまりこれは、db/schema.rb
の内容と開発環境で使っているDBのデータベーススキーマが不整合を起こしていることを意味しています。
db/schema.rb
の内容を確認するとこんなふうにusers
テーブルにはphone_number
カラムは追加されていません。
# mainブランチのdb/schema.rbにはphone_numberカラムは存在しない
create_table "users", force: :cascade do |t|
t.string "email"
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
rails db:migrateのもうひとつの仕事
ところで、Railsを使っている人ならマイグレーションを実行するのにrails db:migrate
コマンドを実行することをご存じだと思います。
rails db:migrate
コマンドを実行すると、新しく追加されたマイグレーションファイルの記述内容に従ってデータベースに変更が適用されます。
しかし、rails db:migrate
の仕事はそれだけではありません。 rails db:migrate
を実行すると、コマンドを実行した直後のデータベーススキーマの状態をdb/schema.rb
に反映するのです。
ここで先ほどの例に戻りましょう。mainブランチでrails db:migrate
を実行すると何が起きるでしょうか?やってみましょう。
$ rails db:migrate
$
はい、何も起きませんね。mainブランチでは新しく作られたマイグレーションファイルはないので、新しくテーブルが追加されたりカラムが追加されたりすることはありません。
しかし、git diff
コマンドを実行すると・・・?
diff --git a/db/schema.rb b/db/schema.rb
index 68ca253..a24c761 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,12 +10,13 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.0].define(version: 2023_11_05_053248) do
+ActiveRecord::Schema[7.0].define(version: 2023_11_05_053414) do
create_table "users", force: :cascade do |t|
t.string "email"
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.string "phone_number"
end
end
なぜかdb/schema.rb
が更新されています。
t.string "phone_number"
は"add-phone-number"ブランチで追加したカラムです。
しかし、これはまだ開発中の機能で使うカラムなので、mainブランチとは無関係です。
もちろん、phone_number
カラムを追加するマイグレーションファイルもmainブランチには存在しません。
これは先ほど説明した「データベーススキーマの状態をdb/schema.rb
に反映する」というrails db:migrate
コマンドの副作用です。
つまり、ここではまだmainブランチでは登場すべきではないカラムが一足先にdb/schema.rb
に登場したことになり、これはこれでmainブランチのソースコードと矛盾を引き起こしています。
db/schema.rbは常にソースコードときれいに同期させるべし
このままぼーっとしてgit commit
&git push
してしまうと、他の開発メンバーが「このusers
テーブルにあるphone_number
カラムって何なん?」と首をかしげることになります。
ここで取るべき行動はmainブランチのコードとしてあるべき姿にdb/schema.rb
を戻すことです。
具体的にはmainブランチはまだphone_number
カラムは登場させるべきではないので、db/schema.rb
は元の状態に戻しておきましょう。
# 予期せずdb/schema.rbに適用されてしまった変更点をロールバックする
$ git checkout db/schema.rb
これでdb/schema.rb
からphone_number
カラムがなくなりました。
ActiveRecord::Schema[7.0].define(version: 2023_11_05_053248) do
create_table "users", force: :cascade do |t|
t.string "email"
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
ここまでの説明でわかること
ここまで以下のような内容を説明しました。
- gitでブランチを切り替えても、gitはデータベーススキーマの状態までは切り替えてくれない
- ゆえにブランチを切り替えたタイミングで、「そのブランチでは本来存在するべきではないテーブルやカラム」がデータベーススキーマに残った状態になることがある
- その状態で
rails db:migrate
を実行すると、「そのブランチでは本来存在するべきではないテーブルやカラム」がひょっこりdb/schema.rb
に顔を出す
このようにrails db:migrate
をすると、状況によっては「そのブランチでは本来あるべきではない状態」をdb/schema.rb
に反映する可能性があります。
よって、db/schema.rb
がdiffに含まれているときは「本当にこのままコミットしていいか?別ブランチで追加したテーブルやカラムが含まれていないか?」という点をじっくり確認する必要があります。
ソースコードとデータベーススキーマに矛盾が発生したときに取るべき戦略
上で説明した例のように、扱うブランチがmainブランチとフィーチャブランチ(たとえば"add-phone-number"ブランチ)の2つだけ、というときはまだマシです。
ローカル環境にフィーチャブランチが複数存在し、それぞれを切り替える必要があるときはかなりややこしいことになります。
なぜなら、ブランチAとブランチBのそれぞれでマイグレーションを実行したりすると、
「ブランチBで追加した不要なテーブルやカラムを抱えたブランチA」
や
「ブランチAで追加した不要なテーブルやカラムを抱えたブランチB」
という状況を生み出してしまうからです。
こういう状況になったときに取るべき戦略をいくつか説明します。
戦略1: ブランチを切り替えるタイミングで変更をロールバックする
一つ目は「ブランチを切り替えるタイミングで、データベースに加えた変更をロールバックする」という戦略です。
つまり、rails db:rollback
を実行してデータベーススキーマを「その機能の開発に着手する直前」に戻してあげる、という方法です。こうすれば、ブランチBからブランチAに切り替えても「ブランチBで追加した不要なテーブルやカラムを抱えたブランチA」ではなく、「きれいなブランチA」になります。
ただし、ブランチBでrails db:rollback
を実行すると、db/schema.rb
からもテーブルやカラムの情報が消えてdiffが発生します。diffを抱えたままブランチを切り替えるわけにはいかないのでgit checkout db/schema.rb
でdb/schema.rb
の変更はロールバックしてからブランチを切り替える必要があります。
また、ブランチAからブランチBに戻ってきたタイミングで再度rails db:migrate
を実行してデータベーススキーマをブランチB向けに戻す必要もあります。
もちろん、ブランチAに切り替えるタイミングでrails db:rollback
を実行するということは、ブランチBの開発中に作成したデータも失われる、ということです。
この戦略はソースコードとデータベーススキーマに矛盾を発生させないという点では理想的ですが、実際には手間やデメリットが大きいので、筆者は滅多に採用しません。
戦略2: ブランチごとにデータベースを別々に用意する
二つ目は「ブランチごとにデータベースを別々に用意する」という戦略です。
たとえば以下のようにデータベースをローカル環境に複数こしらえて、ブランチごとに切り替える、というイメージです。
development:
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
# ブランチAで開発するときはこっち
database: awesome_app_development
# ブランチBで開発するときはこっち
# database: awesome_app_development_for_b
しかし、これはこれで運用や開発フローが複雑になりがちなので、筆者は滅多に採用しません。
この戦略を採用するのは、ブランチBでドラスティックなスキーマ変更が入る(大量にテーブルやカラムが追加されたり、既存のテーブルやカラムが大量に削除されたりする)ときぐらいです。
戦略3: rails db:resetでまるっとデータベースを作り直す
三つ目は「ブランチを切り替えるタイミングでrails db:reset
でまるっとデータベースをゼロから作り直す」という戦略です。
つまり、ブランチBからブランチAに切り替えたらデータベースを再作成、ブランチBに戻ったらまたデータベースを再作成、というようにするわけです。
たしかにこうすれば「ブランチBで追加した不要なテーブルやカラムを抱えたブランチA」みたいな状況は回避できます。
しかし、データベースを再作成すると開発用のデータが全部失われてしまいます。
業務で開発しているRailsアプリケーションでは「秘伝のタレ」のようなテストデータがローカルDBに登録されていることが多いので、rails db:reset
は恐ろしくて実行できません。
この戦略が有効なのは、テストデータが失われても痛くもかゆくもない「学習用に作成しているRailsアプリケーション」ぐらいじゃないかと思います。
⭐️ 戦略4: 何もしない(矛盾を許容し、気を付けながら開発する)→ これがベスト!?
四つ目は「特に何もしない」という戦略です。
つまり、「ブランチBで追加した不要なテーブルやカラムを抱えたブランチA」や「ブランチAで追加した不要なテーブルやカラムを抱えたブランチB」という状況を許容する、ということです。
新しいテーブルが追加されたり、新しいカラムが追加されたりした場合、多くの場合はブランチを切り替えてもRailsアプリの動作には影響を与えません(既存のテーブルやカラムが削除されたりした場合は話が別ですが)。
「今作業しているのはブランチAだが、データベースにはブランチBで追加したテーブルやカラムが含まれている」といったことを頭の片隅に置いておけば、何か問題が起きたときも落ち着いて対処できます。
実際、矛盾した状態で発生する問題の大半は予期しないdb/schema.rb
の更新ぐらいです。
なので、db/schema.rb
に変なdiffが混入しないように気を付ける、というぐらいの対応でなんとかなったりします。
筆者が一番よく採用するのはこの戦略です。
番外:マイグレーションの実行をなるべく避ける
番外編として「マイグレーションの実行をなるべく避ける」という戦略も紹介しておきます。
たとえば、「自分がメインで開発しているのはブランチAだが、状況によってはたまにブランチBを参照しなければいけない」という場合、ブランチBでは「コードを眺めるだけでrails db:migrate
は実行しない」とすれば、「ブランチBで追加した不要なテーブルやカラム」がブランチAにやってくることはありません。
もちろん、チーム開発であれば、マイグレーションが発生するような複数の開発タスクを並行して進めないように開発スケジュールや担当者アサインを調整してもらう、ということも大事です。
とにかく、 「むやみやたらにrails db:migrate
を実行すると、まだ取り込むべきでないデータベースの変更が取り込まれてしまい、ブランチを切り替えてもその変更が残ったままになってしまう」という意識を持つことが大事 です。
マイグレーションを実行する前は「本当にやる?本当に大丈夫?」と自問自答するぐらいの慎重さを持ってもいいと思います。
rails db:migrate:status の情報を読む
さて、ここまで「ブランチを切り替えてもデータベーススキーマは切り替わらないんだよ」「状況によってはソースコードとデータベーススキーマは矛盾した状態になることがあるんだよ」という話を書いてきました。
では、矛盾が起きているかいないかを客観的に判断するにはどうしたらよいでしょうか?
そんなときに便利なのがrails db:migrate:status
コマンドです。
以下はrails db:migrate:status
の実行例です。
$ rails db:migrate:status
database: db/development.sqlite3
Status Migration ID Migration Name
--------------------------------------------------
up 20231105053248 Create users
up 20231105053414 ********** NO FILE **********
down 20231105073625 Add address to users
rails db:migrate:status
の実行結果は、以下のような点に着目するとよいです。
Statusがupで、Migration Nameに名前が表示されている行 (1行目のようなケース)
- マイグレーションが実行され、そのマイグレーションに対応するマイグレーションファイルも存在する状態
- こういう行は矛盾を引き起こしていないので気にしなくてOK
Statusがdownの行 (3行目のようなケース)
- まだ実行されていないマイグレーションファイルの存在を意味する
- マイグレーションファイルを新規作成した直後や、git pullして新しいマイグレーションファイルがダウンロードされてきた状態
-
rails db:migrate
を実行するとupに変わるはず - が、本当に
rails db:migrate
を実行すべきかどうかは熟慮すべし(前述の「番外:マイグレーションの実行をなるべく避ける」を参照)
Statusがupで、Migration Nameが"NO FILE"の行 (2行目のようなケース)
- 何らかのマイグレーションは実行されているが、そのマイグレーションに対応するマイグレーションファイルが見つからない状態
- おそらく別のブランチで追加&実行されたマイグレーションファイルがある
- この行があるときはちょっと注意が必要
「upかつNO FILE」の行がある場合は要注意
最後に挙げた「upかつNO FILE」の行がある場合は、なぜこんな状態になっているのかを確認した方が良いです。
多くの場合、これは別のブランチで追加&実行されたマイグレーションがあることを意味しています。
たとえば、上の例だとmainブランチから"add-phone-number"ブランチに切り替えてdb/migrate
ディレクトリを確認すると、20231105053414_add_phone_number_to_users.rb
というファイルがあるので、このマイグレーションファイルの変更内容がデータベースに適用されたことがわかります(Migration IDの20231105053414
が一致している)。
# "add-phone-number"ブランチには、このファイルが存在する
# (mainブランチにはない)
class AddPhoneNumberToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :phone_number, :string
end
end
もちろん、"add-phone-number"ブランチでrails db:migrate:status
コマンドを実行すると、"NO FILE"にはなりません。
$ rails db:migrate:status
database: db/development.sqlite3
Status Migration ID Migration Name
--------------------------------------------------
up 20231105053248 Create users
up 20231105053414 Add phone number to users
「すべてupかつ、"NO FILE"が1件もない状態」が理想形
理想的なrails db:migrate:status
の実行結果は、すべてupかつ、"NO FILE"が1件もない状態です。
こうなっていればソースコードとデータベーススキーマはきれいに同期されている可能性が高いです。
逆に"NO FILE"が含まれていると、ソースコードとデータベーススキーマが矛盾している恐れがあります。
「これはたぶんあのブランチで追加したマイグレーションファイルだな」と、"NO FILE"の原因が予想できる場合はまだよいですが、「なんでこんなにも大量に身に覚えのない"NO FILE"があるんだ?」と首をかしげてしまう場合は、ちょっと時間をかけて原因を調査した方が良いかもしれません。
それでもなぜか db/schema.rb にdiffが発生することもある
上で述べたように、rails db:migrate:status
の実行結果がすべてupかつ、"NO FILE"が1件もない状態になっていれば、ソースコードとデータベーススキーマはきれいに同期されているはずです。
しかし、そうであるにも関わらず、rails db:migrate
を実行するとなぜか db/schema.rb
にdiffが発生してしまうケースがあります。
integerがbigintに変わってしまう
元々integer型だったのに、rails db:migrate
を実行するとなぜかbigint
に変わってしまうことがあります(SQLite3でよく起きる?)。
- t.integer "user_id", null: false
+ t.bigint "user_id", null: false
この件についてはその昔、いろいろ調査してissueを報告したことがあります。
issue自体は修正されてクローズしたはずなんですが、今でもときどき発生していて、原因はよくわかりません。
が、SQLite3ではintegerでもbigintでも扱いは同じなので、そこまで深刻な問題ではありません。
列の順番が変わってしまう
gitで管理しているdb/schema.rb
の列の順番と、ローカルのデータベースの列の順番が異なっていると、rails db:migrate
を実行するたびに毎回列の順番が入れ替わるdiffが発生します。
create_table "users", force: :cascade do |t|
- t.string "email"
t.string "name"
+ t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
上の例では、gitで管理しているdb/schema.rb
ではemail
→name
の順番でカラムが定義されているのに、ローカルのデータベースではname
→email
の順番で定義されているためにdiffが発生しています。
おそらく、複数のメンバーが並行して開発しているときに、ある人はemail
→name
の順にカラムを追加(=マイグレーションを実行)し、自分はname
→email
の順でカラムを追加してしまって、他の人とカラムの定義順がズレてしまったんだと思います。
MySQLではALTER TABLE文でカラムの定義順を変更できるようですが、PostgreSQLにはそうした機能はないため、列の並び順を変えるのはかなり面倒です。
PostgreSQLを使っている場合は上のページで説明されている手順に従ってがんばって並び順を変えるか、diffの発生を諦めるかのどちらかになると思います。
こういうdiffが発生したら毎回ロールバックする
上で挙げた2つのdiffは、アプリケーションの動作にはほぼ影響与えないので無視しても構いません。
ただ、発生したdiffをうっかりコミットしてしまうと不必要な変更をコードに加えてしまうことになるので、rails db:migrate
を実行して勝手に混入してきたdb/schema.rb
のdiffはロールバックするのがよいと思います。
歴史の長いRailsアプリケーションや複数人で開発しているRailsアプリケーションでは、このほかにもrails db:migrate
を実行するだけで勝手に混入するdb/schema.rb
のdiffがあるかもしれません。
そういったdiffの対処法はケースバイケースで異なってくるため、チーム内で対処方法を検討するようにしてください。
コードと矛盾したdb/schema.rbをきれいに直す方法
何らかの理由でそのブランチのコードとdb/schema.rb
が矛盾してしまった場合は、以下のコマンドで修正することができます。
# [注意] RAILS_ENV=testを必ず付けること!!
$ RAILS_ENV=test rails db:migrate:reset
このコマンドを実行すると、テスト環境のDBをdrop/createし、マイグレーションを最初から最後まで実行し直します。
そして、db/schema.rb
にその結果(テスト環境のデータベーススキーマ)を反映します。
これにより、「そのブランチに存在するマイグレーションファイルと完全に同期した db/schema.rb
ができあがる」というわけです。
注意点は 必ず RAILS_ENV=test
を付ける 、ということです。
これを付けないとdevelopment環境のDBをdrop/createすることになり、開発用に使ってきたローカル環境のデータがすべてぶっ飛んでしまうことになります。
また、この操作ではあくまで db/schema.rb
(と、test環境のデータベーススキーマ)がソースコードと同期するだけです。development環境のデータベーススキーマはソースコードと矛盾したままになっているかもしれない点に注意してください。
(謝辞:このテクニックは弊社ソニックガーデンの @pi_chan に教えてもらいました🙏)
途中からプロジェクトに参加した人はrails db:schema:loadを使う
ところで、この記事で何度も登場しているdb/schema.rb
はそもそも何のファイルなんでしょうか?データベーススキーマの状態をコード上で確認するためだけに存在しているのでしょうか?
実は、db/schema.rb
はデータベーススキーマをゼロから構築する際に実行するrails db:schema:load
コマンドで利用されます。
データベーススキーマをゼロから構築する際は以下の2つの方法があります。
-
rails db:migrate
-
db/migrate
ディレクトリにあるマイグレーションファイルを順番に実行する
-
-
rails db:schema:load
-
db/schema.rb
のスキーマ情報をそのままデータベースに反映する(db/migrate
のマイグレーションファイルは使わない)
-
きれいに作られてきているRailsアプリケーションではどちらでも同じ結果になるはずですが、そうではない場合、お行儀の悪いマイグレーションファイルが含まれていると、rails db:migrate
を実行したときにエラーが発生して、マイグレーションが途中で止まってしまうことがあります。
rails db:schema:load
ではマイグレーションファイルを実行しないので、大半の場合、エラーなくデータベーススキーマを構築することができます。
Railsガイドでも新規にデータベースを構築する際はrails db:schema:load
を使うことを推奨しています。
アプリケーションのデータベースの新しいインスタンスを作成する場合、マイグレーションの全履歴を最初から繰り返すよりも、単に
rails db:schema:load
でスキーマファイルを読み込む方が、高速かつエラーが起きにくい傾向があります。
ですので、途中からRailsプロジェクトに参加してきたメンバーがいたら、rails db:migrate
ではなく、rails db:schema:load
を実行するように伝えてあげてください。
$ rails db:create
# db:migrateだとエラーが起きるかもしれないので、db:schema:loadを実行
$ rails db:schema:load
もちろん、その場合はdb/schema.rb
にソースコードと矛盾したおかしな定義情報が含まれていないことが大前提です。
その他、知っておくと良いこと
GitHubにpushしたマイグレーションファイルを安易に修正しない
マイグレーションファイルに記述ミスがあったからといって、安易にそのファイルを修正してしまうと、他の開発メンバーが迷惑するケースがあります。
詳しくは以下の記事を参照してください。
コンフリクトしたschema.rbをきれいにマージする手順
複数メンバーが並行して開発していると、schema.rbがコンフリクトすることもよくあります。
その場合の対応手順を以下の記事にまとめています。
マイグレーション作成時のチェックポイント
筆者が書いた記事ではありませんが、マイグレーション作成時に気を付けた方がいいポイントがきれいにまとめられています。
コメント欄で筆者もコメントしているので、その内容も参考にしてみてください。
既存のテーブルやカラムの削除は後戻りできないので慎重に
当たり前ですが、 drop_table
や remove_column
で既存のテーブルをカラムを削除すると、そこに保存されていたデータはもう戻ってきません(バックアップがあればリストアできるが、実際はやるのはかなり面倒)。
なので、まずはソースコード上で削除したいテーブルやカラムにアクセスしないように変更して、しばらく何もエラーが起きなければ本当に削除する、という手順を踏んだ方が良いです。
なお、カラムを削除する場合はignore_columns
メソッドを使うと、指定したカラムを論理削除できます。物理削除する前にまず論理削除して、何も問題が起きないことを確認しましょう。
class Post < ApplicationRecord
# titleカラムを論理削除する
self.ignored_columns = [:title]
end
「やばい!」と思ったら先輩プログラマを頼る
ここまで説明してきたように、gitできれいに管理できず、簡単にソースコードと不整合を起こしてしまうデータベーススキーマの扱いはかなり厄介です。
「やばい!なんかやらかしてしまった気がする……」とか「いろいろ触ってるうちに今自分が何をやってるのかわからなくなった😭」といった状況に陥ったら、すぐにメンターや先輩プログラマに助けを求めましょう。
自力でなんとかしようとすると泥沼にはまって余計に状況を悪化させるかもしれませんよ!
まとめ
というわけで、この記事ではRailsのデータベースマイグレーションで気を付けるべき注意事項をあれこれ書いてみました。
具体的には以下のような内容を説明しました。
- gitでブランチを切り替えても、gitはデータベーススキーマの状態までは切り替えてくれない
- ゆえにブランチを切り替えたタイミングで、「そのブランチでは本来存在するべきではないテーブルやカラム」がデータベーススキーマに残った状態になることがある
- その状態で
rails db:migrate
を実行すると、「そのブランチでは本来存在するべきではないテーブルやカラム」がひょっこりdb/schema.rb
に顔を出す -
db/schema.rb
がdiffに含まれているときは、コミットする前にソースコードとdb/schema.rb
がきれいに同期しているか入念にチェックすべき - コードとDBの不整合を避けるという意味で、自分がメインで開発しているブランチ以外では、なるべく
rails db:migrate
を実行しない方よい
こうした知識は一人でRailsの勉強をしているときはほとんど気付けないので、これからチーム開発に入ろうとしている人や、チーム開発に入って間もない人はぜひ理解しておいてください。