10 ポイント 投稿者 GN⁺ 2026-03-23 | 2件のコメント | WhatsAppで共有
  • npmエコシステムでは、依存関係ツリーの肥大化が主要な問題として指摘されており、これは古いランタイム対応、原子的パッケージ構造、古いponyfillの使用に起因する
  • 旧式エンジン互換性とcross-realm安全性を理由に維持されている小さなユーティリティパッケージが、現代の環境でも不要なまま残っている
  • 原子的アーキテクチャは再利用性を高めようとしたが、実際には重複・セキュリティ・保守コストを増やす非効率な構造として機能している
  • すでにすべてのエンジンがサポートしている機能向けの古いponyfillパッケージが削除されず、不要なダウンロードと管理負担を招いている
  • コミュニティはe18e、knip、module-replacementsのようなツールを通じて、不要な依存関係の整理とネイティブ機能への移行を進めている

JavaScript依存関係肥大化の3つの軸

  • e18eコミュニティの成長とともに、性能重視のコントリビューションが増え、不要または保守されていないパッケージを整理するcleanup活動が進行中
  • npmエコシステムでは、**依存関係ツリーの肥大化(dependency bloat)**が主要な問題として指摘されており、古いランタイム対応、原子的パッケージ構造、古いponyfillの使用が主な原因とされる

1. 古いランタイム対応(安全性とrealmを含む)

  • npmツリーにはis-stringhasownのような小さなユーティリティパッケージが多数存在し、これは次の3つの理由で維持されている
    • 非常に古いエンジン(例: ES3、IE6/7、初期Node.js)のサポート
    • グローバル名前空間の改変防止

      • cross-realm値の処理
  • 古いエンジン対応

    • ES3環境にはArray.prototype.forEachObject.keysObject.definePropertyなどのES5機能が存在しない
    • このような環境では自前実装またはpolyfillの利用が必要
    • 最善の解決策はアップグレードだが、一部の利用者は依然として旧バージョンを維持している
  • グローバル名前空間の改変防止

    • Nodeは内部的にprimordialsの概念を使い、グローバルオブジェクトを初期時点でラップして改変から保護している
    • たとえばMapを再定義するとNode自体が壊れる可能性があるため、Nodeは元の参照を保持する
    • 一部のパッケージメンテナはこの方式を一般パッケージにも適用し、math-intrinsicsのような安全性重視パッケージを利用している
  • Cross-realm値

    • iframe間でオブジェクトを渡すとinstanceofチェックが失敗する問題が発生する
    • 例: window.RegExp !== iframeWindow.RegExp
    • chaiのようなテストフレームワークはObject.prototype.toString.call(val)方式でrealm間の型チェックを行う
    • is-stringのようなパッケージは、このcross-realm互換性のために存在する
  • 問題点

    • ほとんどの開発者はモダンなNodeやevergreenブラウザを使っているため、このような互換性は不要
    • それでもこれらのパッケージが一般的な依存関係ツリーの「ホットパス」に含まれ、全員がコストを負担することになる

