A/B/n 실험 구현

실험 구현

예제

bin/feature-flag 명령을 사용하여 특성 플래그를 생성하여 개발용 특성 플래그와 마찬가지로 실험 유형을 사용하는 것으로 시작합니다. 문서화를 위해 특성 플래그(및 실험)를 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의 개발용 특성 플래그 설명서 참조). 이 명령은 실험을 만난 모든 사용자 중 50%가 컨트롤_에 할당되고, 25%가 _빨간색 변형에 할당되며, 25%가 파란색 변형에 할당되는 시나리오를 생성합니다.

/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이면 “클라이언트”)에게 “스티키”로 할당됩니다.
  • { project: project }: 보기 프로젝트에 할당하고 “스티키”로 할당됩니다. 특정 사용자가 아닌 모든 프로젝트를 보는 것이 실험을 실행하는 경우, 이 접근 방식을 고려하십시오.
  • { group: group }: 프로젝트 예제와 유사하지만 프로젝트 및 사용자의 보다 넓은 범위에 적용됩니다.
  • { actor: current_user, project: project }: 주어진 프로젝트를 보는 사용자에게 변형을 할당하고 “스티키”로 할당됩니다. 이렇게 하면 current_user가 볼 수 있는 각 프로젝트에 대해 다른 변형 할당 가능성이 생깁니다. 응용 프로그램의 고트래픽 부분에 이러한 실험이 있다면 큰 캐시 크기가 생성될 수 있음을 이해하십시오.
  • { wday: Time.current.wday }: 요일에 따라 변형을 할당합니다. 이 예제에서는 금요일마다 항상 하나의 변형을 할당하고 토요일에는 잠재적으로 다른 변형을 할당합니다.

컨텍스트는 실험을 정의하고 보고하는 방식에 중요합니다. 실험을 구현하는 방식을 신중하게 고려하고 필요한 경우 팀과 의논하십시오. 또한 선택한 컨텍스트가 캐시 크기에 영향을 미친다는 것을 고려해야 합니다.

위의 예제 이후, 일반적인 경우를 명시할 수 있습니다: 특정하고 일관된 컨텍스트가 주어지면, 해당 경험에 대해 일관된 경험을 제공하고 해당 경험을 위해 이벤트를 추적할 수 있습니다. 구현 세부 사항을 조금 더 심층적으로 살펴보면, 컨텍스트 키가 제공된 컨텍스트에서 생성됩니다. 이 컨텍스트 키를 사용하여:

  • 할당된 변형을 결정합니다.
  • 해당 컨텍스트 키에 대해 추적된 이벤트를 식별합니다.

이러한 개념은 초기에는 다소 추상적이고 이해하기 어려울 수 있지만, 이 접근 방식은 사용자 행동 이상의 실험에 대해 의사소통할 수 있게 합니다.

참고: actor:를 사용하면, current_user가 nil인 경우 쿠키가 사용됩니다. 단, 쿠키가 필요하지 않다면(즉, 노출된 기능이 인증된 사용자에게만 표시되는 경우) { user: current_user }가 동일하게 효과적입니다.

경고: 변형 할당 캐싱은 이 컨텍스트를 사용하여 수행되므로 실험을 정의할 때 캐시 크기에 영향을 고려하십시오. { time: Time.current }을 사용하면 실험이 실행될 때마다 캐시 크기가 증가하게 됩니다. 뿐만 아니라 실험이 “스티키”가 아니며 이벤트가 해결되지 않을 수 있습니다.

고급 실험

실험을 구현하는 두 가지 방법이 있습니다:

  1. 이전에 설명한 기본 실험 스타일.
  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'라는 이름의 사용자는 항상 red 변형에 할당되고 2주 이상 된 계정은 blue 변형에 할당됩니다:

class PillColorExperiment < ApplicationExperiment
  # ...등록된 동작

  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
  # ...등록된 동작

  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
  # ...등록된 동작

  def expensive_tracking_logic
    return unless should_track?

    track(:my_event, value: expensive_method_call)
  end
end

이벤트 추적

실험의 가장 중요한 측면 중 하나는 데이터 수집 및 보고입니다. track 메서드를 사용하여 실험 구현에서 이벤트를 추적할 수 있습니다. 실험을 실행하는 경우 컨텍스트를 호출 사이에 동일한 컨텍스트를 제공하면 실험에 일관되게 이벤트를 추적할 수 있습니다. 컨텍스트를 이해하지 못한다면 지금 콘텍스트에 대해 읽어야 합니다.

실험을 한 곳이나 몇 곳에서 실행할 것으로 가정할 수 있지만 여러 곳에서 이벤트를 추적할 수도 있습니다. 추적 호출은 여전히 동일하며, 일반적으로 snowplow를 사용하여 이벤트를 추적할 때 사용하는 인수를 전달하면 됩니다. Ruby에서 이벤트를 추적하는 가장 쉬운 예는 다음과 같습니다:

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 슬롯이 사용됩니다.