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

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

概要

  • 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を掘り下げるんじゃなくて、どこを見ればいいか分かるからね。スケールしないって言われたけど、逆にそうじゃなかった。

これは絶対に正しい方向だね。

Node.jsのライブラリが必要なときは、まず依存関係がないオプションを探すよ。それがうまくいけば、ラッキーって感じ。

2022年にプロジェクトでこれやったけど、CVE関連でのトラブルは一切なし。バージョン間の移行で問題もなかったし、クライアントは移行作業に一銭も払ってない。

何年もこれを探求してきたし、依存関係なしでサイトやアプリを作るためのチュートリアルサイトも作ったよ(plainvanillaweb.com)。学んだことは、フレームワークやライブラリ、ビルドツールがやっていることの多くは、ブラウザの組み込み機能やバニラパターンで置き換えられるってこと。でも、今のところそういうやり方はあまり知られていない分野だと思う。YouTuberやチュートリアルプラットフォームのウェブ開発知識のエコシステムが、大きなフレームワークやツールに偏っているからだと思う。人々は、フレームワークやビルドツールなしで作るのは実際よりもずっと難しいと思っているし、結果的なウェブアプリのパフォーマンスが実際よりも悪いと思い込んでいる。典型的なReactのコードベースを完全にバニラなコードベースに移植すると、同じくらいモジュール化されて、コード行数は約1.5倍になるけど、依存関係がない分、全体のフットプリントは小さくて、パフォーマンスも良いことが多い。ただし、依存関係が悪いとか、全くメリットがないわけではないし、バニラコーディングが優れているとも言ってないよ。このやり方は時間がかかるし、結果的にコード行数も増えるし、ウェブコンポーネントはフレームワークコンポーネントより「見た目が悪い」こともある。言いたいのは、ほとんどのウェブ開発者が、これらの依存関係を使わなきゃいけないという考えに囚われているけど、実際にはそれはオプションで、必ずしも最良の選択ではないってこと。

最初のポイントについてだけど、パッケージの作者たちをもっと指摘していくべきだと思う。彼らは、GitHubのダウンロード数を稼ぐためだけに、自分のサブパッケージに依存関係を作り上げてるんだよね。マジで… 5,000万ダウンロードもあったら、もうちょっとちゃんとしたものを作れよって感じ。コードがたったの_7行_のパッケージなんて存在するべきじゃない!ロックファイルのメタデータの方が、このコードのミニファイド版よりも大きいんだよ!昔、create-react-appの依存関係リストの5%くらいが、一人の作者の作った小さな依存グラフから来てたことがあった。その人は自分のGitHubページにダウンロード数も載せてたけど、最近はメインのエントリーポイントをちゃんと修正したみたいで、よかった。 https://www.npmjs.com/package/has-symbols https://www.npmjs.com/package/is-string https://github.com/ljharb

50億個のパッケージを持ってるSindreに感謝。彼のパッケージの中で、他のパッケージに依存してるのは少ないけどね。

なんか、ある一人の男がghの組織に潜入して、自分のパッケージを依存関係に追加して、履歴書やスター数を増やそうとしてたのを見たことがある。誰だったか全然思い出せないけど。

GitHubのダウンロード数を稼ぐためだけに これって自己顕示欲なのか、それとも実際に何か利益を得てる人がいるのか?Anthropicは最近、Xスター以上かYダウンロード以上のオープンソースのリポジトリのメンテナーに無料のClaudeを提供したんだ。ダウンロード数が経済的な利益に繋がる可能性は十分にあると思う…

セキュリティの観点から見ると、これって見た目以上に悪いことなんだ。あのマイクロパッケージ一つ一つが攻撃の対象になるからね。今日、trivyのサプライチェーンが侵害されたのを見たばかりで、それがセキュリティツールだっていうのに。誰も監査しない7行のパッケージに何かを忍ばせるのがどれだけ簡単か想像してみて。「ただのユーティリティ」だからね。ダウンロード数のインセンティブがあるから、むしろ危険度が増して、パッケージが増える一方なんだよ。

