0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Laravel・Rails・Phoenix 対応表(1/8)パッケージ管理 — Composer / Bundler / Mix

0
Last updated at Posted at 2026-08-24

PHP/Laravel、Ruby/Rails の経験者が Elixir/Phoenix に入門するとき(またはその逆)、最初に触るのがパッケージ管理です。本記事では Composer / Bundler / Mix の対応関係を、日常コマンドからバージョン制約の記法、ロックファイルの運用、プライベートパッケージ、モノレポ構成、依存解決アルゴリズムの違いまで掘り下げてまとめます。

本記事は Laravel・Rails・Phoenix 対応表シリーズ(全8本)の1本目です。

シリーズ一覧:

  1. パッケージ管理 — Composer / Bundler / Mix (本記事)
  2. プロジェクト作成と日常のコマンド — artisan / rails / mix
  3. REPL — tinker / rails console / IEx
  4. インラインデバッグ — binding.pry と IEx.pry の世界
  5. プリントデバッグ — dd() / pp / IO.inspect
  6. テスト — PHPUnit・Pest / Minitest・RSpec / ExUnit
  7. 定番ライブラリ — ORM・認証・ジョブ・リアルタイムまで
  8. 3つのエコシステムの思想の違い

対象バージョン(2026年8月執筆時点):

PHP / Laravel Ruby / Rails Elixir / Phoenix
言語 PHP 8.5 Ruby 4.0(Rails 8.1 の必須要件は 3.2+) Elixir 1.20
フレームワーク Laravel 13 Rails 8.1 Phoenix 1.8

全体の対応関係

まず全体像から。Elixir では Bundler に相当する別ツールは存在せず、言語標準のビルドツール Mix に依存管理が統合されているのが最大の違いです。Hex(レジストリのクライアント)は初回に mix local.hex で入れますが、日常操作はすべて mix サブコマンドとして提供されます。

PHP Ruby Elixir
パッケージレジストリ Packagist RubyGems Hex
管理ツール Composer Bundler(+ RubyGems) Mix(言語標準に統合)
依存の宣言 composer.json Gemfile mix.exsdeps/0
ロックファイル composer.lock Gemfile.lock mix.lock
dev専用依存 require-dev group :development, :test only: [:dev, :test] オプション
オートロード/読込 vendor/autoload.php(PSR-4) require / Zeitwerk(Rails) コンパイル時に解決
ドキュメントホスティング 各自(GitHub Pages 等) rubydoc.info HexDocs(公式・公開時に自動生成)

Elixir の依存宣言はこういう形です。設定ファイルではなく Elixir のコードなので、条件分岐やコメントも普通に書けます。

# mix.exs
defp deps do
  [
    {:phoenix, "~> 1.8.0"},
    {:req, "~> 0.5"},
    {:credo, "~> 1.7", only: [:dev, :test], runtime: false}
  ]
end

コマンド対応表

やりたいこと Composer Bundler Mix
依存をインストール composer install bundle install mix deps.get
依存を追加 composer require foo/bar bundle add foo mix.exs に手書き → mix deps.get ※1
依存を更新 composer update foo/bar bundle update foo mix deps.update foo
全依存を更新 composer update bundle update mix deps.update --all
古い依存の確認 composer outdated bundle outdated mix hex.outdated
脆弱性監査 composer audit bundle exec bundler-audit(gem) mix deps.audit(mix_audit)
パッケージ情報 composer show foo/bar gem info foo mix hex.info foo
依存ツリー表示 composer show --tree bundle graph(plugin)※2 mix deps.tree
逆依存の調査 composer why foo/bar gem dependency foo --reverse-dependencies mix deps.tree の目視
グローバルツール導入 composer global require gem install mix archive.install hex foo ※3
ロック準拠で実行 vendor/bin/xxx bundle exec xxx 不要(mix は常に lock 準拠)

※1: Mix には bundle add 相当のコアコマンドがありません。mix hex.info foo で最新バージョンを確認して {:foo, "~> 1.0"} を手書きするのが基本です(後述の Igniter を導入すると mix igniter.install foo で追加+自動設定まで可能)。

