HN 日本語サマリー

← 一覧へ戻る
Web開発

JavaScriptフレームワークのAIコスト

The AI cost of JavaScript frameworks (nolanlawson.com)

44 pointsby luu22 コメント

要約

この記事では、AIコーディングエージェントがJavaScriptフレームワークを使用してアプリケーションを構築する際のコスト、品質、保守性を比較するベンチマーク実験の結果を報告しています。実験では、PokéAPIを使用したシンプルなポケモンアプリを複数のフレームワークで構築し、開発時間、トークン消費量、コード行数、Lighthouseスコアなどを評価しました。結果として、フレームワークによって顕著な違いが見られ、特にコストやパフォーマンスにおいて、MPA(マルチページアプリケーション)がSPA(シングルページアプリケーション)を上回る傾向が示されています。

全文翻訳

JavaScriptフレームワークのAIコスト

ウェブ開発コミュニティにおける私の長年の不満の一つは、私たちが下す多くの決定が「雰囲気」に基づいていることです。フレームワークは「驚異的に高速」で、ツールは開発を「スーパーチャージ」し、適切な抽象化はあなたとユーザーの両方に利益をもたらす(DXとUXを一度に!)。このような熱狂的な自慢話は、通常、証拠ゼロで提供されます。Alex Russellはこれを「フロントエンドの失われた10年」と呼び、Baldur Bjarnasonはプログラミングをポップカルチャーと呼び、私は私のブログで事実とベンチマーク(少なくとも私の最善の努力で)を用いてこの傾向に対抗しようとしてきました。

長い間、私たちのコミュニティの証拠に対するアレルギーは、より良いDXへの欲求によって駆動されているように見えました。つまり、開発者はReactでJSXを入力するのが単に好きだっただけで、「仮想DOMは高速だ!」といったReactや関連ツールの宣伝は、それらの初期の直感を正当化するための事後的な合理化に過ぎませんでした。(Jonathan Haidtもこの評価に同意するでしょう。)

しかし、エージェント時代になり、開発者はもはや手作業でコードを入力しなくなり、DXによって制約されなくなったにもかかわらず、私は依然として雰囲気が支配的であるのを目にします(「雰囲気コーディング」の意味合いだけでなく)。あるフレームワークが「エージェントにとってより良い」という理由でフロントエンドが書き直されています。(エージェントに尋ねた人はいますか?)結果として得られるコードは「より高速」になり、エージェントは「より少ない間違い」を犯すようになります。ここでも、これらの主張はしばしば証拠なしになされます。これらの説明を読むたびに、「ベンチマークはどこにあるの?」と思います。

そこで私は好奇心から、ベンチマークを作成しました。このベンチマークの構造は非常にシンプルです:

  • PokéAPIを使用して、小さすぎず大きすぎないポケモンアプリを設計します(私の過去の仕事やこちらを追っている人なら驚かないでしょう)。
  • エージェントに、特定のJavaScriptフレームワーク/メタフレームワーク(例:Next.js、Nuxt、SvelteKitなど)でアプリを構築するように指示します。
  • 結果を仕様適合性スイートでテストします(エージェントの不正行為を防ぐため)。
  • 開発時間、消費トークン数、コード行数、およびLighthouseでのパフォーマンスとアクセシビリティスコアの観点から結果を評価します。

アプリはかなりシンプルです:タイプ、技、さまざまなゲームからのデータを持つポケモンのリストがあります。検索やフィルタリングなどができます:

[画像: 様々なモンスターのリスト、タイプなどが表示されているポケモンのウェブアプリのスクリーンショット]

いくつかの注意点を最初に述べるべきです:これは完璧な実験ではありません。このアプリは単一のホロタイプを表しています – インタラクティビティがほとんどない、比較的コンテンツベースのサイトであり、FigmaやGMailなどよりもWikipediaに近いものです。とはいえ、何かアプリを選んで構築する必要があり、これは少なくとも高レベルで理解しやすく、仕様化してテストするのが比較的簡単でした。