2. 原子的(Atomic)アーキテクチャ

  • 一部の開発者は、パッケージを可能な限り小さな単位に分割し、再利用可能なビルディングブロックとして構成すべきだと主張する
  • その結果、shebang-regexarrifyslashpath-keyonetimeis-wslなど、極端に細分化されたパッケージが多数存在する
  • 例: shebang-regexは1行の正規表現だけを含む(/^#!(.*)/
  • 問題点

    • ほとんどの原子的パッケージは再利用されないか、単一の利用者しかいない
    • 例:
      • shebang-regexshebang-commandだけが使用
      • cli-boxesboxeninkだけが使用
      • onetimerestore-cursorだけが使用
    • この場合、インラインコードと同じでありながら、npmリクエスト、展開、帯域幅などの追加コストが発生する
  • 重複の問題

    • 例: nuxt@4.4.2の依存関係ツリーでは、is-dockeris-streamis-wslpath-keyなどがそれぞれ2つのバージョンで重複して存在する
    • インラインコードに置き換えれば、バージョン衝突や解決コストがなくなり、重複コストはほぼゼロになる
  • サプライチェーンリスクの拡大

    • パッケージ数が多いほど、セキュリティ・保守リスクが増大する
    • 実際に、あるメンテナが多数の小さなパッケージを管理していた結果、アカウントがハッキングされて数百のパッケージが同時に損なわれた事例がある
    • 単純なコード(Array.isArray(val) ? val : [val])は、別パッケージにする必要はなくインラインで処理できる
  • 結論

    • 原子的アーキテクチャは意図に反して、非効率で危険な構造へと変質した
    • ほとんどの利用者に実質的な利益がないまま、エコシステム全体がコストを負担している

3. 古いPonyfill

  • Polyfillはエンジンがサポートしていない機能を環境に追加するコードであり、 Ponyfillは環境を変更せず、直接importして使う代替実装を指す
  • 例: @fastly/performance-observer-polyfillはpolyfillとponyfillの両方を提供する
  • 問題点

    • Ponyfillは過去には有用だったが、対象機能がすでにすべてのエンジンでサポートされていても削除されない
    • 例:
      • globalthis(2019年からサポート、週4,900万ダウンロード)
      • indexof(2010年からサポート、週230万ダウンロード)
      • object.entries(2017年からサポート、週3,500万ダウンロード)
    • これらのパッケージの多くは、単に削除されていないため残っている
    • すべてのLTSエンジンが機能をサポートしているなら、ponyfillは削除されるべきである

肥大化解消の方法

  • 依存関係ツリーの深いネストにより整理作業は難しいが、コミュニティの協力で改善可能
  • 各開発者は「このパッケージは本当に必要か?」と自問し、不要であればissueを立てるか代替パッケージを探す必要がある
  • module-replacementsプロジェクトは、ネイティブ機能で置き換え可能なパッケージ一覧を提供する
  • knipの利用

    • knip未使用の依存関係とデッドコードを検出するツール
    • 直接的な解決策ではないが、整理の出発点として有用
  • e18e CLIの活用

    • @e18e/cli analyzeコマンドで置き換え可能な依存関係を検出できる
    • 例: chalkpicocolorsへ自動マイグレーション
    • 今後は環境に応じて、NodeのstyleTextのようなネイティブ機能の推奨も予定されている
  • npmgraphの活用

    • npmgraph.js.org依存関係ツリー可視化ツール
    • 例: eslint@10.1.0ツリーではfind-upブランチが孤立している
    • 単純なファイル探索機能に6個のパッケージは不要なため、empathicのようなより小さな代替を使える
  • module replacementsプロジェクト

    • コミュニティが置き換え可能なパッケージとネイティブ機能のマッピングデータセットを維持している
    • codemodsプロジェクトを通じて自動マイグレーションも支援する

結論

  • 現在の肥大化は、少数の古い互換性や特殊な構造を維持したい利用者のために全体がコストを負担する構造になっている
  • 過去には避けられなかったが、モダンなエンジンとAPIが十分に発展した今では不要な負担である
  • 今後はこの少数派が別個のスタックを維持し、残りは軽量でモダンなコードベースを使う方向へ移行する必要がある
  • e18enpmxのようなプロジェクトが文書化とツール整備を通じてこれを支援しており、 各開発者も自分の依存関係を点検し「なぜ必要なのか?」を問うべきである
  • みんなで一緒に整理できる

2件のコメント

 
click 2026-03-23

私もライブラリを作るときはまだ cjs ビルドを提供はしていますが、
2026年になっても esm の例すらなく、全部 require ベースのライブラリは、もう少しアップデートしてほしいとは思います。

 
GN⁺ 2026-03-23
Hacker Newsの意見
  • 最近は 依存関係のないJavaScript で開発するのが最善の方向だと思う
    JS/CSSの標準ライブラリも優秀だし、静的解析(TypeScriptのJSDocチェック)、ESモジュール、Web Componentsなども十分に強力だ
    人はこのやり方が拡張性や保守性に不利だと言うが、私の経験ではむしろ単純で変更しやすい構造を維持できた

    • 私も数年来このアプローチを試していて、plainvanillaweb.com というチュートリアルサイトも作った
      フレームワークやビルドツールがやっていることの大半は、ブラウザの組み込み機能と バニラパターン で代替できる
      ただしこうしたやり方はまだ馴染みの薄い領域なので、チュートリアルの大半が大規模フレームワーク中心で回っているのが問題だ
      実際、Reactのコードを完全にバニラへ移してもモジュール性は保たれ、コード量は約1.5倍になる程度で、依存関係がない分むしろ性能は良くなる
      もちろん依存関係が悪いという意味ではない。ただ多くの開発者が「必ず使うべきだ」という 固定観念 に囚われている
    • 単純なマーケティングページなら可能だが、機能の多いアプリなら依存関係が必須な場合も多い
      たとえば私は地図機能の多いサイトを作っているが、mapbox/maplibre/openlayers のような代替のないライブラリを使わざるを得ない
    • 2022年にこの方式でプロジェクトを進めたが、CVEやバージョン移行に関する問題はまったくなかった
      クライアントも移行コストを一銭も払わずに済んだ
    • コンポーネントのレンダリングは簡単だが、フレームワークの核心は モデルのリアクティブ更新 を提供することだ
      この記事 のように、モデル更新をどう処理しているのか気になる
    • 20年近くJSを扱ってきたが、結局は最小限の依存関係だけを置いて残りは自分で作るやり方に落ち着いた
      むしろ 大規模コードベース を少人数で維持するのが楽になった
      最近のツールのおかげで昔より自前実装がずっと簡単になり、agentic engineering とも相性が良い
  • 文章がうまく書かれていて、感情的にならずに問題を明確に説明している
    JSがまともな 標準ライブラリ を持てていないことが、この状況の一因だと思う

    • 最近のJS標準ライブラリはかなり充実してきたが、まだどんな機能が足りないと見ているのか気になる
    • 私はむしろ 激しい文章(rant) が好きだ。人々の感情だけでなく、その理由を理解する助けになる
  • 良い記事だが、問題の根本は 不要な追加(=bloat) そのものだと思う
    「完璧とは、これ以上加えるものがないときではなく、これ以上取り除くものがないときに達成される」というサン=テグジュペリの言葉を引用したい
    ほとんどのソフトウェアは「どうすればもっと優雅に作れるか?」より、「どうすればもっと簡単に追加できるか?」という問いで書かれている
    答えはいつも npm i more-stuff

    • カート・ヴォネガットの文章術のルールのように、「すべての文はキャラクターを示すか、行動を前進させるべきだ」という原則を思い出す
      デモステネスとキケロ の対比のように、これ以上削れないコードこそが良いコードだ
    • すべてのソフトウェアには不要な部分があるが、特にnpmパッケージとWebアプリはひどい
      JSは過去と未来のブラウザ互換性の両方を考慮しなければならず、UI中心の言語なのでアクセシビリティ・国際化・モバイル対応などで 肥大化しやすい
  • 多くの場合、これは隠れた 技術的負債 の問題に見える
    コンパイルターゲットをESxへ上げず、パッケージや実装を更新しないことが原因だ
    ES5はすでに13年間、すべてのブラウザでサポートされている(caniuse.com/es5

    • 実際には古いJSエンジンをサポートしようとする人と、無数の ミニパッケージ を作る人がいる
      どちらも自分たちの行動を機能だと考え、人気パッケージを多く維持している
      だから変わりにくい。ときどきコミュニティが批判するが、彼らにも彼らなりの論理がある
    • ES6以下との互換性を維持しようとする執着は奇妙に感じる
      Babelで旧版へトランスパイルするとコードは 肥大化して遅くなり、しかも古いブラウザではCSSやJS機能の制限で結局動かない
      しかも polyfill が問題を起こしたこともあった(BigIntを処理できなかった指数演算子のpolyfill)
    • 古いブラウザだけでなく、奇妙なブラウザ のサポートも必要だ
      コンソール、TV、旧型Android、iPod touch、Facebook内蔵ブラウザなど様々な環境が存在する
      だから外部モジュールは1つだけにして、残りはトランスパイラ設定で解決している
    • Webには「今デプロイして後で直そう」という文化が強く、古びた依存関係 が長く残る
    • Angularの設計判断のように、過去の構造が現在の 非効率 を招く場合もある
      昔は非同期追跡のためにsetTimeoutなどをオーバーライドしていたが、今は signals ではるかに単純に処理できる
  • 一部のパッケージ作者は ダウンロード数を増やすために 依存ツリーを人為的に細かく分割しているのだと思う
    7行のパッケージが存在するなんて話にならない。lockfileのメタデータの方がコードより大きい
    以前、create-react-appの依存関係の5%がある作者のミニパッケージだった
    has-symbols, is-string, ljharb のような例がある

    • こうした行為が単なる 自尊心 なのか、実際に利益があるのか気になる
      たとえばAnthropicはnpmダウンロード数の多いオープンソース保守者に 無料のClaude を提供している
    • immich.app/cursed-knowledge の記事のように、「下位互換性」を理由に50個のパッケージを追加する人もいる
    • セキュリティ面でも深刻だ。こうした マイクロパッケージ の一つひとつが攻撃面になる
      ダウンロード数競争はむしろリスクを増やす
    • 以前、ある人が組織に入り込んで自分のパッケージを依存関係に追加し、履歴書用のスター数 を増やした例もあった
    • 文化的な問題もある。JSコミュニティでは7行のコードを自分で貼り付けることを「車輪の再発明」だと批判する
      しかし他の文化では、それがむしろ良いことと見なされる
  • JSエコシステムを批判する前に 30 years of br tags を読むとよい
    JSとツールの 進化の過程 を理解できる
    単に「JS開発者が問題だ」と言うのは工学的思考の欠如だ

    • 悪い現実を理解しようとする姿勢は良いが、過度な受容 は敗北主義だ
      私たちは常により良い理論と実践を考えるべきだ
      ソフトウェアの世界は急速に変わるので、自分で「偽の葬式」を行って古い慣行を捨てる必要がある
    • この記事は開発者を責めるというより、現状を 理性的に批判 している文章だと感じた
  • 9年もののNode.jsコードベースを管理しているが、依存関係は8個しかなく、しかもすべて 下位依存なし
    Node組み込み機能を優先して使い、必要な部分だけ自分で実装している
    以前よりはるかに安定し、ストレスも少ない
    Denoの 標準ライブラリ も素晴らしく、ランタイムの基本機能と合わせれば数個のパッケージだけでも十分にアプリを作れる
    JSは 慎重に扱えばかなり良い言語

  • is-string のようなパッケージが主張する cross-realm安全性 は理解できるが、実際にそういう状況はまれだ
    npmがあまりにも簡単に公開を許したことで、「モジュールを分割して公開しよう」という哲学が 過剰拡大 したのが問題だ
    利用者は依存ツリーを監査せずにそのままインストールするので、任意のコストが基本コストになってしまう
    ponyfill の問題は自動化で解決可能だ
    たとえばNode LTSバージョンですでにサポートされている機能を自動検知して削除する Renovate風ボット が役立ちそうだ

  • 社内PWAの原則はただ一つだ:
    Chromeを最新版にアップグレード しろ。それでも問題があるならそのとき考える」

    • 内部向けならこれが正しいアプローチだ。会社が対応ブラウザを決めれば管理しやすい
      Safariの方がメモリを食わないのは理解するが、ポリシーとして統一する方が効率的だ
    • 単純に保つことが結局 最大の利益 をもたらす
  • 「ES3(IE6/7相当)まで対応しなければならない」という話は本当に理解しがたい
    セキュリティ上、銀行サイトでさえそんな旧式ブラウザはブロックすべきだ

    • こういうチームはたいてい2015年ごろに設定した ビルドツールをいまだに変えていない ケースだ
      Webpack、Babel、polyfillスタックをアップグレードするのは大仕事なので、そのまま放置する
      「壊れていないなら直すな」という文化だ
    • 実際にそうした旧版サポートを主張する 特定の人物 がいて、その人が多くの低レベルパッケージを保守している
    • ちなみに Deutsche Bahn がまだWindows 3.1を使っているという話も聞いた