※2: かつての bundle viz は Bundler 2.2 で本体から切り出され、公式プラグイン bundler-graphbundle graph になりました(Graphviz が必要)。

※3: mix archive.install はジェネレータ系(phx_new など)、mix escript.install は CLI ツール系に使います。

# 例: Phoenix のプロジェクトジェネレータを入れる
mix local.hex --force                       # Hex クライアント(初回のみ)
mix archive.install hex phx_new --force     # gem install rails 相当

バージョン制約の記法比較

3エコシステムとも「セマンティックバージョニング前提で、破壊的変更が入らない範囲を許可する」演算子を持ちますが、記号と意味の対応が微妙にズレているのが罠です。

意味 Composer Bundler / RubyGems Mix / Hex
>= 1.2.3 かつ < 2.0.0 ^1.2.3 ~> 1.2(+ >= 1.2.3 ~> 1.2(+ >= 1.2.3
>= 1.2.3 かつ < 1.3.0 ~1.2.3 ~> 1.2.3 ~> 1.2.3
完全固定 1.2.3 = 1.2.3 == 1.2.3
範囲指定 >=1.2 <2.0(スペース = AND) ">= 1.2", "< 2.0"(複数引数) ">= 1.2.0 and < 2.0.0"
OR 条件 ^1.0 || ^2.0 "~> 1.0 or ~> 2.0"

覚え方はこうです。

  • Ruby と Elixir の ~>(悲観的演算子)は同じ意味: 「最後に書いた桁だけ動いてよい」。~> 1.2 はマイナー更新まで許可(< 2.0)、~> 1.2.3 はパッチ更新のみ許可(< 1.3.0)。桁数で意味が変わります
  • Composer の ^ は Ruby/Elixir の ~> 1.2 相当(メジャー未満を許可)。ただし ^0.3 のような 0.x 系は「0.x は何が起きても仕方ない」という semver の慣習に従い >= 0.3.0 < 0.4.0 に狭まります
  • Composer の ~ は桁数依存な点が要注意です。~1.2.3< 1.3.0 ですが、~1.2< 2.0.0 まで広がります。「チルダだからパッチのみ」と思い込んでいると、2桁指定でマイナーが上がって驚くことになります

実務では「Composer は ^、Ruby / Elixir は ~> + 3桁 or 2桁」に統一しておくのが混乱しない近道です。Hex は "~> 1.0" のようにライブラリ側の依存も緩めに書く文化で、mix hex.info foo が表示する {:foo, "~> x.y"} をそのまま貼るのが定番です。

ロックファイルと CI での再現性

「宣言ファイルは 許容範囲、ロックファイルは 実際に使う確定バージョン」という2層構造は3つとも共通です。事故が起きるのは決まって「install のつもりが update だった」パターンなので、対応を整理します。

やりたいこと Composer Bundler Mix
lock どおりに入れる composer install bundle install mix deps.get
lock を作り直す composer update bundle update mix deps.update --all
CI で lock 逸脱を検出 composer validate --strict bundle config set frozen true mix deps.get --check-locked
  • Composer: composer install は lock があればそれに従いますが、composer.json と lock が食い違っていると警告付きで lock を優先します。CI では composer validate --strict を先に走らせて「lock が composer.json と同期しているか」を落とすのが定石です。「1個だけ追加したいのに composer update を打って全部上がった」事故は、追加は常に composer require foo/bar(そのパッケージと依存だけを解決)に統一すれば防げます
  • Bundler: frozen(旧 deployment mode)を有効にすると、GemfileGemfile.lock が一致しない場合に bundle install が失敗します。CI では環境変数 BUNDLE_FROZEN=true を立てておくのが簡単です
  • Mix: mix deps.get は lock を書き換えることがあります(新しい依存を追加した直後など)。CI では mix deps.get --check-locked を使うと、lock に変更が必要な状態ならエラーで落ちるため、「lock のコミット忘れ」を検出できます

もうひとつ Mix 固有の注意点として、依存パッケージは自分のアプリの MIX_ENV に関係なく常に :prod 相当でコンパイルされます。「dev では動くのに依存の debug 用コードが本番で消えている」ような Rails 的な感覚のズレはなく、環境で変えたいのは only: オプション(インストールの有無)だけです。

依存解決アルゴリズムの違い

普段は意識しませんが、「解決が終わらない」「エラーメッセージが謎」というときに効いてくる部分です。

Composer 2 Bundler Mix / Hex
解決アルゴリズム SAT ソルバ PubGrub(2.4 で Molinillo から移行) PubGrub(hex_solver、Hex 2.0 で移行)
特徴 高速・省メモリ(2.0 で大幅改善) 失敗時の説明が読みやすい 失敗時の説明が読みやすい

面白いのは、Bundler と Hex がまったく同じ結論(PubGrub)に別々にたどり着いていることです。PubGrub は Dart の pub 由来のアルゴリズムで、「なぜ解決に失敗したか」を人間が読める形で導出できるのが売りです。Bundler は 2.4 で長年使った Molinillo から移行し、Hex も 2.0 で PubGrub ベースの hex_solver に置き換えました(依存が複雑なプロジェクトで解決が数分〜無限に見えるレベルで固まる問題への対策)。バージョンコンフリクトのエラーが以前より格段に読みやすくなっているのはこのためです。

コンフリクト調査の実践コマンドはこのあたりです。

# Composer: なぜこのパッケージが入っているのか / なぜこのバージョンに上げられないのか
composer why foo/bar
composer why-not foo/bar 2.0

# Bundler / RubyGems
gem dependency rails --reverse-dependencies
bundle graph   # bundler-graph プラグイン(要 Graphviz)

# Mix
mix deps.tree
mix deps.unlock --check-unused   # lock に残った未使用エントリの検出(CI 向き)

path 依存とモノレポ

社内ライブラリを切り出して同一リポジトリで開発するパターンの対応です。

Composer Bundler Mix
ローカルパス依存 path リポジトリ gem "foo", path: "../foo" {:foo, path: "../foo"}
git 依存 vcs リポジトリ gem "foo", github: "org/foo" {:foo, github: "org/foo"}
モノレポ標準機能 umbrella プロジェクト

Composer の path リポジトリは composer.json に書きます(デフォルトで symlink されるため、編集が即反映されます)。

{
  "repositories": [
    { "type": "path", "url": "../packages/*" }
  ],
  "require": { "acme/billing": "*" }
}

Ruby / Elixir はもっと単純で、宣言に path: を足すだけです。

# Gemfile
gem "billing", path: "engines/billing"
# mix.exs
{:billing, path: "../billing"}

Elixir にはさらに一段上の仕組みとして umbrella プロジェクトmix new my_app --umbrella)があります。apps/ 配下に複数の Mix プロジェクトを並べ、相互依存を {:billing, in_umbrella: true} と書くと、ビルド成果物と依存(deps/_build/)をルートで共有しつつ、アプリごとに独立してテスト・リリースできます。Rails Engine のように「フレームワークの機能」ではなく、ビルドツールのレイヤーでモノレポがサポートされているのが特徴です。ただし Phoenix コミュニティでは近年「まずは単一アプリ + コンテキスト分割で十分」という揺り戻しもあり、umbrella は「複数の独立したデプロイ単位が本当にあるとき」の選択肢と考えるのがよいです。

プライベートパッケージ / 社内レジストリ

方式 PHP Ruby Elixir
公式の有償サービス Private Packagist Hex organizations(hex.pm 内蔵)
セルフホスト Satis(静的リポジトリ生成) geminabox / gemstash mix hex.registry build(静的レジストリ生成)
git リポジトリ直接参照 vcs リポジトリ gem "foo", github: "org/foo" {:foo, github: "org/foo"}

Hex は公式レジストリ自体にプライベートパッケージ機能(organizations)が組み込まれているのが便利なところです。使う側は依存に organization: を足すだけです。

{:secret, "~> 1.0", organization: "acme"}

認証は開発機なら mix hex.user auth(所属 organization すべてに自動アクセス)、CI では専用キーを発行して mix hex.organization auth acme --key <hash> を使います。公開側は mix hex.publish --organization acme です。

git 依存は3つとも書けますが、性質が異なります。

  • Ruby: gem install は git を扱えないため、git 依存は Bundler 経由でのみ使えます
  • Elixir: git / path 依存のパッケージは Hex に publish できません(Hex パッケージの依存は Hex パッケージのみ)。アプリでは自由に使えますが、ライブラリを書くときは制約になります
  • PHP: repositoriesvcs を並べる方式はリポジトリ数が増えると解決が遅くなるため、規模が出たら Satis / Private Packagist に寄せるのが定番です

パッケージを公開する側の対応

Packagist RubyGems Hex
公開コマンド GitHub 連携(push で自動更新) gem buildgem push mix hex.publish
メタデータ composer.json foo.gemspec mix.exspackage/0
ドキュメント 各自 rubydoc.info が自動生成 HexDocs へ同時公開
ヤンク/取り下げ 論理削除 gem yank mix hex.retire foo 1.0.0

Hex の公開フローはほぼ Mix に統合されています。

# mix.exs
def project do
  [
    app: :my_lib,
    version: "0.1.0",
    description: "何をするライブラリか",
    package: [
      licenses: ["MIT"],
      links: %{"GitHub" => "https://github.com/acme/my_lib"}
    ],
    deps: deps()
  ]
end
mix hex.user register   # 初回のみ
mix hex.publish         # パッケージ + ドキュメントを公開

mix hex.publish は ex_doc で生成した HTML ドキュメントを hexdocs.pm に同時公開します。Elixir ライブラリのドキュメントがどれも同じ見た目で hexdocs.pm に揃っているのはこの仕組みのおかげで、「README しかない gem」「docs サイトが野良ホスティングの package」が起きにくいエコシステムになっています。

Igniter — 「composer require 相当 + 自動設定」の新定番

「依存を足したら config も書き換えてくれる」方向の進化として、Igniter というコード生成・プロジェクトパッチのフレームワークが近年広まっています(Ash Framework 発)。

# 既存プロジェクトに導入
# mix.exs に {:igniter, "~> 0.6", only: [:dev, :test]} を追加して deps.get 後
mix igniter.install oban        # 依存追加 + そのライブラリのインストーラを自動実行

# 新規プロジェクトを最初から Igniter で
mix archive.install hex igniter_new
mix igniter.new my_app --install ash,oban --with phx.new

単に mix.exs へ1行足すだけでなく、ライブラリ側が用意したインストーラ(config 追記、supervision tree への追加など)まで実行してくれるのがポイントで、Laravel のパッケージが service provider の auto-discovery で「入れたら動く」のに近い体験を後付けしています。対応しているのは Igniter 対応パッケージ(Ash、Oban など増加中)ですが、未対応パッケージでも依存追加だけは代行してくれます。

まとめ

  • Mix は Composer + Bundler の仕事を言語標準ツールが兼ねるbundle exec 相当は不要、ドキュメント公開まで統合
  • バージョン制約は「Composer は ^、Ruby/Elixir は ~>」。Composer の ~1.2(2桁)はマイナーまで動く点だけ要注意
  • CI の lock 検証は composer validate --strict / BUNDLE_FROZEN=true / mix deps.get --check-locked
  • 依存解決は Bundler と Hex がともに PubGrub へ移行済み。コンフリクト時のエラーが読みやすい
  • プライベートパッケージは Hex organizations が最も手数が少ない。git/path 依存のライブラリは Hex に publish できない点に注意
  • bundle add 相当がない Mix の弱点は、Ignitermix igniter.install)がむしろ「自動設定込み」で上回りつつある

参考リンク


次の記事: Laravel・Rails・Phoenix 対応表(2/8)プロジェクト作成と日常のコマンド — artisan / rails / mix

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?