公平を期すために、各フレームワーク/メタフレームワークには公式のMCPまたはスキルを使用させました。これは、「このフレームワークはトレーニングデータであまり代表されていないから、MCPを構築した、人々がそれを使うことを期待している」といった非難を避けるためです。また、正確なアーキテクチャ(例:SSR対CSR)や、アプリを可能な限りパフォーマンスが高くなるように指定しませんでした。単にOpus 5.5に高い努力を指示し、最新バージョンの指定されたフレームワークを使用するように伝えました。各エージェントはサンドボックス内で実行され、Lighthouseでパフォーマンスとアクセシビリティをテストされることを知らずに実行されましたが、適合性テストスイートは提供されました。

CSSの方法論については、Tailwind、Bootstrap、またはその他のコンポーネントフレームワークの使用を避け、視覚的な一貫性を確保するための仕様トークンのセットに基づいて設計すること以外は、何も義務付けませんでした。CSS自体ではなく、JSフレームワーク、CSR対SSR、SPA対MPAなどについてテストしたかったのです。結果として得られるアプリは、特定のHTML構造を義務付けることを避けるために、生のDOMではなくアクセシビリティツリーに基づいた適合性スイートでテストされました – あるフレームワークはシャドウDOMに依存するWebコンポーネントを使用するため、これは必要でした。

この実験から学んだことの一つは、私が測定しているすべてが非常に確率的であるということです。LLMはランダムな単語生成器なので、時々ダイスロールが低く出て、エージェントが奇妙な方向に逸れて多くのトークンを無駄にすることがあります。これはフレームワークやそのツールのせいではありません。Lighthouse自体も一貫性のない結果を出すことがあります:Largest Contentful Paint (LCP) は、ある画像を「最大の」要素と判断し、次の実行では別の画像を選択するかもしれません。完全に公平な実験を行うのは難しいですが、複数のイテレーションを実行したことで役立ちましたし、変動性については正直に伝えようと努めました。

これらの前置きをすべて終えたところで、結果に移りましょう。

結果

コスト、品質、保守性の複数の次元に分けて説明します。各フレームワーク/メタフレームワークについて、5回の実行の中央値を取りました。つまり、同じアプリを同じフレームワークで5回構築しました。(ええ、これが最近のコーディングエージェントでできるクレイジーなことの一つです。)

例外はAngularで、2回動作するアプリを作成できませんでした。そのため、3回のイテレーションで代用しました。(詳細は後述。)

コスト

実行あたりのコスト(米ドル)

フレームワークコスト ($)
Hono+htmx$2.67 (2.23–3.19)
Astro$2.76 (2.16–3.63)
Nuxt$2.82 (2.39–4.25)
Vanilla$2.98 (2.24–3.37)
Next.js$3.14 (2.58–4.13)
Qwik$3.27 (2.35–3.80)
TanStack Start$3.54 (3.11–3.96)
SolidStart$3.79 (2.84–4.81)
React Router$3.81 (2.82–3.97)
SvelteKit$4.28 (3.59–4.65)
Gracile+Lit$4.65 (3.92–5.07)
Angular$4.97 (3.19–6.82)

3〜5回の実行の中央値。

並べ替え テーブルとして表示

これはAPI価格をClaude Codeから直接引用したもので、実際に私が費やした金額を表すものではありません。私はMaxプランを利用しているので、月額100ドルの全体の一部でした。

変動性にも注意してください:React Routerの中央値はHono+htmxの中央値より約1ドル安いですが、最も安いReact Router($2.82)は最も高価なHono+htmx($3.19)よりも安いです。しかし、いくつかの信頼できるパターンが現れています。

最も明確なのは、SvelteKit、Gracile+Lit、Angularが他のフレームワークよりも構築コストが高いことです。SvelteKitの場合、これはエージェントが実行ごとにSvelteKit v3移行ガイドを読んだためかもしれません。これは、Opus 5.5のトレーニングデータには新しすぎるためでしょう。Gracileも同様で、エージェントはフレームワークをよく知らず、Gracileリポジトリをクローンするか、node_modulesからコンパイルされたソースを読みました。Angularの場合は、コード全体のサイズ(下記参照)が原因のようです。

すべてのエージェントがMCP/スキル/Claude Codeプラグインを利用したわけではなく、すべてのフレームワークにそれがあったわけでもないことに言及すべきです:

AIツールの使用状況を見るにはクリック

