Node.jsのpackage.jsonとComposer.jsonの思想差

package.jsonとcomposer.jsonは似ているが「目的」が違う

Node.jsのpackage.jsonとPHPのcomposer.json。
どちらも依存関係を書くJSONファイルで、見た目もかなり似ています。

しかし、実際に運用すると印象は大きく変わります。
結論から言うと、package.jsonは「アプリケーションの設定ファイル」、composer.jsonは「コード構造の契約書」に近いです。

同じパッケージ管理でも、想定しているプロジェクトの形が違うため、使い方を間違えると違和感や事故が起きます。

まず確認:両方とも依存関係を管理するファイル

それぞれの基本的な役割は共通しています。

依存関係の宣言

Node.jsではこう書きます。

{
  "dependencies": {
    "express": "^4.18.0"
  }
}

Composerではこうです。

{
  "require": {
    "monolog/monolog": "^3.0"
  }
}

どちらも「このライブラリが必要です」という宣言です。
インストールも似ています。

npm install
composer install

ここまではほぼ同じです。
しかし、ここから思想の差が出始めます。

最大の違い:JavaScriptは実行環境、PHPは配布コード

Node.jsはアプリケーションを組み立てる文化

Node.jsのpackage.jsonは、アプリケーションの中心にあります。
理由はシンプルで、Node.jsは「実行環境そのもの」だからです。

package.jsonには依存関係以外にも次が入ります。

  • 実行スクリプト
  • ビルド
  • テスト
  • Lint
  • フォーマット
{
  "scripts": {
    "dev": "node server.js",
    "build": "webpack"
  }
}

つまりpackage.jsonは、アプリの操作盤です。
開発者はnpmコマンドを入口として作業します。

PHPは配布コードとして動く文化

一方、PHPのcomposer.jsonは違います。
PHPはサーバーに配置された時点で実行されます。

Webサーバーが入口です。

  • Apache
  • Nginx
  • FPM

そのためcomposer.jsonに書かれる内容は、実行ではなく「読み込み規約」です。

{
  "autoload": {
    "psr-4": {
      "App\\": "src/"
    }
  }
}

つまりcomposer.jsonの中心はスクリプトではなくautoloadです。

autoloadが意味するもの

ここが最も重要な思想差です。

Node.jsでは必要なファイルを明示的に読み込みます。

const user = require('./User');

または

import user from './User.js';

一方、Composerは明示的に読み込みません。

require __DIR__.'/vendor/autoload.php';

これだけです。
以降、クラスは自動的に解決されます。

これは単なる便利機能ではありません。
コードの配置ルールを強制する仕組みです。

PSR-4という規約に従わないと、そもそもクラスが読まれません。
つまりcomposer.jsonは依存関係ファイルであると同時に、設計規約でもあります。

devDependencies と require-dev の違い

似ている項目としてよく比較されるのがここです。

Node.js:

"devDependencies": {
  "jest": "^29.0.0"
}

Composer:

"require-dev": {
  "phpunit/phpunit": "^10.0"
}

一見同じですが、運用の意味は少し違います。

Node.jsではビルドツールが多いため、devDependenciesは「開発ツール置き場」です。
本番ではビルド済み成果物を使うことが多く、node_modules自体をデプロイしないケースもあります。

一方、PHPはビルド成果物を配布しません。
本番もソースコードです。

そのためComposerでは「本番に入るか」が重要になります。
require-devを誤って本番に含めると、メモリ増加やパフォーマンス低下が起きる場合があります。

lockファイルの扱いが文化を表す

Node.js: package-lock.json
PHP: composer.lock

両方とも存在しますが、重要度の扱いが違います。

Node.jsでは削除しても比較的復旧できます。
しかしComposerでは影響が大きいです。

理由は依存解決の強さです。
Composerは間接依存まで厳密に固定します。

つまりcomposer.lockはキャッシュではありません。

> 「このプロジェクトが成立する唯一の依存状態」

です。
Git管理しないと、同じコードでも動作が変わります。

よくある事故:npm感覚でcomposer updateする

Node.js経験者がやりがちな失敗があります。

composer update

気軽に実行すると、次が起きます。

  • 間接依存が更新
  • フレームワーク内部が変わる
  • 既存コードが動かない

npm updateより影響が広いのが特徴です。
Composerは「プロジェクトの再解決」に近い動きをします。

まとめ:同じJSONでも役割は別物

package.jsonとcomposer.jsonは、形式は似ています。
しかし役割はかなり違います。

  • package.json:アプリケーション操作と開発フローの中心
  • composer.json:コード構造と依存関係の契約

Node.jsは「実行するための設定」、
PHPは「読み込ませるための規約」。

この違いを理解すると、なぜPHPではautoloadが重要視され、Node.jsではscriptsが中心になるのかが見えてきます。

同じパッケージ管理でも、ツールは言語文化を映します。
そしてcomposer.jsonは、PHPの「どう書くべきか」を定義するファイルでもあるのです。