A/B/n実験の実施
実験の実装
まず、bin/feature-flag
コマンドを使って機能フラグを生成してください。通常の開発機能フラグと同じように、experiment
をタイプに使ってください。説明のために、機能フラグ(と実験)の名前をpill_color
とします。
bin/feature-flag pill_color -t experiment
目的の機能フラグを生成したら、すぐにコードで実験を実装することができます。基本的な実験の実装は次のようになります:
experiment(:pill_color, actor: current_user) do |e|
e.control { 'control' }
e.variant(:red) { 'red' }
e.variant(:blue) { 'blue' }
end
このコードが実行されると、実験が実行され、バリアントが割り当てられ、(コントローラやビューの場合)window.gl.experiments.pill_color
オブジェクトがクライアントレイヤーで利用可能になります:
- 割り当てられた variant。
- クライアント追跡イベントのコンテキストキー。
さらに、実験が実行されると、その実験のイベントがトラッキングされます:assignment
。イベント、トラッキング、クライアントレイヤーについては後で詳しく説明します。
ローカル開発では、機能フラグインターフェースを使用することで、実験をアクティビティにすることができます。機能フラグを有効にする呼び出しに関連する実験を提供することで、特定のケースをターゲットにすることもできます:
# Enable for everyone
Feature.enable(:pill_color)
# Get the `experiment` method -- already available in controllers, views, and mailers.
include Gitlab::Experiment::Dsl
# Enable for only the first user
Feature.enable(:pill_color, experiment(:pill_color, actor: User.first))
環境上で実験機能フラグをロールアウトするには、ChatOpsを使って以下のコマンドを実行します(GitLabドキュメントの開発における機能フラグで詳しく説明されています)。このコマンドは、実験に遭遇した人の半分に_コントロール_、25%に_赤い_バリアント、25%に_青い_バリアントを割り当てるシナリオを作ります:
/chatops run feature set pill_color 50 --actors
この例で均等なディストリビューションにするには、50% ではなく 66% になるようにコマンドを変更してください。
/chatops run feature set pill_color false
コマンドを使います。--actors
フラグを使うことを強くお勧めします。それ以外のフラグを使うと、バリアント割り当てのキャッシュがどのように処理されるかによって、奇妙な動作をする可能性があるからです。この実験はHTMLラッパーを使ったHAMLファイルで実装することもできます:
#cta-interface
- experiment(:pill_color, actor: current_user) do |e|
- e.control do
.pill-button control
- e.variant(:red) do
.pill-button.red red
- e.variant(:blue) do
.pill-button.blue blue
コンテキストの重要性
先ほどの実験例では、コンテキスト(これは重要な用語です)は{ actor: current_user }
に設定されたハッシュです。コンテキストは、実験をどのように実行したいかに基づいて一意でなければならず、より低いレベルで理解されるべきです。
レポートを簡略化するために、これらのコンテキストを使用することが期待され、またレポーターにも推奨されます:
-
{ actor: current_user }
:実験に参加する各ユーザー(current_user
が nil の場合は “client”)に variant を割り当て、”sticky” にします。 -
{ project: project }
:variant を割り当て、閲覧中のプロジェクトに “sticky” になります。特定のユーザーがプロジェクトを見ているときよりも、プロジェクトを見ているときに実験をしたほうが便利な場合は、この方法を検討してください。 -
{ group: group }
:プロジェクトの例と似ていますが、より広い範囲のプロジェクトやユーザーに適用されます。 -
{ actor: current_user, project: project }
:variant を割り当て、指定されたプロジェクトを表示しているユーザーに “sticky” です。これはcurrent_user
が見るプロジェクトごとに異なる variant 割り当ての可能性を作ります。アプリケーションのトラフィックの多い場所でこのような実験をすると、 キャッシュサイズが大きくなる可能性があることを理解しておいてください。 -
{ wday: Time.current.wday }
:現在の曜日に基づいて variant を割り当てます。この例では、金曜日には一貫して一つの variant を割り当て、 土曜日には異なる variant を割り当てる可能性があります。
コンテキストは実験をどのように定義し、レポーターするかにとって重要です。通常、実験をどのように実施するかで最も重要な点ですので、慎重に検討し、必要であればより広いチームと話し合ってください。また、選択したコンテキストがキャッシュサイズに影響することも考慮してください。
上記の例の後、私たちは一般的なケースを述べることができます:特定の一貫したコンテキストがあれば、私たちは一貫した経験を提供し、その経験のためのイベントを追跡することができます。実装の詳細をもう少し掘り下げると、提供されたコンテキストからコンテキストキーが生成されます。このコンテキストキーを使って
- 割り当てられた variant を決定します。
- そのコンテキストキーに対して追跡されたイベントを特定します。
これは、コンテキスト・キーによって指示され、追跡される、私たちがレンダリングした体験と考えることができます。コンテキスト・キーは、そのコンテキスト・キーに対してレンダリングしたエクスペリエンスのインタラクションと結果を追跡するために使用されます。これらの概念はやや抽象的で、最初は理解しにくいものですが、このアプローチによって、実験を単なるユーザー行動よりも幅広いものとして伝えることができます。
current_user
が nil の場合、actor:
を使用すると Cookie が使用されます。しかし、クッキーを必要としない場合、つまり、公開された機能が認証されたユーザーにしか見えない場合、{ user: current_user }
。{ time: Time.current }
を使うと、実験が実行されるたびにキャッシュサイズが膨れ上がります。それだけでなく、あなたの実験は「スティッキー」にならず、イベントは解決できなくなります。高度な実験
実験を行うには2つの方法があります:
- 前述した基本的な実験スタイル。
- より高度な実験クラスを提供するスタイル。
高度なスタイルは命名規則で処理され、Railsで期待されるものと同じように動作します。
ApplicationExperiment
、デフォルトをオーバーライドできるカスタム実験クラスを生成するには、Railsジェネレータを使います:
rails generate gitlab:experiment pill_color control red blue
これにより、app/experiments/pill_color_experiment.rb
、ジェネレータに提供した_ビヘイビアを_持つ実験クラスが生成されます。前回の例をマイグレーションした後のクラスの例を示します:
class PillColorExperiment < ApplicationExperiment
control { 'control' }
variant(:red) { 'red' }
variant(:blue) { 'blue' }
end
run
を明示的に呼び出すことで、最初に提供していたブロックを提供する代わりに、実験を実行する場所を次の呼び出しに単純化することができます:
experiment(:pill_color, actor: current_user).run
実験クラスで定義した_ビヘイビアは_デフォルトの実装です。しかし、ブロック構文を使ってこれらの_ビヘイビアを_オーバーライドすることもできます:
experiment(:pill_color, actor: current_user) do |e|
e.control { '<strong>control</strong>' }
end
experiment
メソッドに渡すと、run
が呼び出されたかのように暗黙的に呼び出されます。セグメンテーションルール
実行時のセグメンテーションルールを使って、インスタンスでコンテキストを特定のバリアント にセグメンテーションすることができます。segment
メソッドは(before_action
のような)コールバックなので、ブロック名やメソッド名を指定できます。
この例では、'Richard'
という名前のユーザーには常に_赤の_variant が割り当てられ、2 週間以上前のアカウントには_青の_variant が割り当てられます:
class PillColorExperiment < ApplicationExperiment
# ...registered behaviors
segment(variant: :red) { context.actor.first_name == 'Richard' }
segment :old_account?, variant: :blue
private
def old_account?
context.actor.created_at < 2.weeks.ago
end
end
実験が実行されると、セグメンテーションルールは定義された順番に実行されます。実験が実行されると、セグメンテーションルールは定義された順番に実行されます。
この例では、アカウントの年齢に関係なく、'Richard'
という名前のユーザは常に_赤の_バリアントが割り当てられます。逆のロジックにしたい場合は、順番を逆にします。
除外ルール
除外ルールはセグメンテーションルールと似ていますが、あるコンテキストを実験に含め、イベントを追跡する対象として考えるべきかどうかを判断するためのものです。除外とは、与えられたコンテキストに関連するイベントを気にしないということです。
これらの例では、'Richard'
という名前のすべてのユーザーと、2週間以上前のアカウントを除外しています。このような場合、コントロール行動(何もない可能性もある)が与えられるだけでなく、イベントも追跡されません。
class PillColorExperiment < ApplicationExperiment
# ...registered behaviors
exclude :old_account?, ->{ context.actor.first_name == 'Richard' }
private
def old_account?
context.actor.created_at < 2.weeks.ago
end
end
また、should_track?
を呼び出して、カスタムトラッキングロジックで除外をチェックする必要があるかもしれません:
class PillColorExperiment < ApplicationExperiment
# ...registered behaviors
def expensive_tracking_logic
return unless should_track?
track(:my_event, value: expensive_method_call)
end
end
イベントのトラッキング
実験の最も重要な側面の1つは、データを収集し、それについてレポーターをすることです。track
メソッドを使用すると、実験の実装全体でイベントを追跡することができます。実験への呼び出しの間に同じコンテキストを提供すれば、実験に対して一貫してイベントを追跡することができます。コンテキストを理解していない場合は、今すぐコンテキストについて読むべきです。
実験は1箇所か数箇所で実行し、イベントは多くの箇所で追跡すると仮定します。トラッキングの呼び出しはそのままで、snowplow を使ってイベントをトラッキングする際に通常使用する引数を使用します。Rubyでイベントを追跡する最も簡単な例は次のようになります:
experiment(:pill_color, actor: current_user).track(:clicked)
これまでの例で実験を実行すると、デフォルトで:assignment
イベントが自動的に追跡されます。実験から追跡されるすべてのイベントには、特別な実験コンテキストが追加されます。このコンテキストは、通常データチームによって使用され、指定されたイベント間の関連を作成します。
:assignment
もしユーザーが実験(実験が行われる場所という意味)に遭遇していなくて、イベントを追跡した場合、そのユーザーには variant が割り当てられ、後で実験に遭遇したときにその variant を見ることになります。
クライアントレイヤーでの実験
リクエストライフサイクルで実行されたすべての実験は、window.gl.experiments
、このスキーマにマッチするので、クライアントレイヤーで実験を解決するときに使うことができます。
実験用のクラスを定義し、そのバリアントを定義した場合、いくつかの方法でその実験を公開することができます。
最初の方法は実験を実行することです。実験が実行されると、特別なことをしなくてもクライアントレイヤーに表示されます。
つ目の方法は、実験を実行せず、クライアントレイヤーのみで表示させる方法です。そのためには、.publish
。これはロジックを実行しませんが、実験の詳細をクライアントレイヤーに表示させます。
例えば、コントローラのbefore_action
。上記のようにPillColorExperiment
クラスを定義しておくと、実験を実行する代わりにパブリッシュすることで、クライアントに実験を見せることができます:
before_action -> { experiment(:pill_color).publish }, only: [:show]
JavaScriptのコンソールでこのサーフェスを見ることができます:
window.gl.experiments // => { pill_color: { excluded: false, experiment: "pill_color", key: "ca63ac02", variant: "candidate" } }
Vue での実験の使用
gitlab-experiment
コンポーネントを使うと、window.gl.experiments
にプッシュされたバリアントの名前にマッチするスロットを定義できます。
で定義されたビヘイビアにマッチするVueコンポーネントの名前付きスロットを利用できます:
<script>
import GitlabExperiment from '~/experimentation/components/gitlab_experiment.vue';
export default {
components: { GitlabExperiment }
}
</script>
<template>
<gitlab-experiment name="pill_color">
<template #control>
<button class="bg-default">Click default button</button>
</template>
<template #red>
<button class="bg-red">Click red button</button>
</template>
<template #blue>
<button class="bg-blue">Click blue button</button>
</template>
</gitlab-experiment>
</template>
window.gl.experiments
オブジェクトに実験データがない場合、window.gl.experiments
スロットがあれば、それを使用します。