フレームワークインストール済みエージェントによって使用されましたか?
AngularAngular MCP および Angular agent skills5回のうち3回でMCP、4回のうち5回でスキルツール、2回のうち5回でスキルファイル
AstroAstro MCPサーバー5回のうち2回でMCP
Gracile+LitN/AN/A
Hono+htmxHonoスキル5回のうち5回でスキルツール、5回のうち5回でスキルファイル
Next.jsnext-devtools-mcpおよびnext-dev-loopスキル5回のうち1回でスキルファイル、2回のうち5回でAGENTS.md
NuxtNuxt MCPN/A
QwikN/AN/A
React RouterReact Routerスキル5回のうち5回でスキルツール、5回のうち5回でスキルファイル
SolidStartN/AN/A
SvelteKitSvelte Claude Codeプラグイン5回のうち5回でMCP
TanStack StartN/A5回のうち2回でスキルファイル
VanillaN/AN/A

例えば、TanStack Startのエージェントは5回のうち2回しかそのスキルファイルを発見しませんでした(プロジェクトのnode_modulesに出荷されています)。Nuxtの場合、MCPはエージェントによって一度も使用されませんでした。Next.jsの場合、エージェントは5回のうち1回しかスキルファイルを読み込まず、2回しかAGENTS.mdを読み込みませんでした。これは結果の範囲が広く、LLMの確率的な性質を再び示しています。しかし、私はこれを有効な実験と見なしています。なぜなら、多くの雰囲気コーダーがMCPの使用を大文字で叫んでいるとは考えにくく、多くの人が完全にスキップしている可能性が高いからです。

また、Hono+htmxをどのように思いついたのか疑問に思っているなら:基本的に、htmxを何らかの形で表現したかったのです。そしてHonoは良い組み合わせだと思いました(つまり、Rails/Django/etc.のJavaScript代替として)。Gracile+Litについては、Webコンポーネントをその標準的なフレームワーク(Lit)で表現したかったのですが、メタフレームワークとしてはGracileしか知りませんでした。より良い候補があれば、それらはフォローアップ実験に最適でしょう。

「Vanilla」については、エージェントは指示で「UIフレームワークなしのバニラJavaScriptまたはTypeScriptとViteを使用する」ように指示されたことに注意してください。驚くべきことに、これはいくつかのフレームワークを下回っており、何らかのフレームワークがあることは、ないこと(あるいは生のViteよりは良いこと)よりも良いことを示しているようです。Viteが選ばれたのは、多くのフレームワークの基盤となっており、かなり標準的で中立的なJavaScriptインフラストラクチャを表しているからです。

合計時間とトークンはコストとほぼ一致しています:

実行時間

フレームワーク時間(分)
Astro8.2 分 (6.2–9.7)
Hono+htmx8.6 分 (5.9–9.6)
Nuxt9.0 分 (8.0–17.6)
Vanilla10.1 分 (6.9–18.6)
TanStack Start10.3 分 (8.3–13.9)
Qwik10.7 分 (7.1–12.9)
SvelteKit10.8 分 (8.6–13.8)
Next.js10.9 分 (8.3–11.9)
SolidStart11.2 分 (8.2–15.9)
Gracile+Lit12.1 分 (10.8–12.9)
React Router12.5 分 (8.4–13.6)
Angular17.2 分 (9.8–23.7)

エージェントの視点からの壁時計時間。3〜5回の実行の中央値。

並べ替え テーブルとして表示

トークン数(実行あたり、百万単位)

フレームワークトークン (M)
Nuxt3.85 M (3.16–8.18)
Astro4.13 M (3.03–7.57)
Hono+htmx4.26 M (3.40–5.61)
Next.js4.99 M (3.54–8.82)
Vanilla5.00 M (3.44–6.32)
Qwik5.64 M (3.41–7.03)
TanStack Start7.07 M (5.85–7.99)
SolidStart7.13 M (5.02–10.07)
React Router7.58 M (4.55–8.18)
SvelteKit8.53 M (6.79–9.29)
Gracile+Lit9.04 M (7.36–11.10)
Angular9.76 M (4.92–14.94)

キャッシュ読み取り、キャッシュされていない入力と出力。3〜5回の実行の中央値。

並べ替え テーブルとして表示

ちなみに、トークンはClaude Codeによって報告されました。期間は単なる総壁時計時間です。

品質

