サービスの成長と環境変化に追いつくためアップデートを実施
最初に、西村氏はKARTEの基本的な機能を紹介した。
「KARTEでは、今現在サイト上にどんなユーザーが訪問しているか知ることができ、エンドユーザーが実際にどんな動きをしているかを確認できます。」
エンドユーザーの行動に応じたアクションも配信可能になっており、ポップアップやチャットなどを提供できるのだ。たとえば、サイトの初回訪問者のみにクーポンを配布するなど、エンドユーザーの状態に応じたアクションを打てる顧客体験を改善・強化できるマーケティングのためのWebサービスだ。
この機能を実現するため、「計測タグ」と呼ぶ専用タグを顧客のWebサイトに埋め込んでもらう。そして、KARTEが提供するサードパーティスクリプトを読み込むことで機能を実現している。
サードパーティスクリプトは、訪問者の状態に応じたイベントデータをサーバーに送信し、サーバーから返却されたポップアップなどのアクションの描画をおこなうJavaScriptのコードだ。
計測タグは、全てのお客さまのWebサイトに埋め込まれており、共通のサードパーティスクリプトをCDNを介して読み込むというシステム構成になっている。
KARTEでは、2015年のサービスリリース以来、継ぎ足しで計測タグとサードパーティスクリプトを改善してきたが、3つの課題を持っていた。
「1つ目の課題は、スクリプトサイズの肥大化が目立ってきたことです。一部のプロジェクトにしか必要ない機能が存在しており、またブラウザの進化などにより不要なコードが増加していました。」
さらに、Web全体でのトレンドの変化への対応も要因のひとつだ。GoogleによるCore Web Vitalsの導入以降、それまで非機能要件であったWebサイトのパフォーマンスが、SEOを重視するサイトで一種の機能要件と見なされていたのだ。
「2つ目の課題は、サードパーティスクリプト上で動作するKARTEプロダクトの増加です。複数のプロダクトを動かす統一的な仕組みがなく、プロダクトごとに自由に開発している状態でした。3つ目の課題は、計測タグ自体の拡張性に制限があり、
KARTEでは、こうした課題に対処するため、スクリプトサイズの軽量化、サードパーティスクリプト上で動作するプロダクトのプラグイン化、計測タグ自体の拡張を目指すことにした。
肥大化したコードサイズを約1/10に軽量化
「サードパーティスクリプトの軽量化では約1/10に軽量化できました。従来はgzip圧縮前で307キロバイトあったのスクリプトが、新しいサードパーティスクリプトでは、最小設定ではありますがgzip圧縮前で25キロバイトという計量化に成功しました。」
西村氏は、軽量化のために実施したいくつかの改善策を説明した。まずはプロジェクトごとにJavaScriptを作成することで、お客さまごとに必要な機能を取捨選択できるようにした。
プロジェクトごとのJavaScriptは、全プロジェクト共通のコードとプロジェクトごとのJSON Configをbundleして生成している。このとき、機能の取捨選択を2つのステップで実現している。
図のようにentry.jsとfeature.jsのような2つのコードがあり、rollupなどでentry.jsのコードをbundleする場合を想定する。entry.js内のUSE_A、USE_Bはプロジェクト別の設定ファイルで定義されており、USE_Aがtrue、USE_Bがfalseのようにbundle時に定数置換することができる。これをterserで最適化すると、featAだけが有効になり、featBのコードを消すことができる。
このほかにも、Treeshakingや Dead Code Eliminationを意識した開発をおこない、公式にサポートが終了したInternet Explorerの対応をしないといった改善策により、コードサイズの軽量化を実現した。
「ただ、こういう手法にもデメリットがあります。1番大きなデメリットとして感じていることは、全プロジェクト共通のJavaScriptの更新時に、プロジェクトの数だけJavaScriptを生成し直す必要があることです。これには、2つの課題がありました。」
1つ目の課題は、デプロイ時間が長くなることだ。サービスが成長していきプロジェクト数が膨大になると、全体のデプロイ時間も長くなる。たとえば、リリース直後にバグが発見されたときに、巻き戻しにも時間がかかってしまい、お客さまにも迷惑をかけてしまう可能性があるのだ。
「この課題の解決のために、bundleの最適化を実施しています。たとえば、bundleの方法をrollupからesbuildにして試験しているところです。また、プロジェクトごとの最適化はできませんが、bundleしないでプロジェクトごとの設定をそのまま文字列置換する方法も行っています」
2つ目の課題は、CDNのInvalidationに制限があることだ。JavaScriptを更新した際にはキャッシュ削除をおこなうInvalidationをかけるのが一般的だが、同時にInvalidationできるファイル数に制限があるのだ。
「サービスの成長に連れてプロジェクト数がすごく多くなった場合、Invalidationの制限に引っ張られてInvalidationできないプロジェクトが出てくるなどスケール面で問題が出てきます。」
そこで、CDNのInvalidationをかけずに、CDNのキャッシュ時間を短めに設定して、キャッシュ時間が切れることで、エッジのJavaScriptが更新される仕組みにした。
複数プロダクトや機能の追加に対応できるようプラグイン化
「会社のフェーズの変化もあり、サードパーティスクリプト上で動かすプロダクトも増えてきました。プロダクトというのは、ポップアップ表示とかチャット機能とか、エンドユーザーの困り事に合わせたFAQの表示などを表しています。こういったさまざまなプロダクトをサードパーティースクリプト上で動かす統一的な仕組みがなく、プロダクトごとに自由に開発している状態でした。」
こうした課題を解決するためプラグイン化を進めたが、次図のような要件があった。
こうした要件への対応のうち、プロジェクトごとに必要な機能が異なるという点は、先ほど紹介したようにプロジェクトごとにJavaScriptを生成する方法で実現した。
「さらにサートパーティスクリプト本体から切り離されて開発される点、サードパーティスクリプト本体から情報を渡す必要がある点については、dynamic importとシンプルなinterface規約を設けるという単純な方法で解決しました。」
プラグイン開発時には、図の左側にある疑似コードのようなインターフェースを満たしたプラグインを実装してもらい、管理画面での変更登録に応じて本体からプラグインをロードするようにした。本体部分に絡む引数をargsで渡すようにしてあり、共有ライブラリという概念を持たない。また、プラグイン開発用にargsのモックを介するユーティリティを社内パッケージへ提供している。
「社内のエコシステムでもう少しもんでから、このプラグインシステムを社外にも公開したいと考えています」
プラグイン間のデータのやり取りに関しては、Pub/Subの仕組みを採用した。Pub/Subでは、さまざまなトピックを定義して、各トピックに対して色々なプラグインからメッセージをパブリッシュする。トピックをサブスクライブすると、保持しているメッセージを含めてメッセージを受け取りできるようになる。
この仕組みを利用することで、プラグイン間のデータのやり取りを実現したのだ。
新しい計測タグは、Web管理者に負担をかけないよう拡張する
「旧計測タグでは弊社側の機能拡張の際に、お客さま側での作業が必要となってしまう場合がありました。まず、お客さまは左側の擬似コードのようなスクリプトを書くことによってイベントを弊社側のサーバーに飛ばすことができます。そのために、右上部の擬似コードのように tracker オブジェクトに trackメソッドを事前宣言するようなスクリプトタグをお客様側のWebサイトに埋め込んで頂く必要があります。
このとき、trackメソッドだけを使い続けるのであれば問題ないですが、弊社側で新規の機能としてdeleteのようなtrack以外のメソッドを開発した場合には、右下部の疑似コードのように新しいメソッド宣言を追加する必要がありました。ですので、新しい機能を使いたい場合は、お客さまによる事前宣言用のスクリプトタグの書き換えが必要でした。このような背景もあり、新しいメソッドを容易に開発できないということが課題としてありました。」
そこで、新しい計測タグでは、第1引数がメソッド名となるようなファンクションを生やすようにした。メソッドの定義自体は、サードパーティスクリプト内部に存在している。
「右側の事前宣言用の疑似スクリプトを見るとわかりやすいと思いますが、サードパーティースクリプト内部に存在するメソッド定義が露出するまでにイベントトラッキング関数が叩かれたらargsをarrayに逐次保存しています。そして、サードパーティスクリプトが実行されてメソッド定義が露出した時、arrayに溜まっていたメソッドとバリューのセットを順次読み取って、メソッドを実行しています。このようにメソッド定義がサードパーティスクリプト内部に定義されているので、お客さまが事前宣言用のスクリプトタグを変更することなく、容易にメソッドを追加できるようになりました。」
しかし、計測タグを新しいものに移行するとき、全てのイベントトラッキング関数をお客さまに書き換えていただく必要が出てくる。そこで、互換用のスクリプトタグを作成して、旧計測タグのインターフェースを新計測タグの上に実装することで、書き換えの負担を押さえた。
このような取り組みによりKARTEでは、2015年のサービスリリース以降初となる計測タグとサードパーティスクリプトの全面刷新が実現した。西村氏は、今後もプロジェクトごとJavaScriptのbundle速度の向上や、スクリプトサイズを小さく保っていく仕組みや文化を作っていきながら、サードパーティスクリプト上に乗るプロダクトをがんがん開発していきたいと述べて講演を締めくくった。