🎸

RailsにおけるAmplitude Experimentのバックエンド実装

2024/06/21に公開

はじめに

こんにちは。バックエンド基盤チームの山本です。バックエンド基盤チームは弊社の技術基盤チームの中にあります。技術基盤チームにはフロントエンド基盤チームや認証基盤チームなど、サービス全体を俯瞰して活動しているチームが他にも存在します。今回はフロントエンド基盤チームと連携し、Amplitude Experimentのバックエンド実装を行った話をしようと思います。

Amplitudeの活用

弊社ではAmplitudeを用いて、ユーザーの反応や行動を分析し、サービスの改善に役立てています。
Amplitudeの機能としてユーザーの操作を分析するだけではなく、featureフラグやABテストを行うExperimentという機能が存在します。

  • featureフラグ
    必要に応じて機能をON/OFFできる機能です。この機能により、プログラムを本番環境に配備する「デプロイ」と、ユーザーが利用できる状態とする「リリース」を分離することができるようになります。
  • ABテスト(A/Bテスト)
    ユーザーを任意のルールでAグループとBグループに振り分け、異なるバージョンのコンテンツやデザインによるコンバージョンを比較できます。これにより直感や推測に頼らずに、具体的なエビデンスをもとに改善できます。

バックエンドのABテスト連携

弊社が開発を担当している、診察の予約システムではフロントがReact、バックエンドがRailsとなっています。一般的にABテストに関しては、レイアウトやボタンの色を変更したり、フォームの入力項目数を変更したりフロントエンド領域におけるデザイン修正の検証に使われているケースが多いのではないでしょうか?
今回はもう少し突っ込んで、フロントの振り分けグループに連動してバックエンドの処理も変更したいケースを考えてみます。これができれば、フロントのABテストのグループに応じたバックエンドの処理が可能となります。たとえばバックエンドで送信しているメールやSMSの文面や、DBに保存するデータの変更がAmplitude Experimentに応じたグループで可能となります。

AmplitudeのIDをバックエンドまで持ってくる仕組み

Amplitudeは、ユニークユーザーをデバイスID、ユーザーID、Amplitude IDの3つのIDで追跡します。デバイスIDは各デバイスに対して一意に生成され、ユーザーIDはサービスによって割り当てられます。Amplitude IDはこれらをリンクします。つまり、デバイスID、ユーザーIDさえ分かれば、Amplitude IDは特定できます。ユーザーの特定についてはこのドキュメントが分かりやすいです。
Amplitude ExperimentはRuby用SDKも提供しており、デバイスIDとユーザーIDが分かれば、フロントエンドと同じABテストやfeatureフラグの設定値をバックエンドで取得できます。
ところが、話はそう簡単ではありません。デバイスIDはその名の通りデバイス(OSやWebブラウザ)ごとにUUIDが作成されます。これはフロントエンドでしか知り得ない情報を元に作成されます。デバイスIDを何とかしてバックエンドまで持ってこないと、Ruby用SDKが利用できません。
今回はこの問題に対してフロントエンド基盤チームに協力してもらい、GraphQLリクエストのHeaderにAmplitudeのデバイスIDとユーザーIDを埋め込んでもらいました。

実装例

# Gemfile

# Amplitude
gem 'amplitude-experiment'
# app/services/amplitude/experiment.rb
class Amplitude::Experiment
  # Amplitude::Experimentクラスの新しいインスタンスを初期化します。
  #
  # @param api_key [String] AmplitudeサービスのAPIキー。
  # @param device_id [String] デバイスのID。
  # @param user_id [String, nil] ユーザーのID(オプション)。
  # @raise [RuntimeError] APIキーまたはデバイスIDが欠けている場合。
  def initialize(api_key:, device_id:, user_id: nil)
    raise 'APIキーが必要です。' if api_key.blank?
    raise 'device_idが必要です。' if device_id.blank?
    user_id = user_id == '' ? nil : user_id
    experiment = AmplitudeExperiment.initialize_remote(api_key, AmplitudeExperiment::RemoteEvaluationConfig.new)
    user = AmplitudeExperiment::User.new(device_id:, user_id:)
    @user_experiments = experiment.fetch(user)
  end

  # 現在のユーザーに対して指定されたExperimentの値を返します。
  #
  # @param experiment_name [String] Experimentの名前
  # @return [String] Experimentの値(存在する場合)、存在しない場合はnil
  def value(experiment_name)
    @user_experiments[experiment_name]&.value
  end

  def api_key
    # AmplitudeExperimentのDeploymentsで指定したバックエンド用のDeploymentのAPI KEYを取得
    ENV['AMPLITUDE_EXPERIMENT_BACKEND_API_KEY']
  end
end
# 使い方
device_id = request.headers['x-device-id']
user_id = request.headers['x-user-id']
amplitude = Amplitude::Experiment.new(device_id:, user_id:)

# ABテストの場合
test = amplitude.value('ab_test') # 'control' or 'treatment'

# featureフラグの場合
flag = amplitude.value('flag') # 'on' or nil

まとめ

このブログでは以下を解説しました。

  • Amplitude Experimentの機能
  • デバイスIDとユーザーIDをバックエンドに持ってくる方法
  • RubySDKを用いたExperimentの値の取得

この記事が皆様のRailsにおけるAmplitude Experimentのバックエンド利用に役に立てば幸いです。

Linc'well, inc.

Discussion