もちろん、結果が重要だと考えているので、Lighthouseでもテストしました。ソフトウェア開発者として、私たちは物事を安価に構築するだけでなく、パフォーマンスが高く、アクセスしやすいものにすることも重要だと考えています。

まず、私のベンチマークに関する2つの注意点を述べます:

  • ポケモンアプリは小さく、大規模で複雑な実際のアプリケーションを表しているわけではありません。
  • LighthouseとCore Web Vitalsは一般的にSPAに不利なので、MPAフレームワークはTotal Blocking Time (TBT)、JavaScriptバイト数などでほぼ確実に勝利します。アプリ内パフォーマンスメトリックを追加することも検討しましたが、複雑さと変動性が増すため断念しました。これらは私が発明したメトリックであり、業界標準ではありません。

簡単にするために、ホームページのスコアのみを報告します。他のページもありますが、それらはすべて比較的似たようなスコアを出すか、LCPのようなメトリックで激しい変動を示すため、含めると物事が複雑になりすぎるだけです。

要するに、パフォーマンス、アクセシビリティ、CLS(Cumulative Layout Shift)のLighthouseスコアはほぼ飽和しており、フレームワークによる変動はあまりありません:

フレームワークCLSパフォーマンスアクセシビリティ
Angular0.00098100
Astro0.000100100
Gracile+Lit0.000100100
Hono+htmx0.000100100
Next.js0.00099100
Nuxt0.000100100
Qwik0.000100100
React Router0.000100100
SolidStart0.000100100
SvelteKit0.000100100
TanStack Start0.00099100
Vanilla0.11197100

より興味深い数字を得るためには、他のメトリックを見る必要があります。最も信頼できるのは転送されたJavaScriptバイト数で、これはクライアントサイドフレームワークのサイズにほぼ対応します:

転送されたJavaScriptバイト数

フレームワークJS (KiB)
Astro0 KiB (0–1)
Vanilla7 KiB (6–7)
Gracile+Lit8 KiB (1–13)
Hono+htmx18 KiB (18–18)
SvelteKit36 KiB (35–36)
SolidStart40 KiB (38–40)
Qwik40 KiB (40–41)
Nuxt84 KiB (83–85)
Angular93 KiB (81–102)
React Router115 KiB (115–119)
TanStack Start118 KiB (112–119)
Next.js142 KiB (136–143)

Lighthouseによって報告。3〜5個のアプリの中央値。

並べ替え テーブルとして表示

Total Blocking Time (TBT) は、JavaScriptバイト数だけの関数ではないという点で興味深いです。一部のアプリは他のアプリよりもメインスレッドで多くの時間を費やしています。とはいえ、数字はすべて非常に小さいです:

Total Blocking Time (TBT)

フレームワークTBT (ms)
Astro0 ms (0–0)
Gracile+Lit0 ms (0–0)
Hono+htmx0 ms (0–0)
SvelteKit0 ms (0–1)
Vanilla0 ms (0–0)
TanStack Start1 ms (0–36)
Qwik3 ms (0–30)
SolidStart11 ms (0–12)
React Router17 ms (0–52)
Nuxt40 ms (14–54)
Next.js71 ms (66–121)
Angular103 ms (93–123)

Lighthouseによって報告、シミュレートされたモバイルスロットリング。アプリごとに5回のLighthouse実行の中央値、その後アプリごとに3〜5回の実行の中央値。

並べ替え テーブルとして表示

Largest Contentful Paint (LCP) は、前述のメトリック自体の問題により非常に変動しやすいです。しかし、予想通り、SPAには概ね罰を与え、MPAを報酬します:

Largest Contentful Paint (LCP)

フレームワークLCP (ms)
Astro810 ms (792–1662)
Gracile+Lit956 ms (796–980)
Hono+htmx978 ms (977–1662)
Qwik979 ms (954–1278)
SvelteKit1130 ms (1129–1131)
SolidStart1204 ms (1203–1278)
Vanilla1667 ms (764–1676)
Nuxt1727 ms (1427–1844)
React Router1729 ms (1729–1770)
Angular1954 ms (1351–1955)
TanStack Start2028 ms (1805–2104)
Next.js2090 ms (1028–2187)

Lighthouseによって報告、シミュレートされたモバイルスロットリング。アプリごとに5回のLighthouse実行の中央値、その後アプリごとに3〜5回の実行の中央値。

