世界を動かす技術を、日本語で。

JavaScriptの膨張を支える三つの柱

2026年3月22日原文(43081j.com)

概要

  • e18eコミュニティ の拡大とパフォーマンス重視の貢献増加
  • 依存関係の肥大化 (dependency bloat)の主な原因を解説
  • レガシーサポート・原子的アーキテクチャ・Ponyfill の問題点
  • 不要な依存の削除 や代替モジュールへの移行の推奨
  • knipやe18e CLI などのツール活用による依存ツリーの最適化

依存関係の肥大化:主な3つの原因

  • 古いランタイムサポート、安全性、クロスレルム対応による小規模ユーティリティの乱立
    • 例: is-stringhasown など、本来ネイティブで対応可能な機能のためのパッケージ
    • ES3対応 のため、Array.prototype.forEachやObject.keysなどES5以降の機能を再実装
    • グローバル名前空間の改変防止 目的で、Nodeのprimordialsのような仕組みを模倣
    • クロスレルム値 (iframe間など)判定のための特殊な実装
  • 実際にはごく少数のケース にしか必要ないが、一般的なパッケージにも組み込まれている現状

原子的アーキテクチャの問題点

  • 機能を極限まで分割 し、再利用可能な小パッケージ群を構築
    • 例: arrify (値を配列化)、 slash (パス区切り変換)、 cli-boxes (ボックス罫線データ)
  • シングルユースパッケージ が多く、実質的にはインラインコードと同義
    • 例: shebang-regex はほぼ shebang-command のみで利用
  • 重複バージョン の発生や、 サプライチェーンの拡大 によるセキュリティリスク増大
    • 例: is-dockerpath-key など、同じ機能の異なるバージョンが依存ツリーに混在
  • 単純なロジック でも独立パッケージ化され、保守・セキュリティコスト増

Ponyfillの過剰残存

  • Ponyfill :環境を汚染せずに新機能を実現するための、インポート型ポリフィル
    • 例: globalthis (globalThisのponyfill)、 indexof (Array.prototype.indexOfのponyfill)
  • 本来は一時的な措置 だが、全エンジンでサポートされた後も残存
  • 不要なPonyfill が依存ツリーに残り続け、無駄な容量・複雑さの原因

解決策と推奨アクション

  • 「なぜこのパッケージが必要か?」を自問自答 し、不要なら削除・代替案を検討
  • 冗長な依存を発見したら、メンテナーに削除を提案
  • 直接依存に問題が多い場合は、よりシンプルな代替パッケージを検討
    • 参考: module-replacementsプロジェクト
  • knipの活用 による未使用依存・デッドコードの発見・削除
    • knipのドキュメント参照
  • e18e CLIのanalyzeモード で、不要または置換可能な依存を検出
    • 例: chalk のようなパッケージがネイティブ機能で代替可能か判定
    • 公式ドキュメントや推奨リンクを参照

まとめ

  • 依存関係の最適化 は全員のコスト削減と安全性向上に直結
  • ツールの活用「本当に必要か?」の意識 がクリーンなエコシステム構築の鍵
  • 少数の特殊なニーズ のための肥大化を防ぎ、シンプルで保守しやすい依存ツリーを目指す

Hackerたちの意見

一つだけ柱があるんだけど、社内のPWA用ね:Chromeの最新バージョンにアップグレードして、それでも問題が続くなら、こちらで調べるよ。

シンプルにするのが一番だよね。

