PLAID Engineer Blog

PLAID Engineer Blog


KARTEを提供する株式会社プレイドのエンジニアブログです。プレイドのエンジニアのユニークなパーソナリティを知ってもらうため、エンジニアメンバーたちが各々執筆しています。

PLAID Engineer Blog

社内でフロントエンドのパフォーマンスチューニングコンテストを開催した

mizchimizchi

フロントエンド/Node.js エンジニアの mizchi です。plaid では新しい分析エンジンのフロントエンド側の技術的な仕様を考えたり、それを実装したりしています。趣味として社内の他のプロダクトのパフォーマンスを勝手に測って、貼り付けていくこともあります。

plaid のエンジニア組織には「組」という制度があって、メインとなるプロダクト以外にも、そのテーマで会社横断で活動するグループがあり、最低一つ所属することが奨励されています。例えばセキュリティ組、バグトリアージ組、Tech 勉強会組などがあります。

最近になって パフォーマンス組を新設しました。これは主にフロントエンドのパフォーマンスを調査して、各プロダクトにその改善を促すグループです。

plaid のフロントエンドは PaaS としての外向けのサードパーティスクリプトと、その管理画面があるのですが、サードパーティの方はパフォーマンスも計測しているのですが、管理画面の方は歴史が長く設計変更も多々あり、富豪的な感じになってしまっています。

これを改善するためにいくつか調査を行い、実際にいくつかは修正したのですが、本質的には「組織的なパフォーマンス意識」を向上させる必要がある、という結論になりました。組織としてのパフォーマンスを意識できないと、目の前の問題を解決したとして、いずれ再発します。(これはセキュリティなどでも同様ですね)

そこで、その文化の改善のために、まず社内のプロダクトをテーマにハッカソンの開催を開催してみよう、という話になりました。

開催の経緯

とある日の slack のスレッドの自分とデザイナーの高橋さんの会話。

mizchi 「ところで Baisu (社内の CSS ガイドライン兼モック) むちゃくちゃ重くないですか」

高橋「めちゃ重いですよね」

mizchi 「なんで重いんだろう… JS でした」

高橋「モック環境だから色んなライブラリとか試すのにいれまくってるからか!なるほど」

mizchi 「ここパフォチュのお題によさそう。lighthouse で高得点出した人が優勝」

mizchi 「壊しても悲しむのが高橋さんだけってのがパフォチュ向き」

見ていた同僚が突然テザーサイトを作成

開催決定!

mizchi「業務時間使って開催しますね」

nashibao(CPO)「どんどんやっていけ」

真面目な話

社内のワークフローでは、社内のモノレポの一部の baisu というプロダクトで vue.js のモックページを作成し、それを元に各プロダクトで実装が行われています。

このワークフローの抱える問題として、 baisu の抱えるパフォーマンス上の問題は、そのままプロダクトに引き継がれてしまいます。実際いくつかパフォーマンス上の問題として表出していました。

ここでの改善は、そのまま管理画面やその他のページの改修にそのまま活かせます。プロダクトに直接コミットするより、まず同じ問題を抱えたモックページをチューニングすることで、心理的な負荷を減らしつつ大胆なアプローチを取れるはずだ、と考えました。

まず自分で解いてみた

前提として、この baisu というのは、基本的に vue.js + vue-router で構築された SPA です。html-webpack-plugin と webpack, webpack-dev-server で起動し、本番は静的サイトとして superstatic 上で動作します。

firebase/superstatic: Superstatic: a static file server for fancy apps.

そもそも問題として適当かどうかを測るため、自分でまず自分がチューニングしつつ問題を洗い出しました。

その上で発生している問題の洗い出し。

これによって、適当なお題だと判断。あえて放置して本番を迎えました。

開催告知

弊社は 2 ヶ月に一回チームを入れ替えるのですが、その入れ替え期で比較的締め切りなどがない余裕があるタイミングで開催しました。事前に「業務時間を使って参加していいよ、ただしチームの同意はとってね」という周知をしました。

レギュレーションとスコアサーバーの実装

周知した方向性として、「計測対象となるページの個別の最適化は避けて、できるだけ一般的な、マージしたいと思えるような実装で解いてね(それでもスコアは十分伸びるので)」という感じに。

計測用スクリプトはこんな感じ。

