『HOT PEPPER Beauty』Frontend スピードハッカソンに参加した

はじめに

こんにちは、さぃと (@saitoeku3) です。
今回はリクルートが主催するハッカソンに参加したので、振り返りや学びを残しておこうと思います。

ハッカソンなので最終的に順位がついて僕のチームは残念ながら 4 位でしたが、普段とは違う体験がたくさんできて最高のイベントでした。

イベントの内容

HOT PEPPER Beauty のあるページの速度改善を行いどれだけパフォーマンスを高められるかを競うハッカソンです。

レギュレーションは「Chrome Canary を使用する」「見た目の大きな変更をしてはならない」「機能の変更をしてはならない」というもので、Lighthouse の計測結果を参考にして順位を決定しました。

後に触れるのですが、参加者はフロントエンドだけではなくてバックエンドやインフラの領域まで自由に触ることができます。

やったこと

チームとしての施策は同じチームで大活躍してくれた @kaa_a_zuRecruit主催のスピードハッカソンに参加してきた にまとめているので、そこで確認してください。

僕の取り組みとどんな思いで取り組んだのかを書いていきます。

予習

行きの新幹線の中で超速本を読んだり、JSConf JP で行われたスピードハッカソンのリポジトリ を触ったりしていました。

実は以前超速本を読んだときに Scrapboxネットワーク処理の改善というメモを残しており、短い時間でこれを元に思い出しながら実践する時間を取れたのは良かったです。

アプリケーションの測定

1 番最初に環境構築が終わったので、すぐに Chrome Developer Tools で測定をしました。
よく使われるものとして思いつく施策はいくつかありましたが、アプリケーションによって抱える問題は変わってきます。がむしゃらにやっても効果は薄いので、最初に原因を明らかにしたのは正解だったと思います。

ネットワークタブのウォーターフォールの測定

DOM の描画を除けば初期描画までの時間の多くはネットワークが影響しています。
何が原因で通信に影響が出ているのか調べるためにネットワークタブのウォーターフォールを見ていました。

これは qiita.com のネットワークタブで時間がかかっている通信を順番に並べた様子です。
多くは画像の読み込みですよね。題材のページも同様に画像がボトルネックになっていました。

カバレッジの測定

これは pixiv.net の CSS と JS のカバレッジです。
Chrome Developer Tools を開いて Cmd + Shif + P の後に Show Coverage と入力すると表示されます。

トップを開いただけで全ての CSS や JS が適応されるわけではありませんが、使われていない赤色の部分が多いことがわかります。これらの多くは本来不要な通信を行ってパフォーマンスを下げていることになるので、理想を言うと削るべきです。

題材のページはもっと酷い状況でした。

施策の提案と実施

すぐ調べられる範囲では「無駄な CSS と JS が多い」「画像が大きすぎる」という課題を発見できたので、僕は次のことに取り組んでいました。

なお「題材のページが 1 ページのみで規模が小さいこと」や「全員が使い慣れているわけではないこと」を考慮して webpack は使用しませんでした

CSS の最適化

デッドコード (使われていない CSS) を削除するために PurgeCSSスクリプトを書きました。ただし、Qiita で見つけたサンプルはバージョンの違いによって期待する動作をしなかったので、途中でメンバーにパスして minify のスクリプトを書いていました。

minify には clean-css を使いました。ファイルの中身を渡すだけでシュッと簡単にやりたいことができて体験が良かったです。

JavaScript の最適化

uglify-js を使って minify をしました。選定理由は「題材のページは全て ES5 であるから」「使用した経験があるから」です。

webpack のプラグインとしてよく使われている (記憶が正しければ Webpacker に標準で搭載されている) terser でも minify ができるので候補としては考えていましたが、使い慣れていないので使いませんでした。(引き出しが少ないと窮屈で辛い)

画像の遅延読み込み

Chrome Canary を使っていたので最初は安心して loading="lazy" を採用しました。

しかし、行きの新幹線の中で標準の遅延読み込みよりも IntersectionObserverisIntersecting で画面に入ったか判定するほうがシビアに遅延読み込みを実現できることを確認していたので、途中からは lazysizes を使用しました。

※ 体感なので根拠は薄い

ここは自分で React 用のライブラリ を書いていたのことにも助けられて、スムーズに導入できました。

結果

これらに加えて画像の圧縮、HTTP2 化、リソースの gzip 化などを行ったところパフォーマンスは 97 まで改善させられました。
終了 30 分前までは他のチームに 300P 差をつけた 1位だったので優勝を確信しましたが、最終的には 4位 で終わってしまい悔しかったです。

タネ明かし

優勝チームは静的ファイルを Express から配信するのは遅いことを知っていたので、Nginx から直接配信することで高いパフォーマンスを実現していました。
Why is it not recommended to serve static files from Node.js?

そもそも Express は Node.js フレームワークの中でも遅い部類です。
fastify によるベンチ https://github.com/fastify/benchmarks

そして、主催者の 1 人である古川さんが次のように仰っていました。

実際にパフォーマンスタブを見ないと気づけない問題もあったので、もっと使えるものはフルで使って測定するべきだと思いました。

おわりに

今回のイベントでは、これまで僕がフォーカスを当ててこなかったパフォーマンスのチューニング方法とその重要性を学ぶことができました。

業務だとパフォーマンスとプロダクトの成長や DX はトレードオフだと思われがちかもしれませんが、今後はユーザーにとって本当に良いアプリケーションを開発できるように気を配りながら実装していきたいです。

最後にこのような素晴らしいイベントを開催してくださったリクルートと一緒に取り組んだ最高のチームメンバー、競い合ったライバルに感謝を述べて締めます。ありがとうございました。(またやりたい)