🐥

DB のスキーマ情報の差分と闘った話し (Rails)

2023/06/19に公開

みなさんこんにちは。
バックエンドエンジニアの豊永です。

今回は Rails を使ってるとよく遭遇すると思われる問題に対して、 Makefile を使って解決した話を共有しようと思います。

何が問題だったか?

Github の Pull Request 機能を利用して複数人でコードレビューを行いながら開発するシーンはよくあるかと思います。その際に、 rails db:migrate を実行し自分の開発用 DB にレビュー中のコードのスキーマ情報を適用してしまうことは無いでしょうか?
レビュー完了後、そのまま自分の開発に戻り Pull Request を出すと、身に覚えのないテーブル名が db/schema.rb に記述されていて驚くことがあります。
実は db/schema.rb は migration ファイルから直接生成されるものではなく、開発用 DB から生成される仕組みになっています。(※ db:migrate 時に RAILS_ENV を明示しない場合)
そのため、レビュー中に適用したスキーマ情報が反映されてしまうのです。

Pull Request で正確なレビューを行うためには、自分の Pull Request には自分が更新した内容のみを含めるべきです。
これを実現するために今までは次の対応を行っていました。

  • 空の db 作成
  • db:migrate を再実行

しかし、レビューが重なると毎回この作業を行う必要がありとても手間です。

解決方法の検討

  1. db/schema.rb を管理対象から外す
  2. 簡単に db:migrate を イチから実行出来るようにする

1. db/schema.rb を git の管理対象から外す

管理対象から外せば、差分を気にしなくて良くなるので、良い案と思いました。
しかし、以下の懸念があり、この案は不採用としました。

  • CI でテーブル作成のタイミングで使っている
  • 本当に影響がないのかは分からず、ちょっと怖い

2. 簡単に db/schema.rb を イチから作成出来るようにする

先の説明の通り db/schema.rb は開発用 DB から作成されます。それであれば、空の DB を作成しその DB に対して migration ファイルを順に適用していけば、正しい db/schema.rb が作成できるはずです。
しかし、Pull Request を出すたびに開発用 DB をクリアするのは現実的ではありません。そこで db/schema.rb を作り出すためだけの DB を簡単なコマンドで作成できる仕組みを導入しました。

その仕組みを Makefile を使って実現することにしました。

Makefile は、主に再コンパイルのコマンドをまとめておくものになります。

man make より

The purpose of the make utility is to determine automatically which pieces of a large program need to be recompiled, and issue the commands to recompile them.

今回、再コンパイルではないですが、コマンドをまとめておく用途で使います。

コードの修正

Makefile の抜粋

latest-schema:
  echo 'migrate start'
  docker-compose exec -e TMP_WORKING_DATABASE='tmp_working' web bundle exec rails \
    db:drop \
    db:create \
    db:migrate
  echo 'migrate end'

※ Docker / docker-compose を使っているという前提なので注意してください。
データベース名を外から渡せば、現在使っている開発環境のデータベースも影響を受けることがありません。

config/database.yml の抜粋

development:
  <<: *default
  database: <%= ENV['TMP_WORKING_DATABASE'] || 'xxxxx_dev' %>
  host: <%= ENV['DB_HOST'] || '127.0.0.1' %>
  username: xxxxx
  password: xxxxx
  port: 5432

実行

あとは、db/schema.rb に意図しない差分ができた時など、db/schema.rb を綺麗にしたい時にこのコマンドを実行すれば OK です。

$ make latest-schema

まとめ

Makefile といえば C 言語などのコンパイルに利用するイメージがありますが、今回のように一連のコマンド実行をまとめておく用途にも使えます。この Makefile をチームで採用してからは Pull Request 時の不可解なファイル更新に悩まされることなくレビューできるようになりました。
同様の悩みを抱えている方のご参考になれば幸いです。

Linc'well, inc.

Discussion