pnpmが「速い」「軽い」「戻れない」と言われる理由は、設定やコマンドではなく node_modules の構造そのもの にあります。npm や yarn と同じ JavaScript パッケージマネージャーでありながら、pnpm は依存関係の扱い方を根本から見直しました。本記事では、pnpm の node_modules が何をどう変えたのかを、内部構造と設計思想の観点から解説します。表面的な比較ではなく、「なぜこの仕組みなのか」を理解することで、pnpm を使うべき理由が見えてきます。

従来の node_modules はなぜ問題だったのか

npm や yarn が採用してきた従来の node_modules は、「とにかく動く」ことを最優先に設計されてきました。その結果、多くの問題を抱えることになります。

代表的なのが 依存関係のフラット化 です。あるパッケージが内部的に依存している別のパッケージが、いつの間にか自分のプロジェクトから直接参照できてしまう。これは一見便利ですが、依存関係の境界を曖昧にします。

さらに問題なのが、以下の点です。

  • 同じパッケージがプロジェクトごとに何度もインストールされる
  • node_modules が巨大化し、ディスクを圧迫する
  • 「なぜ動いているのか分からない依存関係」が生まれる

結果として、壊れやすく、再現性の低い環境が出来上がります。


pnpm の node_modules はどう違うのか(全体像)

pnpm はこの問題を「運用」ではなく「構造」で解決しようとしました。

pnpm の最大の特徴は、実体のパッケージを node_modules 直下に置かないことです。代わりに、.pnpm ディレクトリにすべての実体を集約し、必要な場所にリンクを張ります。

pnpm の node_modules は、次のような二層構造になっています。

  • .pnpm:実体(パッケージ本体)が格納される場所
  • node_modules/パッケージ名:リンクのみが存在する場所

見た目は従来と似ていますが、中身はまったく別物です。


ハードリンクとシンボリックリンクの仕組み

pnpm がディスク容量を大幅に削減できる理由は、リンクの使い分けにあります。

  • .pnpm 内の実体:ハードリンク
  • 各プロジェクトの node_modules:シンボリックリンク

これにより、同じバージョンのパッケージは 1度だけダウンロード され、複数のプロジェクトで共有されます。

重要なのは、これは pnpm 独自の仕組みではなく、OS レベルのファイルシステム機能を活用している点です。ファイルシステムが同一であれば、複数のプロジェクトが同じ実体を安全に参照できます。

その結果、次のようなメリットが生まれます。

  • インストールが高速
  • ディスク使用量が少ない
  • キャッシュが自然に効く

なぜ pnpm は「幽霊依存」を許さないのか

pnpm が「厳しい」と言われる理由のひとつが、依存関係の扱いです。しかしこれは欠点ではなく、意図された設計です。

幽霊依存(Phantom Dependencies)とは、

package.json に書かれていない依存関係を、たまたま参照できてしまう状態

を指します。

npm や yarn ではフラット構造の影響で幽霊依存が起きやすく、pnpm では原則として発生しません。pnpm は「宣言されていない依存関係は存在しないものとして扱う」ことで、依存関係を 明示的かつ健全 に保ちます。

一時的にエラーは増えますが、長期的には壊れにくい構成になります。


pnpm の node_modules 構造を実際に見てみる

pnpm 環境の node_modules は、概念的には次のような構成になります。

  • node_modules/
    • react(シンボリックリンク)
    • lodash(シンボリックリンク)
    • .pnpm/
      • react@18.x.x/
      • lodash@4.x.x/

重要なのは、import / require の書き方は従来と変わらないことです。Node.js のモジュール解決アルゴリズムは、リンクであるかどうかを意識しません。

開発者は構造を知らなくても使えますが、理解しておくとトラブルシュートが圧倒的に楽になります。


pnpm を使うと何が変わるのか(実務視点)

実務で pnpm を使うと、次のような変化を実感します。

  • 初回インストール後の速度が非常に速い
  • CI のキャッシュサイズが小さくなる
  • 複数案件を扱っても環境が壊れにくい

特に monorepo や複数サイト管理では、「依存関係を共有できる」ことの恩恵が大きくなります。


pnpm が向いているケース・向いていないケース

向いているケース

  • 複数プロジェクトを並行して管理する
  • monorepo 構成を採用している
  • CI / Docker を重視する

注意が必要なケース

  • 古いツールが node_modules を直参照している
  • npm 前提で書かれた独自スクリプトが多い

ただし多くの場合、問題はツール側にあるため、pnpm を機に構成を見直す価値は十分にあります。


npm / yarn から pnpm に移行する際の注意点

移行時に注意すべき点は次の通りです。

  • lockfile に互換性がない
  • 幽霊依存がエラーとして顕在化する
  • CI のキャッシュパスを見直す必要がある

最初は戸惑いますが、直すべき依存関係が見えること自体が大きなメリットになります。


まとめ:pnpmは「速さ」より「健全さ」を選んだ依存管理

pnpm の node_modules は、単なる高速化のための仕組みではありません。依存関係を正しく定義し、壊れにくい環境を作るための設計です。

一度この構造に慣れると、「なぜ今まであんなに壊れやすかったのか」と感じるようになります。pnpm は速いから選ばれているのではなく、正しい依存管理を強制するから信頼されているのです。