いつものように、彼は何年も前からやってる誰かを真似してるね。https://www.npmjs.com/package/is-number - それから、is oddやis evenみたいなパッケージもある(はい、別々のパッケージだよ。だって、ブール値の否定された値をどうやって取得・比較するかなんて、誰が覚えてるの?)正直、ここ15年でJavaScriptが注目されてきたのに、その型システムがどれだけひどいかは信じられない。唯一の型に関する「改善」はclassキーワードの追加だけど、どうやら「% 2」を理解できない人たちが、プロトタイプ継承も理解できないみたいだね。

いつものことだけど、ここには文化的な問題があるね。あの7行のコードをアプリに貼り付けるのは全然可能だって知ってる。でも、多くの開発文化ではそれが良いこととされてる。JavaScriptの人たちと仕事してるなら、これを「車輪の再発明」とか「自分で作る」とか言ったり、「これはベストプラクティスに反する」とかのバリエーションで呼ぶよ。

「アトミックアーキテクチャ」と小さなパッケージの話で一番イライラするのは、その明らかにバカげたところだよね。普通の人ならisOdd/isEvenを見て、ひどいアイデアだってわかるはずなのに、それを文化的な柱に持ち上げて、素晴らしいイノベーションだと思ってる。まるで反ワクチン派と話してるみたい。

ジュニアの中には、そんな感じのリソニコードを書いてる子もいるよ。大きな関数は書かない方がいいって聞いて、無理やり分割して、もう分割できないところまで行っちゃうんだよね。

名前が賢そうに聞こえるからだよ。浅はかでパフォーマンス重視な人もいるし、見た目の良いブログ記事で「原子アーキテクチャ」ができるって言われたら、みんながそれに飛びついて、自分がどれだけ賢いか見せたがるんだ。

初期の頃はその哲学がちょっと新鮮だった。発表のハードルがすごく低くて、物を貯め込むんじゃなくて、ツールを作って共有することが奨励されてた。おそらく、npmやNodeエコシステムの成功にも少しは関係してたと思う、特に貧弱な標準ライブラリを考えるとね。もちろん、ほとんどのことと同じで、極端に行くとおかしなことになるし、isOddみたいなのが生まれちゃう。

フォールバックサポートは、バンドルに追加のコードが含まれる正当な理由だけど、それは必要だから「膨張」ではないよ。理想的な世界では、すべてのウェブサイトがES5、ES6、ES2025のバンドルを生成して、ブラウザの能力に基づいて必要な最小限のものを提供するけど、それを正しくやるのは本当に難しいし、間違えるとアプリが壊れるから、開発者がやらないのも理解できる。他の2つ、アトミックアーキテクチャとポニーフィルは、単に開発者の経験不足(または怠惰)だよね。パッケージのソースを見て、本当に必要かどうか考えてないなら、ちゃんと仕事できてないってことだし。過去に追加したコードが、訪問者が使ってるブラウザのメトリクスを見て必要ないってわかったら、ちゃんとメンテナンスして削除しないと。それじゃユーザーを第一に考えてないから、ダメだよ。

ブロートは主にパッケージの作者が追加するもので、ウェブサイトの作者じゃないよ。誰がそれを使っているかも分からないし、メトリクスも見れない。多くのウェブサイトの作者がisEvenやポリフィルを直接使っているとは思えないな。

よく書けた記事だね。問題をうまく説明しながら、愚痴っぽくならないのがいい。状況の一因は、JavaScriptが常に「原子アーキテクチャ」スタイルのパッケージを含む標準ライブラリが欠けていることだと思う。(もちろん、標準ライブラリがすべてを解決するわけではないけど。)

でも、愚痴は好きだよ。

e18e CLIを使って置き換え可能な依存関係を検出する https://github.com/e18e/cli それ、すごいね!エージェントのためにコミット前にフックして、移行作業をやらせることができそう。