const fs = require('fs');
const path = require('path');
const lighthouse = require('lighthouse');
const chromeLauncher = require('chrome-launcher');

const TARGET_URLS = [
    "http://localhost:8080/",
    "http://localhost:8080/user/story/",
    "http://localhost:8080/karte/wizard/",
    "http://localhost:8080/organization/",
    "http://localhost:8080/account/register/",
];
const metricsLighthouse = async (chrome, url) => {
    const options = { logLevel: 'error', output: 'html', onlyCategories: ['performance'], port: chrome.port };
    const runnerResult = await lighthouse(url, options);
    const filename = url.replace("http://localhost:8080/", "").replace(/\//g, "_");
    const reportHtml = runnerResult.report;
    fs.writeFileSync(`report_${filename}.html`, reportHtml);
    return {
        url,
        score: runnerResult.lhr.categories.performance.score * 100,
        totalByte: runnerResult.lhr.audits["total-byte-weight"].numericValue
    }
};

(async () => {
    const chrome = await chromeLauncher.launch({ chromeFlags: ['--headless'] });
    try {
        const results = [];
        for (const url of TARGET_URLS) {
            const result = await metricsLighthouse(chrome, url);
            results.push(result);
        }
        const totalScore = results.reduce((total, result) => {
            return total + result.score;
        }, 0);
        const totalByte = results.reduce((total, result) => {
            return total + result.totalByte;
        }, 0);
        console.log(`Total score: ${totalScore}`);
        console.log(`Total bytes: ${totalByte} bytes`);
        console.table(results);
    } finally {
        await chrome.kill();
    }
})();

要は lighthouse の Performance のスコアです。

開催

結果

ほぼ全員が 80mb を 10mb 付近まで削っていたのですが、最終的に優勝したのは、 html-webpack-plugin の吐くコードを書き換えた人で defer にしたことで、lighthouse のスコアが上がった、新卒一年目の kosukeoya でした。

kosukeoya 曰く、「自分はフロントエンドのパフォーマンスチューニングは初めてだったが、事前資料として共有されたサイバーエージェントのハッカソンでの最適化を上から順になぞった」ということで、あの資料やっぱ有効だったんだなぁという感じに。

https://github.com/CyberAgentHack/web-speed-hackathon-online/wiki/Web-Speed-Hackathon-Online-出題のねらいと解説

参加者の声

webpack-bundle-analyzer - npm

技術的な感想 / 得られた教訓

きれいな webpack chunk の様子です

baisu [11 Sep 2020 at 18:25] - Gyazo

講評中に見つかった、プロダクトに活かせそうなものの

この辺やらないとわからなかった気がします。

運営してみての感想 / 反省

プロダクトと同じ構成のモックの読み込み改善というお題がよかったと思います。そのまま業務にフィードバックできるので、明日からやっていけそう、みたいな声が多かったです。

そもそもルーティングのチャンク分割、 lazyload にたどり着いてくれないとその後の改善がないのが心配だったんですが、参加者全員、そこはたどり着いていました。ハッカソン中に時間経過でヒントを小出ししたのも、何もできない人が出なくてよかったかなと思います。

反省として、最終的な script の defer 属性によるスコア差については、今回のお題ではやや評価が難しいところがあります。なぜなら、baisu は js を読み込まないと画面に何も表示されない、SSR なしの SPA なので、エントリポイントの同期/非同期は、結果的なユーザー体験にあまり影響がありません。

まあ、スコアサーバーの機嫌を読むのもコンテストの一部ということだと思うので、これはこれで良いと思います。ただ html-webpack-plugin の吐くコードに手を入れる、というのは気づきづらかったですね。

人が集まりやすい事前準備をして、告知の反応も多かったんですが、やはり業務時間を使う心理的な抵抗からか人が集まりづらく、想定の半分ぐらいでした。ここは次にもっと工夫したいと思います。

社内プロダクトの改善という他社で再現性があるか微妙なテーマではあり、これも外に出せないのが申し訳ないんですが、似たようなモックと課題をもつような会社では開催できるのではないでしょうか。そもそも実プロダクトを対象にしてもいいと思います。

Hiring

plaid では webpack chunk を 1byte でも絞り込みたいという人を募集しています。

参考にした先行事例

mizchi
Author

mizchi

Comments