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
객체가 클라이언트 레이어에서 사용 가능해 집니다. 여기에는 다음과 같은 세부 정보가 포함됩니다:
- 할당된 변형.
- 클라이언트 추적 이벤트를 위한 컨텍스트 키.
또한 실험이 실행될 때, :assignment
에 대한 이벤트가 추적됩니다. 이벤트, 추적 및 클라이언트 레이어에 관한 내용은 추후에 다루며, 설명하겠습니다.
로컬 개발에서는 기능 플래그 인터페이스를 사용하여 실험을 활성화할 수 있습니다. 또한 기능 플래그를 활성화하기 위한 호출 시 관련 실험을 제공함으로써 특정 케이스를 타겟팅할 수 있습니다:
# 모든 사용자에게 활성화
Feature.enable(:pill_color)
# `experiment` 메서드 가져오기 -- 이미 컨트롤러, 뷰 및 메일러에서 사용 가능
include Gitlab::Experiment::Dsl
# 첫 번째 사용자만 활성화
Feature.enable(:pill_color, experiment(:pill_color, actor: User.first))
환경에서 실험 기능 플래그를 배포하려면 다음 명령을 ChatOps를 사용하여 실행합니다 (이는 GitLab 개발의 기능 플래그 문서에서 더 자세히 다룹니다). 이 명령은 실험에 접하는 모든 사람의 절반이 control_로 할당되도록 하는 시나리오를 만듭니다. 25%는 _red 변형으로, 25%는 blue 변형으로 할당됩니다:
/chatops run feature set pill_color 50 --actors
이 예제의 경우, 균등 분포를 위해 명령을 50이 아닌 66%로 설정하도록 변경하세요.
주의:
실험 실행을 즉시 중지하려면 /chatops run feature set pill_color false
명령을 사용하세요.
경고:
ChatOps 명령을 사용할 때 --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일 경우 “클라이언트”)에게 “sticky”합니다. -
{ project: project }
: 변형을 할당하고 현재 보고 있는 프로젝트에 “sticky”합니다. 특정 사용자가 어느 프로젝트를 보고 있을 때보다 프로젝트를 보는 것이 실험을 실행하는 데 더 유용하다면 이 방식을 고려하세요. -
{ group: group }
: 프로젝트 예제와 유사하지만, 더 넓은 범위의 프로젝트와 사용자에게 적용됩니다. -
{ actor: current_user, project: project }
: 변형을 할당하고 주어진 프로젝트를 보고 있는 사용자에게 “sticky”합니다. 이렇게 하면current_user
가 보는 각 프로젝트에 대해 다른 변형 할당 가능성이 생성됩니다. 이는 이러한 실험이 높은 트래픽 부분에서 수행될 경우 큰 캐시 크기를 초래할 수 있음을 이해하세요. -
{ wday: Time.current.wday }
: 현재 주의 요일에 따라 변형을 할당합니다. 이 예제에서는 금요일에 하나의 변형이 지속적으로 할당되고, 토요일에는 잠재적으로 다른 변형이 할당됩니다.
컨텍스트는 실험을 정의하고 보고하는 데 중요한 요소입니다. 이는 일반적으로 실험을 구현하는 방식에서 가장 중요한 측면이므로 신중하게 고려하고, 필요 시 더 넓은 팀과 논의하세요. 또한 선택한 컨텍스트가 캐시 크기에 영향을 미친다는 점을 염두에 두세요.
위의 예를 통해 우리는 일반적인 경우를 진술할 수 있습니다: 특정하고 일관된 컨텍스트를 제공하면 일관된 경험을 제공하고 해당 경험에 대한 이벤트를 추적할 수 있습니다. 구현 세부 사항을 좀 더 깊이 탐구하면, 제공된 컨텍스트에서 컨텍스트 키가 생성됩니다. 이 컨텍스트 키를 사용하여:
- 할당된 변형을 결정합니다.
- 해당 컨텍스트 키에 대해 추적된 이벤트를 식별합니다.
이것은 우리가 렌더링한 경험으로 생각할 수 있으며, 이는 컨텍스트 키에 의해 결정되고 추적됩니다. 컨텍스트 키는 우리가 렌더링한 경험의 상호작용과 결과를 추적하는 데 사용됩니다. 이러한 개념은 초기에는 다소 추상적이고 이해하기 어려울 수 있지만, 이 접근 방식은 우리에게 사용자 행동 이상으로 실험에 대해 소통할 수 있게 해줍니다.
주의:
actor:
를 사용하면 current_user
가 nil일 경우 쿠키를 사용합니다. 그러나 쿠키가 필요하지 않다면 - 즉, 노출된 기능이 인증된 사용자에게만 표시되도록 해야 한다면 - { user: current_user }
도 효과적입니다.
경고:
변형 할당의 캐시는 이 컨텍스트를 사용하여 처리되므로 실험을 정의할 때 캐시 크기에 미치는 영향을 고려하세요. { time: Time.current }
를 사용하면 실험이 실행될 때마다 캐시 크기가 증가하게 됩니다. 뿐만 아니라, 귀하의 실험은 “sticky”하지 않으며 이벤트가 해결되지 않을 것입니다.
고급 실험
실험을 구현하는 두 가지 방법이 있습니다:
- 이전에 설명한 기본 실험 스타일.
- 실험 클래스가 제공되는 더 고급 스타일.
고급 스타일은 네이밍 규칙에 의해 처리되며, Rails에서 기대하는 것과 유사하게 작동합니다.
기본값을 재정의할 수 있는 사용자 정의 실험 클래스를 생성하려면 Rails 생성기를 사용하십시오:
rails generate gitlab:experiment pill_color control red blue
그러면 사용자 정의한 _behaviors_가 포함된 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
우리가 실험 클래스에서 정의한 _behaviors_는 기본 구현을 나타냅니다. 그러나 이러한 _behaviors_를 재정의하기 위해 블록 구문을 사용할 수 있으므로, 다음과 같은 방식도 유효합니다:
experiment(:pill_color, actor: current_user) do |e|
e.control { '<strong>control</strong>' }
end
experiment
메서드에 블록을 전달하면, run
이 호출된 것처럼 암시적으로 실행됩니다.세분화 규칙
런타임 세분화 규칙을 사용하여 특정 변형으로 컨텍스트를 세분화할 수 있습니다. segment
메서드는 콜백(예: before_action
)과 같으며 블록이나 메서드 이름을 제공할 수 있습니다.
이 예제에서는 이름이 'Richard'
인 모든 사용자가 항상 red 변형에 할당되고, 2주 이상의 오래된 계정은 blue 변형에 할당됩니다:
class PillColorExperiment < ApplicationExperiment
# ...등록된 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'
인 모든 사용자가 계정 나이에 관계없이 항상 red 변형에 할당됩니다. 반대 논리를 원한다면 순서를 바꾸십시오.
배제 규칙
배제 규칙은 세분화 규칙과 유사하지만, 특정 컨텍스트가 실험에 포함되어 이벤트를 추적할 수 있는지 여부를 판단하는 데 사용됩니다. 배제란 주어진 컨텍스트와 관련된 이벤트에 대해서는 신경 쓰지 않는 것을 의미합니다.
이 예제에서는 이름이 'Richard'
인 모든 사용자와 2주 이상 된 모든 계정을 배제합니다. 이들은 제어 행동이 주어지며 - 그것이 아무것도 아닐 수 있습니다 - 이 경우에는 아무 이벤트도 추적되지 않습니다.
class PillColorExperiment < ApplicationExperiment
# ...등록된 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
# ...등록된 behaviors
def expensive_tracking_logic
return unless should_track?
track(:my_event, value: expensive_method_call)
end
end
이벤트 추적
실험의 가장 중요한 측면 중 하나는 데이터를 수집하고 이를 보고하는 것입니다.
track
메소드를 사용하여 실험 구현 전반에 걸쳐 이벤트를 추적할 수 있습니다.
실험 간의 호출에 동일한 컨텍스트를 제공하면 이벤트를 일관되게 추적할 수 있습니다.
컨텍스트를 이해하지 못한다면 지금 컨텍스트에 대해 읽어봐야 합니다.
우리는 실험을 하나 또는 몇 군데에서 실행한다고 가정할 수 있지만, 이벤트는 잠재적으로 많은 장소에서 추적됩니다.
트래킹 호출은 같으며, 이벤트를 snowplow를 사용하여 추적할 때 일반적으로 사용하는 인수를 포함합니다.
루비에서 이벤트를 추적하는 가장 쉬운 예는 다음과 같습니다:
experiment(:pill_color, actor: current_user).track(:clicked)
지금까지의 예제 중 어떤 것으로 실험을 실행하면 :assignment
이벤트가 기본적으로 자동으로 추적됩니다.
실험에서 추적되는 모든 이벤트에는 이벤트에 특별한 실험 컨텍스트 가 추가됩니다. 이는 일반적으로 데이터 팀이 주어진 실험의 이벤트 간 연결을 생성하는 데 사용될 수 있습니다.
사용자가 실험(즉, 실험이 실행되는 장소)에 아직 노출되지 않았다면, 그들에게 이벤트를 추적하면 변형이 할당되고, 나중에 실험을 만났을 때 그 변형을 보게 됩니다. 이때 :assignment
이벤트가 그들에게 추적됩니다.
참고: GitLab은 추적과 관련하여 고객을 민감하게 존중하려고 하므로, 우리의 실험 라이브러리는 식별 ID를 추적하지 않고도 실험을 구현할 수 있게 합니다. 그러나 실험 보고 요구 사항에 따라 항상 가능하지는 않습니다. 때때로 실험에서 특정 레코드 ID를 추적하도록 요청받을 수 있습니다. 접근 방식은 구현을 만드는 PM과 엔지니어에 따라 크게 달라집니다. 현재 이와 관련하여 추천 사항은 제공되지 않습니다.
클라이언트 레이어의 실험
요청 생명주기에서 실행된 모든 실험은 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">기본 버튼 클릭</button>
</template>
<template #red>
<button class="bg-red">빨간 버튼 클릭</button>
</template>
<template #blue>
<button class="bg-blue">파란 버튼 클릭</button>
</template>
</gitlab-experiment>
</template>
참고:
주어진 실험 이름에 대해 window.gl.experiments
객체에 실험 데이터가 없으면, control
슬롯이 존재할 경우 사용됩니다.