JavaScriptフレームワークのコストを考える

先日、私はBrightonで開かれたJavaScriptのカンファレンスFFConfで「(ここにライブラリやフレームワークの名前を入れる)を使おう。これこそ最強中の最強中の最強だ!」と題して話をしました。

ここに、そのプレゼンテーションの内容を書き起こします。もっと注目されるべき、最近のモバイルデバイスのフレームワークにかかるコストに関して、議論を広げる一助となればと思います。

2015年11月16日更新 – テーブルに1行、プロダクション環境下のReactについての行を追加しました。良いニュースをお伝えしますと、これはvanillaよりも3倍遅いですが、TodoMVCに関して言えば速いと言えます!PolymerのTodoMVCサンプルも最新バージョン1.2.2にアップデートされ、同様により速くなりました。

読むよりも見たい方へ、講演のビデオはこちらです。(必要なら、スライドも入手できます

読むほうが都合がよい方は、このまま進んでください。

フレームワークの利点

この年の初め、扱うツリーのサイズが大きくなっていくReactの性能の特徴について書きました(要は、大きいツリーほど、多くの計算をする)。その投稿に対する反応は、有益で建設的なものから、全く逆のものまで実に様々でした。おかげで私はまた新たな気づきを得たのですが、それは程度の差こそあれ全てのフレームワークにあてはまることです。ここにまとめてみます。

  • フレームワークを使うのは楽しい。 より厳しく退屈な経験を積極的に探求しようという開発者には会ったことがありません。もちろん、当たり前ですが、労働効率重要です。
  • フレームワークで、MVPをより素早く構築できる。 特に、規模の小さい、立ち上がったばかりの組織や企業にとって、これは大切です。即興で開発ができるツールの有無は、サービスを公開できるか否かに影響します。
  • フレームワークはプラットフォーム内のバグ周りで役立つ。 近年、全てのブラウザが標準に則り始めたので、これは以前ほど大きな問題ではないと思います(全く的はずれでもありませんが)。私がこの仕事を始めた当初、IE5や6、初期のFirefox、SafariそしてもちろんChromeなどを扱っていた頃と比べると、格段にラクになりました。
  • (フレームワーク名を入れる)を知っている、ということが収入につながる。 仕事を探しているなら、最新かつ最善のフレームワークを使えることが大きなプラスになるかもしれません。

時にははっきりと、たいていは暗に何度も言われたのは、労働効率こそが、多くの開発者にとって最も重要な要因である、ということでした。言い換えるなら、その心は、「もっと簡単にできるなら、ユーザにとってもっと使いやすいものを開発できる」です。言っていることは魅力的ですが、その考えには欠けているポイントがたくさんあります。

開発者の労働効率 VS ユーザのニーズ

ユーザにもニーズがあるのです。すぐに思いつくことを挙げます。
サイトとアプリのローディングは速いのがいい。
スムーズに反応してほしい。
私の電話を遅くしないでほしい。
クラッシュしないでほしい。
私の欲しい機能が付いているべき。

そこですぐに、トレードオフの可能性を考えることになります。一方で開発者の利便性と労働効率、他方でユーザのニーズ。私たちは、いやでもその2つの取引をしなければなりません。なぜならフレームワークはタダではないからです。

次に進む前に

ここで、いったんライブラリの話をして、今回の議論からは外します。理由は、ライブラリは良くないと分かれば取り除き、別のものに差し替え可能だと思うからです。ライブラリの日付書式が気に入らない? 問題なし、他の何かと取り替えましょう。他方、フレームワークは差し替えがずっと困難で、結局そのアプリを再構築する羽目に陥ることも多いのです。またその性質上、フレームワークはより大きく、影響範囲が広いのです。

コードのコスト

コストのかからないコードはありませんが、特にフレームワーク固有の下記のようなコストがあると考えています。

A deprecation warning.
フレームワークの何らかが非推奨であることを伝える魔法のような瞬間。明らかにJavaに取って代わられています。謎です。

  • 学習のコスト。 新しいフレームワークを学習する時間をとらなければなりません。つまり、パターン、プラクティス、何かをするときのやり方など、そのフレームワークに”慣用的な”コードを書くということです。これはWebプラットフォームそのものと違いはありません。近道はなく、ひとつひとつの事項を覚えていくしかありません。
  • 再学習のコスト。 いつか、アプリの構築中に、コンソールに「非推奨」の警告が表示され、コードを更新しなければならない時がくるでしょう。その場合、たいていフレームワークの最新バージョンの要求のうち、旧バージョンとは異なる点を理解する必要があります。また、古いバージョンのフレームワークで運営されているプロジェクトをアップデートしなければならないこともあるでしょう。
  • デバッグのコスト。 フレームワークには、ソフトウェアと同様、バグがつきものです。これは予測できることであり、Vanillaを使って自作するときと変わりありません。しかしフレームワークのデバッグは、困難、不可能なレベルであることが多いのです。問題を見つけたら、フレームワークの元のコードに確実な修正が施されるまで、モンキーパッチを適用して自分で作ったバージョンも持っておくのがよいでしょう。

開発者のコストとは別に、ユーザにもコストがかかります。

  • 時間 開発者が送り出した制作物が起動するまでにどのくらいかかりますか?
  • 帯域幅 どれだけの帯域幅を消費しますか?
  • CPU使用率(バッテリ) CPUにかかる負担はどれほどでしょうか。CPUの使用率はバッテリの使用に関わります。CPUを浪費すれば、バッテリも浪費されます。
  • フレームレート コードはどのくらい効率良くスクリーンをアップデートしていますか?(ユーザはスムーズな反応を好むことは周知のとおりです)
  • メモリ使用量 どのくらいのメモリを消費しますか? それは他のアプリが使うメモリに影響したり、タブをクラッシュさせたりしないでしょうか。

データを表にする

以上のことに気をつけながら、これらのコストを測ることについてお話します。どのようなトレードオフを選んでいくのか、開発者が一緒になって、より詳しいイメージを描いていければと思います。

フレームワークのモバイルにおける起動時間を見てみましょう。理論上、一定の条件のアプリを使えば、各フレームワークの起動時間を比較できるはずです。ここではTodoMVCをテストに使います。なぜなら、開発者から見て優れたWebアプリですし、ユーザとして見たときにはそれぞれの機能は同一であるためです。

TodoMVC.
TodoMVC: 起動テストのソース

 起動 / インタラクティブになるまでの時間

先にリストアップしたコストのうち、Nexus 5とiPhone 5S上でいくつかのフレームワークを起動する際の時間、帯域幅、そしてCPU使用率をチェックしました。

Measuring time to interactive.
注釈:起動 / インタラクティブになるまでの時間
(ラベル上段左から)JSリクエスト – JS取得 – モデルデータリクエスト – モデルデータ取得 – ページがインタラクティブに
(ラベル下段左から)ロード – 評価、ロード&実行

反応時間:アプリが反応可能な状態に至るまでにかかる時間を測ります。

各フレームワークのJavaScriptの最初のペイロードの評価と実行、それからモデルに最初のデータセットをプロセスし設定するのにかかる時間を測ります。スタイル、レイアウト、描画などにかかるコストは、様々なTodoMVCにおいてほぼ違いはないので、ここでは除外します。また、転送時間もテストしません。

方法

ページをNexus 5にロードするためにWebPagetestを使い、それぞれの実行に対してタイムラインファイルを要求し、それをBig Rigに渡してプロセスするようにしました。iPhoneに関しては自身で行い、手動で計算する必要がありました。なぜなら、あいにくSafariのJavaScriptプロファイリングからはタイムライン、トラッキングファイルのエクスポートができなかったからです。

Using Big Rig to measure TodoMVC bootup time.
Big Rigを使い、フレームワークごとのTodoMVC起動時間を短時間で査定

テストの反復

このようなテストを自身で行う場合は、少なくともChromeを走らせるデバイスを使うとして、次の作業が必要です。

  • Big Rig CILをインストールする。: npm install -g bigrig
  • WebPagetestを開く。
  • ロケーションをDullesに設定する。
  • ブラウザをNexus 5 – Chrome(またはBeta/Dev)に設定する。
  • Chromeの設定パネルで”Capture Dev Tools Timeline 開発ツールタイムラインの取得”にチェックを入れる。
  • テストを実行する。
  • テスト結果の左側をクリックして、タイムラインファイルをダウンロードする。
  • bigrig --file /path/to/timeline.json --pretty-printを実行。

WebPagetestからタイムラインファイルを取得する過程のビデオです。

結果

様々なフレームワークのテスト結果です。

フレームワーク サイズ 起動時間 N51,3 起動時間 iPhone 5S2,3
Polymer v1.1.4 41KB5 409ms 233ms
Polymer v1.2.2 47KB5 155ms 232ms
AngularJS v1.4.3 324KB 518ms 307ms
React v0.13.3 [JSX未変換] 311KB 1,201ms 1,463ms
React v0.13.3 [BabelでJSX変換]4 162KB 509ms 282ms
React v0.13.3 [JSX変換済み; prod build]4, 6 160KB 174ms 118ms
Backbone v1.2.2 [inc. jQuery & Underscore] 139KB 248ms 168ms
Ember v1.10.0-beta.3 580KB 1,992ms 1,440ms
Vanilla 16KB 50ms 33ms
  1. Nexus 5上のChrome 47でテスト済み。
  2. iPhone 5S上のSafari 9でテスト済み。
  3. 全ての起動時間は初期todoリストデータの操作を含む。
  4. JS Transformerはストリップ済み、Babelで変換したJSXファイル。
    5. Web Components Polyfill(12KB)を除外。
  5. Reactは、minifyしたプロダクション・ビルドのコードに切り替え。

結果は非常に明白でした。モバイルでフレームワークを使うと、特にVanilla JavaScriptで書いた場合と比較して、非常に負荷が大きいようです。Polymer 1.2.2が最速で、これは素晴らしいのですが、Vanillaと比べると3倍もの時間がかかっています。ReactはPolymerによく似ていますが、そのスケール不変性に懸念が残ります

以下は、より状況を明確にするためのメモです。
* TodoMVCはReactのJSX変換を行わないので、自分で行った。 Reactに関して3つのエントリがあるのは、TodoMVCのサンプルはJSXを変換せず、代わりにJSX変換ライブラリを含むためです。状況を良くするため(なぜならJSX変換には時間がかかるからです)、よりベースに近いバージョンのサンプルを作り、再度テストを行いました。欠点は、それはReactの小バージョンではないので、結局は別のデータであるということです。そこでReactをプロダクションバージョンに切り替えました。それがテーブル内Reactの3つ目のデータです。
* これらの時間は転送時間を含まない。 ここで計測したのはJavaScriptで、フレームワークが起動し最初のビューを取得するまでの時間です。実際TodoMVCのいくつかのフレームワークは小さくなっていません。フレームワークの転送サイズについての議論は、昨年のFilament Groupの記事をチェックしてみてください

予測される反論

このテストに対し、議論に値する反論が出てくると思われます。

  • 「TodoMVCは慣用的ではない」  この点について検討しましたが、全ての実装は慣用的だというのが私の理解です。そうでない場合、フレームワークの専門家はそれに対するPRを出します。
  • 「TodoMVCは私のユースケースではない」  それはあり得ますが、お使いのユースケースは恐らくTodoMVCよりも高コストだろうと思います。最近Paur IrishはRedditのモバイルサイトで性能検査を行いました。そこで分かったのは、モバイルでReactのコンポーネントをいくつか起動するだけで22秒かかるということでした。
  • 「私たちのユーザはNexus 5やiPhone 5Sを使わない」 これもあり得ます。このテストで良いハードウェアを使っているのは有利ですし、多くの人が最新のモバイルを持っていないことも理解できます。その場合の結果はずっと悪いのだろうと推測できます。
  • 「(ここにフレームワークの名前を入れる)の次バージョンを使ったほうがいいのでは」 それもそうです。しかし、あなたが現バージョンで作ったものに大して関係があるとは思えません。

「64,000ドルの問い」

よって、次の問いは避けられません。「フレームワークを使うべき?」

私はこの問いに答えることはできません。全てはあなたの必要次第だからです。それを使わなくては、と考えるに至るには何百万もの理由があります。ただ、フレームワークについて私の考えを述べておきます。

* フレームワークはアイデアとコンセプトに寄与する。 フレームワークは、どのアプローチが役に立ち、あるいは役に立たないかを理解する際の要です。それは究極にはプラットフォームレベルでの改良を達成し得ます。この点から言うと、フレームワークは将来のプラットフォーム変更を支える重要な試験の場であり、Webに永久に埋め込まれてしまう前に動きを理解する助けになります。
* フレームワークはコントロールの否定。 先ほどライブラリを除外したのは、取り替えが効くからです。一方、フレームワークは、コントロールを否定します。それらはアプリのライフサイクルを制御し、コードが走るエントリポイントを決定します。あなたはコードの最終形に責任がありますが、コントロールはできません。
* フレームワークはモバイルでは高コスト。 少なくとも、Vanillaと比較した場合です。私は高過ぎると思いますが、人それぞれ許容値は違います。

フレームワークには、労働効率上の利点があると思います(これは重要、もちろん賛成です)。しかし、多くの開発者にとって、Webプラットフォームそのものの知識への投資は、長期で賭けるに値するものです。フレームワークは盛衰が激しく、Web上の潮の満ち引きのようなものです。そして上でも述べたように、アイデアとパターンに寄与します。しかし、現在使っているものがもう役立たないと気づいたり、修正不可のバグを見つけたりした場合、その土台となるプラットフォームを理解できることは大きな助けになります。

さらなる議論へ

今年の初め、Reactに関する記事で、次のように書きました。

開発者の労働効率はユーザのニーズほど重要ではない。

今もこの考えは変わりません。より気楽な生活を望むのと同じくらい、うまく動かないものを送り出したくないと思いますし、ユーザにコストを払わせたくないのです。今日は、モバイルでフレームワークを起動するコストに懸念を持ちました。

これは最初の一歩でしかありません。起動以外にも、メモリ使用率、長期間のCPU利用量、フレームレートへの影響など、まだ検討していない基準があります。全体では、ユーザに送り出したコードの影響、ユーザに渡したコストを適切に評価するには、他にもしなければならないことがあると考えます。

速い起動、低いメモリ使用率、スムーズな実行、さらに労働効率の良いフレームワークに到達できれば、成功です。その時まで、少なくともモバイルに関しては、Vanilla Webプラットフォームを使い続けたいと思います。