こんにちは。会計Plusでエンジニアをしているぽっけです。最近はシャケをしばくバイトで、やっとでんせつに上がりました。
今日はstrong_migrationsというRails向けのgemを紹介します。
strong_migrationsとは
https://github.com/ankane/strong_migrations
strong_migrationsは、危険なmigrationを検出するgemです。
データベースのmigrationは、ときに危険になります。たとえば実行するDDLによってはデータベースへの書き込みをブロックしてしまうことがあります。またテーブル定義の変更は、うまくやらないとアプリケーションが意図せぬ動作をするかも知れません。
strong_migrationsはそのような危険なmigrationを検出します。
使い方
使い方はかんたんです。strong_migrations
gemをGemfile
に追加し、bin/rails generate strong_migrations:install
コマンドを実行するだけです。
strong_migrationsをインストールしておくと、危険なmigrationは自動的にエラーになって失敗するようになります。
また危険とみなされたものも、safety_assured { add_column ... }
のように、safety_assured
メソッドのブロックの中に書くことでエラーを抑制できます。
検出される問題
検出されるすべての例は、READMEに列挙されています。 https://github.com/ankane/strong_migrations#checks
わかりやすいところだと、カラムの削除やデフォルト値付きのカラムの追加などがあります。
strong_migrationsはRails向けのgemですが、チェックされる項目の多くはRDBMSに起因する問題であるため、Railsを使っていない方も目を通してみると発見があるかも知れません。
strong_migrationsの素晴らしい点
危険なmigrationを検出できるという点でかなり価値のあるgemだと思いますが、個人的にstrong_migrationsの真の素晴らしさは別の所にあると思います。それは検出結果の手厚さです。
たとえばデフォルト値付きのカラムの追加が行われた場合のエラーを見てみましょう。strong_migrationsを有効にしていると、次のmigrationはエラーが発生します。
class AddTitleToArticle < ActiveRecord::Migration[7.0] def change add_column :articles, :title, :string, default: '' end end
検証用コードのフルバージョンはこちら(長いので折りたたんであります)。
# frozen_string_literal: true require "bundler/inline" gemfile(true) do source "https://rubygems.org" git_source(:github) { |repo| "https://github.com/#{repo}.git" } # Activate the gem you are reporting the issue against. gem "activerecord", "~> 7.0.0" gem 'strong_migrations' gem "sqlite3" end require "active_record" require "minitest/autorun" # This connection will do for database-independent bug reports. ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") ActiveRecord::Schema.define do create_table :articles, force: true do |t| t.string :content, null: false end end class Article < ActiveRecord::Base end class AddTitleToArticle < ActiveRecord::Migration[7.0] def change add_column :articles, :title, :string, default: '' end end class BugTest < Minitest::Test def test_migration AddTitleToArticle.migrate(:up) end end
この検証用コードはRailsのバグ報告テンプレートをベースにしています。こういうときにめっちゃ便利です。
発生するエラーは以下のとおりです。
=== Dangerous operation detected #strong_migrations === Adding a column with a non-null default blocks reads and writes while the entire table is rewritten. Instead, add the column without a default value, then change the default. class AddTitleToArticle < ActiveRecord::Migration[7.0] def up add_column :articles, :title, :string change_column_default :articles, :title, "" end def down remove_column :articles, :title end end Then backfill the existing rows in the Rails console or a separate migration with disable_ddl_transaction!. class BackfillAddTitleToArticle < ActiveRecord::Migration[7.0] disable_ddl_transaction! def up Article.unscoped.in_batches do |relation| relation.update_all title: "" sleep(0.01) end end end
このように、かなり手厚い出力がなされます。strong_migrationsの特に素晴らしい点は、エラーメッセージに以下の項目が網羅されていることです。
- 何が問題なのか
- なぜ問題になるのか
- どのようにして対応をしたらいいのか
たとえばこのエラーメッセージでは、「デフォルト値付きのカラムの追加」が問題であり、それが「テーブル全体を再書き込みするためにread/writeをブロックする」のが問題になる理由であり、「提案されているコードを参考に書き換える」必要があることが説明されています。
この3点が説明されていることで、このエラーメッセージを見たユーザーは次に何をしたらいいのかが理解できます。つまり、strong_migrationsの警告はとても対応しやすくなっています。このようなツールはエラーへの対応方法が分かりづらく無駄に時間を吸われてしまうものも多い中、このように充実した説明はツールを使う人にとってとても優しいです。
この丁寧さは、自分がなにかツールを作る際にも意識したいものです。
(欲を言えば「このエラーをsafety_assured
で握りつぶして良い場合」も説明されていると、より嬉しいかも知れないですね。)
まとめ
strong_migrations gemの紹介でした。このgemを使うとかんたんに危険なmigrationを検出できます。またこのgemの出力の手厚さについても紹介しました。
少し前にstrong_migrationsにpull requestを送ってマージされたのが嬉しかったので、今回は紹介をしてみました。
ぜひあなたのRails appにも導入してみてはいかがでしょうか?
マネーフォワードでは、エンジニアを募集しています。 ご応募お待ちしています。
【会社情報】 ■Wantedly ■株式会社マネーフォワード ■福岡開発拠点 ■関西開発拠点(大阪/京都)
【SNS】 ■マネーフォワード公式note ■Twitter - 【公式】マネーフォワード ■Twitter - Money Forward Developers ■connpass - マネーフォワード ■YouTube - Money Forward Developers