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의 개발에서 기능 플래그 문서).

/chatops run feature set pill_color 50 --actors

이 예에서 공평한 분배를 위해 명령을 변경하여 50 대신 66%로 설정하십시오.

참고: 실험을 즉시 중지하려면 false를 사용하여 /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 }: 주어진 프로젝트를보는 사용자에게 변형을 할당하고 “고정”합니다. 이렇게하면이 사용자가 볼 수있는 모든 프로젝트에 대해 다른 변형 할당 가능성이 생깁니다. 이는 응용 프로그램의 매우 높은 트래픽 부분에이 종류의 실험이있는 경우 큰 캐시 크기를 만들 수 있음을 이해하세요.
  • { wday: Time.current.wday }: 현재 요일을 기준으로 변형을 할당합니다. 이 예에서는 금요일에 항상 한 가지 변형을 할당하고 토요일에는 잠재적으로 다른 변형을 할당합니다.

컨텍스트는 실험을 정의하고 보고하는 방법에 중요합니다. 실험 실행 방식을 선택하는 데 가장 중요한 측면이며 신중하게 고려하고 필요에 따라 더 넓은 팀과 논의하십시오. 또한 선택한 컨텍스트가 캐시 크기에 영향을 미친다는 점을 고려하십시오.

위의 예제를 통해 일반적인 경우를 설명할 수 있습니다: 특정하고 일관된 컨텍스트가 주어지면 해당 경험에 대한 일관된 경험을 제공하고 해당 경험을 추적할 수 있습니다. 구현 세부 정보에 조금 더 깊게 파고들기 위해 컨텍스트 키가 제공되는 컨텍스트에서 컨텍스트 키가 생성됩니다.

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

우리는 이것을 초기에는 다소 추상적이고 이해하기 어려운 개념으로 생각할 수 있지만, 이 접근법을 사용하면 사용자 동작 이상으로 실험에 대해 의사 소통할 수 있습니다.

참고: actor:를 사용하면 current_user에 쿠키를 사용합니다. 그러나 쿠키가 필요하지 않으면 - 즉, 노출 기능이 인증된 사용자에게만 표시된다는 의미입니다 - {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'라는 이름의 사용자는 항상 빨간색 변형이 할당되고 2주 이상 된 계정은 파란색 변형이 할당됩니다:

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'인 경우에 관계없이 계정 연령에 따른 결과에 상관없이 항상 빨간색 변형이 할당됩니다. 반대로 논리를 바꾸려면 순서를 바꿉니다.

참고: 세분화 규칙을 정의할 때 주의할 점: 진실로 평가된 결과 이후에 최적의 성능을 얻기 위해 남은 세분화 규칙은 건너뜁니다.

제외 규칙

제외 규칙은 세분화 규칙과 유사하지만, 컨텍스트를 실험에 포함시킬지 여부를 결정하는 것을 목적으로 합니다. 제외란 주어진 컨텍스트에 대한 이벤트에 관심이 없음을 의미합니다.

다음 예제는 모든 이름이 '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할 수 있습니다. 이로 인해 어떠한 로직도 실행되지 않지만 실험 세부 정보가 클라이언트 레이어에 표면화되어 거기서 사용할 수 있습니다.

예를 들어, PillColorExperiment 클래스를 정의하고 위에서 언급한 것과 같이 표면화시키고자 한다면, before_action에서 실행하는 대신에 발행할 수 있습니다:

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 슬롯이 있는 경우 그것이 사용됩니다.