Composerはツールではなく「契約」

なぜComposerのトラブルは「人間側の問題」になるのか

結論から言うと、Composerはライブラリを入れる便利ツールではありません。
プロジェクトの前提条件を文章化した“契約書”に近い存在です。

この認識を持たないまま使うと、
「なぜか動かない」「昨日は動いた」が頻発します。

逆に言うと、Composerのトラブルの多くは
コマンドの使い方ではなく、契約の扱い方の問題です。

composer.jsonは設定ではなく宣言

多くの人が最初に誤解する点があります。

composer.jsonは設定ファイルだと思われがちです。
しかし役割は違います。

設定ファイル:ツールの動作を変える
宣言ファイル:プロジェクトの条件を定義する

composer.jsonは後者です。

{
  "require": {
    "php": "^8.1",
    "framework/core": "^5.0"
  }
}

これは「こういう環境とライブラリが存在する前提でコードを書きます」という宣言です。
つまり、実装より先に前提を定義しています。

composer.lockは契約の確定版

さらに重要なのがcomposer.lockです。

composer install

このコマンドは、lockファイルの内容を再現します。
つまり、契約を“履行”します。

逆に、

composer update

は契約を“更新”します。
内容が変わります。

ここを区別しないと、
意図せずプロジェクトの前提を変更することになります。

なぜチーム開発で重要になるのか

1人開発では問題が表面化しにくいです。
同じ環境で同じ人が作業するからです。

しかしチームでは違います。

  • 開発者AのPC
  • 開発者BのPC
  • CI
  • 本番

すべて環境が違います。
契約がないと成立しません。

composer.lockを共有する意味はここにあります。
コードではなく、前提条件を共有しています。

破られると何が起きるか

契約が守られないと、次が起きます。

  • ローカルでは動く
  • CIで落ちる
  • 本番だけ失敗

原因はコードではありません。
前提条件の不一致です。

特にありがちな行動:

  • lockをコミットしない
  • 本番でupdate
  • 手動でvendor編集

これは契約の破棄に近いです。

なぜComposerは厳密なのか

Composerはバージョン制約を細かく解釈します。

"library/package": "^2.3"

これは「2.3以上3.0未満」です。
曖昧ではありません。

一見面倒に見えますが、理由があります。

> 再現可能な開発を成立させるため

再現性がないと、バグ修正が成立しません。
同じコードを同じ条件で動かせないからです。

契約としての運用方法

意識すると変わる点があります。

installとupdateを分ける

  • install:日常作業
  • update:計画的変更

updateは「メンテナンス」ではありません。
仕様変更です。

変更はレビュー対象

composer.jsonとcomposer.lockの差分は
コードと同じようにレビュー対象です。

依存関係は実行コードに等しい影響を持ちます。

注意点:契約は自動で守られない

Composerは契約を記録しますが、
守るかどうかは人間次第です。

例えば:

composer install --no-dev

本番では必要ですが、
これを知らないと開発と本番がズレます。

ツールは契約を提示しますが、
運用が伴わなければ意味がありません。

まとめ:コードの前に前提がある

プログラムはコードで動いているように見えます。
しかし実際には前提条件の上に成立しています。

Composerはその前提を明示します。

  • 必要なPHP
  • 必要なライブラリ
  • 必要な構造

これを共有することで、
チームで同じソフトウェアを扱えるようになります。

Composerを「インストールツール」と考えると扱いを誤ります。
「契約書」と考えると、扱い方が自然に決まります。

そしてこの視点を持つと、
依存関係の管理は作業ではなく設計の一部だと理解できるようになります。