並べ替え テーブルとして表示

Next.jsの数値の激しい変動は、あるアプリが画像の遅延読み込みを避け、他の4つ や これ らはそうしなかったことによって説明できます。AstroとVanillaも同様の変動があり、これは優先順位付けやLCPの癖が原因のようです。

一般的に、このメトリックがクライアントサイドJavaScriptの実行後(そしておそらく複数のスロットリングされたラウンドトリップでポケモンデータを取得した後)にペイントするSPAよりも、HTMLで迅速にペイントするMPAを報酬する傾向があるのは驚くことではありません。変動性を無視すると、ここでのLCPは主にJavaScriptバンドルサイズの関数です。(Qwikはここで注目すべき例外です:技術的にはクライアントサイドルーティングを使用するSPAですが、MPAフレームワークの中でスコアされています。)

ただし、一部のフレームワークはクライアントとサーバーのどちらに重点を置くか、あるいはクライアントサイドフレームワークを使用したかどうかで異なりました。例えば、Gracileエージェントは2回の実行でLitを使用しませんでした。「Vanilla」の場合、エージェントは(Viteを使用するように指示された以外は)完全な自由度がありましたが、5回すべてでSPAを構築することを選択しました。

SSR対CSRも、Angularのトラブルの一部でした:2回の実行で、エージェントはSSRアプリを構築しましたが、それはlocalhost以外からのリクエストを拒否しました(指示にはリバースプロキシの後ろにアプリが配置されると明記されていました)。私はこれを仕様への違反と見なし、失敗した2回のイテレーションを再実行しないことにしました。

保守性

保守性を測定する方法はたくさんあります(サイクロマティック複雑度のような高度なものを含む)が、シンプルに保つために、実績のあるものを報告することにしました:総コード行数。

コード行数

フレームワークコード行数
Hono+htmx1370 (1264–1489)
Nuxt1415 (1382–1458)
Astro1461 (1379–1503)
Vanilla1492 (1437–1742)
Next.js1539 (1452–1627)
React Router1564 (1421–1621)
SvelteKit1611 (1503–1816)
SolidStart1630 (1528–1894)
TanStack Start1697 (1506–1771)
Gracile+Lit1717 (1412–1748)
Qwik1744 (1646–1827)
Angular2009 (2003–2051)

空でないソースファイルの行数。3〜5個のアプリの中央値。

並べ替え テーブルとして表示

私にとって、これは最も興味深いチャートの一つです。ある意味、フレームワークの目的全体は、保守する必要のあるコード量を減らすことです。Rich Harrisの記憶に残る言葉を借りれば:

「フレームワークはコードを整理するためのツールではありません。それらは心を整理するためのツールです。」

– Rich Harris, 「リアクティビティの再考」

エージェント時代においては、これはさらに重要になります。なぜなら、コードサイズは最終的に将来の編集も含めたトークン数に影響し、コードベースを長期的に保守する実際のコストに影響するからです。これは、簡潔さだけが重要な価値であるという意味ではありませんが、Hono+htmxがAngularとほぼ同じことを約70%のコードで(しかもより高速に)実現できるのを見るのは間違いなく興味深いです。

結論

このベンチマークを改善できる方法はたくさんあります。例えば、サービスワーカーを使用してオフラインキャッシングを義務付けたり、ポケモン画像を最適化したり、遅延読み込み、fetchpriority、Speculation Rules APIなどを使用したりできます。これらのパフォーマンスハックの多くを知っていても、意図的にエージェントをそちらに誘導しませんでした。なぜなら、それは典型的な雰囲気コーダーが行うことではないからです。

一方で、このベンチマーク全体は、すべてのHTMLファイルを静的アセットとして事前に生成するだけで不正行為が可能です(ポケモンの数はそれほど多くありません)。私は指示でこれを明確に禁止し、テストハーネスで明示的なチェックを行いました。なぜなら、それはベンチマークの精神に反すると感じたからです。(私の目標は、大量の動的な可能性のあるデータをシミュレートすることであり、純粋な静的サイトよりも挑戦的です。)

