
JavaScript
イベント

マガジン
技術ブログ
こんにちは、ミイダス Tech Officeです。 ミイダス株式会社のテックチームが直近で開発した機能や取り組みを現場のエンジニアから共有する「MIIDAS Tech LIVE」。 第15回目の開催となる今回は3つのリリース・取り組みをお届けしました。
はじめに こんにちは。BASE Departmentでバックエンドエンジニアをしている白数( かじゅ )です。 私たちのチームでは今年の夏、カートの表示速度を改善するプロジェクトに取り組みました。 ここでいう「速度」は、サーバーの処理時間だけを指すものではなく、購入者がボタンを押してから次の画面が表示されるまで、実際に待っている時間を指します。 計測を始めた時点で特に表示に時間がかかっていたのは、PAY IDアプリで「購入手続きに進む」を押してから購入確認画面が表示されるまでの経路でした。 一度に大きく作り替えず、小さく変えては数字を見る、を繰り返しフロントエンドとバックエンド合わせてマージ(リリース)したプルリクエストは約140件にもなりました。 その積み重ねの結果、最も効果が大きかったWebでカートを開く操作では、ボタンを押してからカートが表示されるまでの待ち時間がプロジェクト開始前の半分以下になりました。 この記事では、表示速度を改善するまでの過程と実施した対応の一部をご紹介します。 「ボタンを押してから画面が出るまで」を一つの指標にした BASEのカートでは、商品ページの「カートに入れる」や、アプリの「購入手続きに進む」を押すと、次の画面に遷移する前に中継用のページに入ります。 このページの役割は、カートの状態を確認し、カート編集画面と購入確認画面のどちらへ進むかを決めることです。購入者に見えるのはローディング表示だけなので、以下では「振り分けページ」と呼びます。 購入者は振り分けページの読み込み、APIの処理、次画面の読み込みが終わるまで待つことになります。 複数のページをまたぐためAPIの応答時間だけを見ても、この待ち時間は分かりません。 これまでAPIのレイテンシは日々計測している状況でしたが、画面表示までの時間は計測できていませんでした。 まずは振り分けページのナビゲーション開始から、次画面の表示が完了するまでを一つのカスタムイベントにまとめ、ブラウザからNew Relicへ送るようにしました。 この値をプロジェクトの主要指標としたうえで関連データを含む形でダッシュボード化しp50(中央値)とp90を中心に日々の推移を手軽に確認できるようにしました。 詳細はお見せできませんが作成したダッシュボード 内訳を見るとページ全体を2回読み込んでいた 処理の内訳は振り分けページ、バックエンド、購入確認画面の3区間に分かれます。 処理の内訳 区間ごとに計測すると、p50ではバックエンドのAPIが全体に占める割合は非常に小さく、待ち時間の大半は振り分けページと購入確認画面という、2回のページ全体の読み込みでした。 不要なJavaScriptを読まないようにした 改修前の振り分けページは、ローディングを表示するだけにもかかわらず、13本のJavaScriptファイルを読み込んでいました。 中身はReactや状態管理ライブラリ(XState)などで、振り分けの処理はこれらの読み込みと初期化が終わるまで始められませんでした。 そこで、振り分けの処理を始めるまでに読み込むJavaScriptを減らすことを方針にしました。 先に、改修前後を図に示します。 改修前後 まずはフロントエンドの軽量化を行いました。主な対応は以下です。 サポートブラウザの見直しにより不要になった古いpolyfillを削除 OpenAPI定義から自動生成していたAPIクライアントを最小実装へ見直し 実際には使用していない処理まで含まれており、ファイルサイズが大きくなっていたため自動生成された型定義のみを利用し必要最小限の実装へ変更 次に「カートに入れる」などのアクションをサーバー側でも実行できる構成へ変更しました。 これまでXStateやReact hooksに依存していた処理を、Reactに依存しない素の関数へ置き換えたうえで、BFF(Backend for Frontend)であるNext.jsのAPIルートへ移しています。 これらの対応により、 ブラウザからバックエンドAPIを順番に呼び出す必要がなくなった BFFへの1回のリクエストで処理が完結 ブラウザ側に残る処理は、警告表示・計測・画面遷移のみ という構成になりました。 Reactに依存する処理がなくなったため、Next.jsのページとしてリクエストを受けつつもJSXを使用せずに、ローディング表示と振り分け用のインラインスクリプトのみの必要最小限なHTMLを文字列で組み立てて直接レスポンスすることで、振り分けページのみはReactを使用しない構成に変更しました。 その結果、約353KB(gzip圧縮後)のJavaScriptが全て不要となり、このページで実行するJavaScriptファイルは0本となっています。 ここまでの対応に要したプルリクエストは、polyfillの削除が7件、APIクライアントの見直しが25件、素の関数への置き換えが37件、BFFへの移設が13件、Reactの削除が6件です。 最も件数が多かった素の関数への置き換えは、単体ではプロジェクト開始前の表示時間(p50)の約4%にあたる短縮にとどまりましたが、BFFへの移設とReactの削除を可能にする下準備となりました。 最終的には、プロジェクト開始前の表示時間(p50)に対して「カートに入れる」経路では合わせて約19%、カートを開く経路では約13%にあたる時間を短縮できています。 次の画面で使う情報を先に取得して渡すようにした 購入確認画面で使うカート・購入手続き・決済情報は、振り分けページのSSRで取得しても使われなかったり、購入確認画面のSSRでバックエンドから取り直されたりと、無駄な待ち時間の原因になっていました。 これらの情報はBFFで振り分けの処理を実行する際に並行して取得し、短い有効期限でRedisへ保存する形とすることでバックエンドからの取り直しを不要にしました。 この対応でPAY IDアプリの経路では、プロジェクト開始前の表示時間(p50)の約14%にあたる時間を短縮できています。 また、振り分けページでローディングを表示している間に、次画面で使うJavaScriptを先読みしておくことで遷移後の読み込み時間の短縮にもつながっています。 バックエンドはp90を中心に改善した 上述したとおり、p50では全体に占めるバックエンド処理の割合が小さかったものの、p90ではバックエンド処理の占める割合が大きかったため改善を行いました。 バックエンドの対応に要したプルリクエストは14件です。 1リクエストの中で外部APIを複数回実行しており処理時間の大半は、この外部APIからの応答を待つ時間です。 そこで不要な外部APIの呼び出しを経路から外しました。 次画面では参照せず、表示後に別のAPIで取り直す値を計算していた。同じ処理を別画面向けのAPIと共有し、そちらでは値を使っていたため、不要な経路でだけ計算をスキップした ある判定のために2種類の外部APIを呼んでいたが、判定に必要な情報は片方のAPIのレスポンスにも含まれていたため、1回の呼び出しにまとめた また、ループの中にN+1問題がありました。 バックエンドはレイヤーを分けたモジュラーモノリスの構成で実装されています。 DBアクセスや他機能への問い合わせがループの中に隠れていてコード上では気付きにくく、N+1の逐次問い合わせが発生していたため、事前に一括取得するようにリファクタリングを行いました。 AIを使って調査と改善のサイクルを速めた ここまでに紹介したボトルネックの調査や改善内容の検討にはAIを活用しています。 たとえば、New Relicのログや計測結果を調べる際には、確認したい内容をもとにクエリのたたき台を作らせたり、複数の計測結果から次に確認すべき箇所を整理させたりしました。また、コードを調査する際には、処理の呼び出し関係を追い、不要なAPI呼び出しやループ内の問い合わせが発生している可能性がある箇所を洗い出すために利用しました。 AIの出力をそのまま採用するのではなく、実際のログやコード、計測結果と照らし合わせて確認し、修正後はダッシュボードで数値が改善していることを検証しました。 このように、仮説を立て、コードを調査し、小さく修正して計測するサイクルを繰り返すことで、多数の改善を継続的にリリースできました。 表示時間が階段状に下がった 上記以外の対応も含め、プロジェクト開始前の1週間と全施策完了後の1週間を比べたときの表示時間の変化は次のとおりです。 経路 p50(中央値) p90 PAY IDアプリ: 購入手続きに進む → 次画面 -35% -40% Web: カートに入れる → 次画面 -42% -46% Web: カートを開く → 次画面 -53% -51% p50では最大53%、p90では最大51%の短縮です。 速度改善期間中のPAY IDアプリにおける購入手続きに進む→次画面のp50 約140件のプルリクエストを施策別に見ると、不要なJavaScriptを読まないようにする対応が88件、次の画面で使う情報の先取りが20件、バックエンドの改善が14件、計測の仕組みづくりや本記事で紹介していない改善が20件です。 フロントエンド、バックエンドともに効果が大きかったのはAPIの呼び出し回数を減らす施策でした。 改善の効果をチームで喜んだ ダッシュボードの数字だけでなく、実際に画面を操作して体感でも確かめていました。 速度が改善するたびに盛り上がり、良い雰囲気の中でプロジェクトを進めることができました。 改善の効果があったときの会話 おわりに 過去にも速度改善の対応経験はありましたが、AIの利用によりボトルネックの分析や特定が非常に楽になりました。 断片的に散らばったログを自力でかき集め、分析や対応内容を検討していたとすると、これだけの数のプルリクエストをリリースするのは難しかったと思います。 また、今回はプロジェクトとして対応しましたが、AIによって分析のハードルが大きく下がるので、日常的に分析させることで、処理を効率化できる箇所に早く気付けそうだと感じました。 BASEでは様々な職種で一緒にプロダクトを作り上げていくメンバーを募集しています。興味のある方は、ぜひお気軽に採用情報をご確認ください! binc.jp
こんにちは、エンジニアの中山です。以前 Google の AI Overview で、ピザに接着剤を使うアドバイス が掲出されて話題になりましたが、皆さんは生成 AI の出力品質にどのように向き合って...



