これって、隠れた技術的負債みたいに感じるんだよね。みんながコンパイルターゲットをESxに更新してないし、パッケージも更新してないし、パッケージの作者も実装を更新してない。古いブラウザのサポートはあるけど、ES5はもう13年もどこでもサポートされてるしね(https://caniuse.com/es5によると)。

新しいバージョンは、さらに重くなることが多いよね。この全体の記事は、一般的に「JS開発者に何が起こってるんだ?」っていう私の意見を強化するだけだ。ほとんど無思考でトレンドを追いかけて、四角い車輪を再発明してる感じ。20年前にほとんどJSなしでできたことを振り返ると、どれだけ劣化してるかがわかる。

ES6、ましてやES5以前と互換性を保とうとする欲求が全く理解できない。2025年にnode 0.4との互換性を無邪気に維持したい人たちを見て、もっとひどいことになる可能性があると気づくよね。皮肉なことに、開発者たちはBabelを設定して古いバージョンにトランスパイルするけど、その出力は重くて(regeneratorのようなパスはオーバーヘッドが多いから実行も遅くなる)、最近のCSSプロパティやJS機能を使ってるせいで、想定されている古いブラウザでは全く動かないこともある。仕事でポリフィルが原因でプログラムが壊れたこともあった。確か、BigIntの入力を処理できないクソみたいなのポリフィルだった。

根本的な問題は、ウェブが「今出して後で直す」ことを報いるから、古い依存関係や保守的なターゲットが残って、壊れるまでそのままになってるってこと。

is-stringのようなパッケージのクロスレルムの議論は、私が一番無視しにくいものだけど、そこでも数字が合わない。実際に値をレルム間で渡しているプロジェクトはほんの少しで、そういうプロジェクトがクロスレルムセーフなユーティリティを取り入れるべきなのに、すべてのパッケージの下流の消費者がそれを考慮する必要はない。Pillar 2の深い問題は、原子的なパッケージは哲学的な議論としては意味があったけど、npmが簡単に公開できるようになった瞬間に崩れたこと。インセンティブは「すべてを公開して、消費者が必要なものを選ぶ」だったけど、現実は消費者がツリーを監査することはなく、ただインストールして忘れるだけ。だから、本来はオプトインのはずだったコストがデフォルトでオプトアウトになっちゃった。ポニーフィルの問題は、私にとって一番解決しやすい気がする。「すべてのLTSバージョンのNodeがこれをネイティブでサポートしてる?」っていうシンプルな自動チェックがあれば、大体の問題をキャッチできる。e18e CLIはいいスタートだけど、まだ誰かが意図的に実行する必要がある。古いポニーフィルを削除するPRを開くRenovateスタイルのボットがあれば、メンテナが気づくのを待つよりも早く進むかもしれない。

いい記事だけど、これらは全部マージナルだと思う。膨張の主な原因はポリフィルや原子的なパッケージじゃない。膨張の原因は膨張そのものだ!サン=テグジュペリ(『星の王子さま』の著者)のこの言葉が好きだ:「完璧は、何も足さないときに達成されるのではなく、何も引き算できないときに達成される。」ほとんどのソフトウェアはそんなふうに書かれてない。「どうやってもっとエレガントにできるか?」じゃなくて、「もっと簡単に物を追加する方法は?」って聞いてるんだ。答えはnpm i more-stuff

良い文章のためのヴォネガットのルール第4条:>「すべての文は、キャラクターを明らかにするか、行動を進めるかのどちらかをしなければならない。」またはキンティリアンのデモステネスとキケロへの称賛:「デモステネスには何も加えられないが、キケロからは何も引き算できない。」

すべてのソフトウェアには膨張があるけど、npmパッケージやウェブアプリは特にそれがひどいよね。これって言語に起因してると思う?JavaScriptは、過去と未来のブラウザで動くようにコードを作りたいから、互換性のために膨張が起こることが多いんだよね。記事にもあったけど、UI用の言語だから、アクセシビリティや国際化、モバイル対応などのためにアプリやフレームワークに膨張が生じることもある。

今は依存関係なしのJavaScriptを書くのがいいと思う。JS/CSSの標準ライブラリは素晴らしいし、静的解析(TypeScriptはJSDocをチェックできる)、インポート(ESモジュール)、UI(ウェブコンポーネント)もいい感じ。みんなが私のアプローチはスケールしないとか、メンテナンスが大変だって言うけど、私の経験では、依存関係が多いプロジェクトでは味わえないような、シンプルで変更しやすい状態が続いてる。

似たようなことをやってる。最近作ってるプロジェクトは、できるだけ依存関係を減らしてるんだけど、正直、メンテナンスの負担がかなり減ったよ。何かが壊れたときに、15層のnode_modulesを掘り下げるんじゃなくて、どこを見ればいいか分かるからね。スケールしないって言われたけど、逆にそうじゃなかった。

Hacker Newsで議論の続きを見る