ベンチマークの実行方法も改善できます。より多くのイテレーションを実行したり、異なるコーディングエージェントを使用したり、異なる努力レベルを試したり、異なるフレームワークやフレームワークの異なるバージョンを使用したり、MCP/スキルあり/なしでテストしたり、Chromium以外のブラウザでテストしたり、異なる種類のアプリを構築したりするなどです。これらのことをしなかったのは、主に時間の都合上です。しかし、誰かが自分でベンチマークを実行したり、フォークしたりしたい場合は、GitHubで公開しているので、自由にどうぞ。あなたの結果をここに報告します(見つけた間違いや欠陥も含めて)。

私の目標は、「私は雰囲気コーダーですが、どのフレームワークを選ぶべきか?」という究極のベンチマークを書くことではありません。この記事のポイントは、AIコーディングの時代においても、雰囲気にではなく証拠に基づいて意思決定を行うことが可能であることを示すことです。ベンチマーク自体は、コードを確認しましたが、大部分は雰囲気でコーディングされています。もし間違いがあれば、それは私のものと見なしてください。競合するフレームワークが私の行ったことを改善しようとすることも大歓迎です。

また、特定のフレームワークを非難するつもりもありません。ベンチマークを実行する前に、どのフレームワークが「勝つ」かは知りませんでしたが、MPAがSPAを上回ると予想していました(これはこの記事全体で喜んで認めてきました)。よりSPA寄りのベンチマークを書くことも certainly できます。例えば、アプリを複雑なダッシュボード、ビデオゲーム、生産性アプリなどにすることで。異なるメトリックを測定することもできます。LighthouseとCore Web VitalsはSPAにかなり厳しいので、アプリ内のインタラクティビティを測定することもできます。

フレームワークの作者自身も、フレームワークやAIツールを改善して、このベンチマークでより良い結果を出すことができると確信しています。例えば、現在のMCPやスキルのセットが、良いことよりも害をなしている可能性が高いという強い疑念があります。したがって、もしこの投稿がこれらのフレームワークのパフォーマンスに関する最後の言葉となるなら、私は大いに失望するでしょう。また、このようなベンチマークで良い結果を出そうと躍起になっている新興フレームワークもきっとあるはずです。

何よりも、この記事が、この奇妙な新しい雰囲気コーディングの時代において、JavaScriptフレームワークの選択にさらなる精査を適用するきっかけとなることを願っています。あなたは本当に証拠に基づいて技術スタックを選んでいますか?それとも、すでに知っているものを選んでいるだけですか?特に、まだ自分でコードを読み、レビューし、デバッグしているなら、それは確かに理解できます。しかし、もしあなたがほとんど手を離しているなら、あなたがかつて動物のように一行一行コードを書いていた頃にした選択に、本当にどのような愛着があるのでしょうか?ソフトウェアの構築方法全体を再評価しているなら、npm installの後に何を入力するかを再評価する価値があるかもしれませんね?言っておきますが。

個人的には、上記のフレームワークのいずれにも愛着はありません。ただし、ユーザーのために可能な限り最高の体験を構築するよう努めるべきだと信じています。私は上記のフレームワークの多くの作者を知っており、尊敬しており、彼らが私が提唱するのと同じ価値観のために精力的に働いていることを知っています。私の希望は、コミュニティ全体を、次世代のWebアプリを支える、より良く、より速く、よりアクセスしやすいフレームワークへと、より良い方向へ押し進めることです。

追加情報

テストで使用されたフレームワークのバージョン(Qwikはテストの途中でパッチリリースがありました):

フレームワークバージョン
Angular@angular/core 22.2.3
Astroastro 7.4.2
Gracile+Lit@gracile/gracile 0.11.0, lit 3.3.3
Hono+htmxhono 4.13.14, htmx.org 2.0.11
Next.jsnext 16.4.3, react 19.3.1
Nuxtnuxt 4.6.2, vue 3.5.44
Qwik@builder.io/qwik-city 1.20.2 (iteration 1), 1.20.3 (iterations 2, 3, 4, 5), @builder.io/qwik 1.20.2 (iteration 1), 1.20.3 (iterations 2, 3, 4, 5)
React Routerreact-router 8.5.0, react 19.3.1
SolidStart@solidjs/start 2.1.0, solid-js 1.9.17
SvelteKit@sveltejs/kit 3.1.0, svelte 5.57.2
TanStack Start@tanstack/react-start 1.169.4, react 19.3.1
Vanillavite 8.3.5

あなたはFediverseでコメントできます。