Kaizen Platformの技術の大変革 ~ 大規模トラフィックと高性能配信への転換 ~

アプリケーションエンジニアのkuです。

Kaizen Platformは、経験豊富なグロースハッカーのネットワークを活かしてより少ない手間でA/Bテストを提供するサービスから始まって4年になりました。現在では古典的なA/Bテストに加えて、広告をはじめに様々な改善手法をクリエイティブとセットで提供しています。

様々な手段を提供する一方で、基盤となるアーキテクチャは4年前の創業時に開発したものを使い続けていました。当時とは事業環境も変わって将来に向けた大規模な投資が可能になり、技術的にも中長期的視点で課題に取り組むことができるようになりました。

そこで、これからのサービス開発を柔軟に効率的に進めるため今年の初めから基盤の刷新を始めているので、その話をします。

A/Bテストの実現方法(現行版)

現行版は創業時に作られたプロトタイプを元にしています。

A/Bテストをしたいクライアントさんのサイトにscriptタグを入れて Kaizen Platform のJavaScriptを読み込んでもらいます。このJavaScriptファイルはクライアントさんごとに異なる内容になっていて、中にはどのURLでどんなデザイン案を表示するかなどが入っています。ユーザがブラウザでページを開いた時にこのJavaScriptが実行されて組み込まれたデータに従ってA/Bテストが行われます(もしくは何も行われなかったりします)。

信頼性の確保

クライアントさんのサイトにKaizen PlatformのJavaScriptコードを入れてもらうので、そのスクリプト(SiteScriptと呼んでいます)には高い信頼性が求められます。弊社のスクリプトがクライアントさんのサイトを壊してしまっては元も子もありません。そのため新しいバージョンをリリースする際には、ちょっと古いInternet Explorerから最新のAndroidまで様々なデバイス・ブラウザで

  • ユニットテスト
  • e2eテスト
  • マニュアルでの動作確認

を行って変更の安全性を確認する体制を構築しました。それによりここ3年重大事故なく開発を進めてくることができました。

利点

この実装はA/Bテストの実行をひとつのJavaScriptファイルだけで完結させられるので、サーバ側のシステム構成をシンプルにすることができます。現行版はJavaScriptを配信するCDNと、A/Bテストの結果を受信するログサーバとでできています。

f:id:kaizenplatform:20180220114547p:plain

JavaScriptファイルに全部を詰め込むことで生じる問題もあります。

  • 全てのロジックがサイトに入れてもらうJavaScriptの中に入っているので、たとえ小さな変更でも信頼性担保のテストは欠かせない。結果として開発サイクルが遅くなる
  • たくさんA/Bテストを実施するほどJavaScriptファイルの中に入れるデザイン案データが増えて、ファイルサイズが大きくなる
  • 機能を増やすとコード量が増えるのでやっぱりJavaScriptファイルが大きくなる
  • ロジックがクライアントさんのサイトで使われているjavascriptライブラリの影響を受けやすい(prototype.jsとか)

また、全てがブラウザ内で動作するため機能的な制約もあります。 ユーザに対して単純にランダムにデザイン案を表示するのではなく、これまで蓄積したデータセットと訪問者の属性を照らしあわせて表示するデザイン案を変えてみたい、という要求をブラウザだけで実現するのは困難です。

新版

上述の壁をふまえて、クライアントさんに入れてもらうJavaScriptコードの変更頻度を最小限にして動作テストにかかる負荷を減らすと同時に、今後の拡張性を広げるためにインテリジェントな処理は全てサーバに移すことにしました。

A/Bテスト対象ページ、デザイン案データ、セッションなどユーザ固有の情報、デザイン案の選択ロジックをサーバに移動します。JavaScriptの中には、サーバにログを送信する部分とデザインの適用ロジックのみを残してサーバから指定された動作を無条件に実行するだけのスクリプトになります。

f:id:kaizenplatform:20180220114612p:plain

ロジックの大半がサーバに移動すればクライアントさんのサイトに入れてもらうスクリプトに変更を入れる必要は少なくなり、信頼性確保のためのテストに必要な時間も、テストをパスしてもゼロにはならない動かなかったらどうしようという精神的な負荷も減らせます🙂

トレードオフ

しかし新しい構成がいいことずくめなら、そもそもはじめからそういう構成にしています。当然いくつかのトレードオフがあります。

コスト:moneybag::moneybag::moneybag:

これまではJavaScriptの中でA/Bテストを実行するかどうかを判別して、実行したときだけログを送信していました。またJavaScriptを入れた全てのページで常にA/Bテストを実施するわけではないのでログとして送られて来るリクエスト数は全体のトラフィックの一部でした。

新しい構成ではJavaScriptが読み込まれると常にログが送信され、A/Bテストを行うかどうか、どのデザイン案を適用するか、全てサーバで判別します。そうすると1日数億リクエストをサーバで処理することになってそれなりにコストがかかります。

デザイン案が適用されるまでのレイテンシ

現行版はJavaScriptが読み込まれてDOMContentLoadedイベントをトリガーにデザイン案を適用しています。新しい構成は一度サーバに問い合わせをして、そのレスポンスに従ってデザイン案を適用するのでデザイン案の適用が完了するまでどうしても時間がかかります。

f:id:kaizenplatform:20180220114625p:plain

と、思っていたのですがChrome devtool timelineで実際測定してみると、ファイルサイズが小さくなって転送にかかる時間が短くなること、ファイルサイズが小さくなることでスクリプトのコンパイルにかかる時間が減ること、スクリプトが読み込まれてからDOMContentLoadedイベントが発生するまでの時間があること、それらを合わせると通信にかかる時間はほとんど隠蔽できることがわかりました。

というわけで問題はコストだけ! コストはなんとでもするから気にするなという新CTO渡部の力強い言葉のもとに開発を進めています。

これから

今回の再構築はA/Bテストだけでなく、事前にIssue Finderから自動生成された条件に一致する訪問者にメッセージを出してコンバージョンに繋げたり、訪問者の属性と蓄積しているデータから有効なアクションを計算して実行する、といった、何もしなくてもマシンが自動で悪いところを直してくれるサービスを実現するための布石です。

毎日集まってくる数億リクエストのデータをもとにコンバージョンに繋がるアクションをリアルタイムに計算して100ms以下で返す、そんなシステムを作りたい方、お待ちしております。