RailsエンジニアがまとめるDBマイグレーションの今|積み上げ式と状態ベース、Rails migrate・RidgepoleとLaravel migrate・sqldefの整理

RailsからLaravelを眺める

※本記事は「RailsからLaravelを眺める」シリーズの第7回です。Rails出身の私がLaravelを触りながら、Railsと比較して違いを整理していく連載になります。


はじめに

RailsでもLaravelでも、アプリ開発を始めると必ず登場するのが DBマイグレーション
テーブルやカラムをコードで管理できるのは便利ですが、数年単位で運用すると マイグレーションファイルが雪だるま式に膨れる という課題に直面します。

この問題に対応するため、Rails界隈では Ridgepole、Laravel界隈では sqldef を採用するプロジェクトが増えています。
今回は標準migrateを整理した上で、Ridgepoleとsqldefとは何者なのか、どう違うのかをサンプルコード多めで紹介します。


Rails標準のmigrate

基本の流れ

bin/rails generate migration AddAgeToUsers age:integer
bin/rails db:migrate

生成されるファイル例:

class AddAgeToUsers < ActiveRecord::Migration[8.0]
  def change
    add_column :users, :age, :integer
  end
end

db/schema.rb

create_table "users", force: :cascade do |t|
  t.string "name"
  t.integer "age"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

メリット

  • Rails Wayに統合されている
  • ActiveRecordとの連動が自然

デメリット

  • マイグレーションファイルが積み上がる
  • 大規模プロジェクトでは管理が困難

Laravel標準のmigrate

基本の流れ

php artisan make:migration add_age_to_users_table --table=users
php artisan migrate

生成されるファイル例:

return new class extends Migration {
    public function up(): void {
        Schema::table('users', function (Blueprint $table) {
            $table->integer('age')->nullable();
        });
    }

    public function down(): void {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('age');
        });
    }
};

メリット

  • SeederやFactoryと統合しやすい
  • API開発との親和性が高い

デメリット

  • Railsと同じく積み上げ式
  • 長期運用でフォルダが膨張

Ridgepoleとは?

Ridgepoleは、Rails界隈で広く使われている DBスキーマ管理ツール です。

特徴

  • スキーマを Schemafile に宣言的に記述
  • DBとの差分を検出し、自動で ALTER TABLE を生成・適用
  • ActiveRecord DSLに近い書き方ができる

サンプル

Schemafile

create_table "users", force: :cascade do |t|
  t.string   "name", null: false
  t.integer  "age"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

適用

bundle exec ridgepole --apply -f Schemafile --config config/database.yml

メリット

  • Railsの世界観にマッチしている
  • スキーマの状態を一元管理できる
  • 過去のマイグレーション履歴を気にせずDBを再現可能

sqldefとは?

sqldefは、Laravelやその他言語でも利用される SQLベースのスキーマ管理ツール です。

特徴

  • SQLファイルでスキーマを定義
  • --apply コマンドで差分を自動検出・適用
  • MySQL/PostgreSQL/SQLiteなど幅広く対応
  • 言語に依存せず、Rails/Laravel問わず利用可能

サンプル

初期エクスポート:

mysqldef -u root -p myapp_db --export > schema.sql

schema.sql

CREATE TABLE users (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(255) NOT NULL,
  age INT,
  created_at DATETIME NOT NULL,
  updated_at DATETIME NOT NULL
);

適用:

mysqldef -u root -p myapp_db --apply < schema.sql

差分例:

ALTER TABLE users ADD COLUMN age INT;

メリット

  • SQLなので直感的で学習コストが低い
  • 言語非依存、チーム混在環境でも使いやすい
  • CI/CDとの相性が良い

Ridgepoleとsqldefの違い

項目Ridgepolesqldef
言語RubyGo
定義ファイルRuby DSL (Schemafile)SQL (schema.sql)
適用方法ridgepole --applysqldef --apply
対応DBMySQL / PostgreSQLMySQL / PostgreSQL / SQLite 他
フレームワーク親和性Rails文化圏言語非依存(Laravelで採用増)
学習コストRails経験者に低いSQLが分かれば誰でも扱える

実務での選び方

Railsアプリを運用するなら

  • 小〜中規模・短期開発:標準の db/migrate で十分。
  • 中〜大規模・長期運用:Ridgepoleを導入することで、Schemafile一発でDBを再現できる。
  • CI/CD:Schemafileをコミットしておけば、テスト環境や本番リリースでの事故を大幅に減らせる。

Laravelアプリを運用するなら

  • 小規模・初期フェーズphp artisan migrate で十分。Seederとの組み合わせも便利。
  • 成長期・チーム開発本格化:sqldefで状態同期を導入すれば、マイグレーション地獄を回避できる。
  • マルチ言語環境:バックエンドがLaravelでも、フロントや別サービスがGo/Nodeの場合はSQLベースのsqldefが強い。

両者に共通する実務的判断基準

  • DBをどう位置づけるか
    • アプリの一部 → 標準migrate
    • インフラ資産 → Ridgepole / sqldef
  • プロジェクト寿命
    • 短期(PoC、数ヶ月) → 標準migrate
    • 長期(数年〜) → 状態同期型を検討
  • 開発体制
    • 少人数 → 標準migrate
    • 複数チーム・複数言語 → sqldef/Ridgepole

まとめ

RailsとLaravelの標準migrateは便利ですが、どちらも「積み上げ式」であり、長期運用には向きません。

  • Ridgepole はRails向けに「状態同期型」の発想を持ち込み、SchemafileでDBスキーマを一元管理できる。
  • sqldef はSQLベースで言語を選ばず、Laravelをはじめ幅広い環境で使える。

両者の共通点は、「DBスキーマをコードとして宣言し、差分を自動適用する」 という思想です。
これにより、開発環境・テスト環境・本番環境を正確に揃え、リリース時の事故を防ぐことができます。

実務的結論

  • 短期開発や小規模プロジェクト → 標準migrateで十分。
  • 長期運用・チーム開発 → 状態同期型を導入して将来の負債を減らす。
  • 異種混在チームやマイクロサービス → SQLで一元管理できるsqldefが最適。

2025年の現場感としては、RailsならRidgepole、Laravelならsqldefが半ばデファクトになりつつあります。
「未来の自分やチームを楽にできるかどうか」を基準に選ぶのが、最も実務的な判断基準でしょう。

コメント

タイトルとURLをコピーしました