GitLab의 개발에서의 피처 플래그

note
이 문서는 GitLab 제품의 개발 및 운영에 기여하는 방법을 설명합니다. 만약 귀하께서 자체 응용 프로그램에서 기능을 표시하고 숨기기 위해 피처 플래그를 사용하려면, 대신 이 피처 플래그 정보를 확인하십시오.
caution
새로 도입된 모든 피처 플래그는 기본적으로 비활성화되어야 합니다.
caution
새로 도입된 모든 피처 플래그는 배우자와 함께 사용되어야 합니다.

블루프린트:

이 문서는 피처 플래그의 내부 사용 개선의 일환으로 계속 작업 중입니다. 새로운 문제를 제기하고 epic에 첨부하십시오.

피처 플래그 라이프사이클 개요 또는 피처 플래그를 사용해야 하는지 여부를 결정하는 데 도움이 필요한 경우에 대해서는, 피처 플래그 라이프사이클 핸드북 페이지를 참조하십시오.

피처 플래그 사용 시기

핸드북의 “피처 플래그를 사용해야 하는 경우” 섹션으로 이동하였습니다.

GitLab 개발에서의 피처 플래그

피처 플래그를 사용할지 여부를 결정할 때 고려해야 할 사항은 다음과 같습니다:

  • 피처 플래그는 기본적으로 비활성화되어야 합니다.
  • 피처 플래그는 가능한 한 짧은 기간 동안 코드베이스에 유지되어야 하며, 피처 플래그 계정 필요성을 줄이기 위해.
  • 피처 플래그를 운영하는 사람은 피처 플래그 뒤의 기능의 상태를 문서와 다른 이해 관계자들에게 명확히 전달해야 합니다. 피처 플래그 이름과 피처 플래그가 기본적으로 켜져 있는지 꺼져 있는지가 명확해지자마자 이슈 설명을 업데이트해야 합니다.
  • 피처 플래그를 도입하거나 상태를 업데이트하거나 기존 피처 플래그를 제거하는 Merge Request은 피처 플래그” 레이블을 할당해야 합니다.

기능 구현이 여러 Merge Request에 걸쳐 제공되는 경우:

  1. 첫 번째 피처 플래그를 기본적으로 비활성화상태로 생성합니다. 이것은 첫 번째 피처 플래그를 사용하는 Merge Request에서 별도로 추가되지 않아야 합니다.
  2. 하나 이상의 Merge Request을 통해 점진적인 변경을 제출하면서, 새 코드가 추가된 경우에만 피처 플래그가 활성화될 수 있도록 보장합니다. 개발 중에 로컬 GDK에서 피처 플래그를 활성화 상태로 유지할 수 있습니다.
  3. 해당 기능이 다른 팀원들에 의해 테스트되도록 준비되면, 초기 문서 작성을 수행합니다. 피처 플래그에 대한 상태에 대한 세부 정보를 포함합니다.
  4. 특정 그룹/프로젝트/사용자를 위해 피처 플래그를 활성화하고 구현에 문제가 없는지 확인합니다. 문서가 없는 경우에는 gitlab-org/gitlab와 같은 공개 프로젝트를 위해 피처 플래그를 활성화하지 말아야 합니다. 팀원 및 기여자가 공개 프로젝트에서 기능을 사용하는 방법에 대한 문서를 찾을 수 있습니다.
  5. 해당 기능이 제품 사용을 준비하였을 때, Self-Managed 인스턴스를 포함하여 다음과 같이 한 Merge Request을 엽니다:
    • 최신 플래그 상태를 설명하는 문서를 업데이트합니다.
    • 변경 로그 항목을 추가합니다.
    • 기능을 활성화하여 새로운 동작을 가능하게 하거나, 피처 플래그를 기본적으로 활성화로 전환합니다(opsbeta 피처 플래그에 대해서만).

피처 플래그 제거가 여러 Merge Request을 통해 제공되는 경우:

  1. 피처 플래그의 값을 변경하는 것은 Merge Request에서의 유일한 변경사항이어야 합니다. 피처 플래그가 코드베이스에 존재하는 한, 피처 플래그의 두 상태는 모두 완전히 기능하게 유지되어야 합니다(기능이 켜진 상태와 꺼진 상태).
  2. 모든 피처 플래그에 대한 언급이 제거된 후, 기존 코드를 제거할 수 있습니다. 피처 플래그 배포 이슈에서의 단계를 따르고, 단계를 건너뛰어야 하는 경우에는 이유를 설명하는 코멘트를 이슈에 추가해야 합니다.

피처 플래그를 사용하면 해당 기능의 릴리스가 최소한 한 달(=하나의 릴리스)을 지연시킬 것이라고 생각할 수 있습니다. 그러나 그렇지 않습니다. 피처 플래그는 특정 기간(예: 적어도 한 릴리스) 동안 유지되어야 하는 것이 아니며, 대신 기능이 안정적으로 판단될 때까지 유지되어야 합니다. 안정적이란 GitLab.com에서 문제(장애와 같은)를 일으키지 않고 작동한다는 것을 의미합니다.

깨진 기본 브랜치의 위험

피처 플래그는 도입하는 Merge Request에서 사용되어야 합니다. 그렇지 않을 경우 기본 브랜치의 깨진 상태 시나리오가 발생합니다. 이는 default 브랜치에서만 실행되는 rspec:feature-flags 작업 때문입니다.

피처 플래그 유형

예상된 사용과 일치하는 피처 플래그 유형을 선택합니다.

gitlab_com_derisk 유형

gitlab_com_derisk 피처 플래그는 GitLab.com 배포를 위한 위험 제거를 위해 사용되는 단기 피처 플래그입니다. GitLab에서 사용되는 대다수의 피처 플래그가 gitlab_com_derisk 유형입니다.

제약 사항

  • default_enabled: 반드시 true로 설정하지 않아야 합니다. 이 유형의 피처 플래그는 GitLab.com에서 위험을 줄이기 위해 사용되므로, GitLab.com에서 활성화된 후 코드베이스에 피처 플래그를 유지할 필요가 없습니다. default_enabled: true는 이 유형의 피처 플래그에는 어떠한 효과도 내지 않습니다.
  • 최대 수명: 기본 브랜치에 Merge된 후 2개월
  • 문서: 이 유형의 피처 플래그는 짧은 기간 동안 유지되며 배포와 관련이 있기 때문에, GitLab의 모든 피처 플래그 페이지에 문서화되지 않아야 합니다.
  • 배포 이슈: 피처 플래그 롤아웃 템플릿에서 생성된 롤아웃 이슈가 있어야 합니다.

사용법

gitlab_com_derisk 피처 플래그의 형식은 Feature.<state>(:<dev_flag_name>)입니다.

다음을 GitLab Rails 콘솔에서 실행하여 이들을 활성화하거나 비활성화할 수 있습니다.

# 인스턴스에 대해 활성화:
Feature.enable(:<dev_flag_name>)

# 인스턴스에 대해 비활성화:
Feature.disable(:<dev_flag_name>)

# 특정 프로젝트에 대해 활성화:
Feature.enable(:<dev_flag_name>, Project.find(<project id>))

# 특정 프로젝트에 대해 비활성화:
Feature.disable(:<dev_flag_name>, Project.find(<project id>))

gitlab_com_derisk 피처 플래그의 상태를 확인하려면:

# 피처 플래그가 활성화되었는지 확인
Feature.enabled?(:dev_flag_name)

# 피처 플래그가 비활성화되었는지 확인
Feature.disabled?(:dev_flag_name)

wip 유형

일부 기능은 복잡하고 여러 Merge Request을 통해 구현되어야 합니다. 그 기능이 완전히 구현될 때까지 아무 에게도 노출되지 않아야 하는 경우에는 “작업 진행 중”을 나타내는 wip 피처 플래그를 사용하여 해당 기능을 아직 사용하지 않고도 모든 변경 사항을 기본 브랜치에 Merge할 수 있게 합니다.

기능이 완료되면, 해당 기능이 고객에게 어떻게 제시/문서화될지에 따라 gitlab_com_derisk 또는 beta 유형으로 변경될 수 있습니다.

제약 사항

  • default_enabled: 반드시 true로 설정되어서는 안 됩니다. 필요한 경우, 기능이 완료되면 이 유형을 베타로 변경할 수 있습니다.
  • 최대 수명: 기본 브랜치로 Merge된 후 4개월
  • 문서: 이 유형의 피처 플래그는 대부분 미완료 코드를 숨기기 때문에 GitLab의 모든 피처 플래그 페이지에 문서화할 필요가 없습니다.
  • 롤아웃 문제: wip 피처 플래그는 활성화되기 전에 다른 유형으로 전환되어야 하므로 롤아웃 문제가 필요하지 않을 가능성이 높습니다.

사용법

# 피처 플래그가 활성화되었는지 확인
Feature.enabled?(:my_wip_flag, project)

# 피처 플래그가 비활성화되었는지 확인
Feature.disabled?(:my_wip_flag, project)

# 피처 플래그를 프론트엔드로 푸시
push_frontend_feature_flag(:my_wip_flag, project)

beta 유형

현재 형태로 디자인된 모든 사용 사례에 대해 기능을 확장, 지원 및 유지하는 데 확신할 수 없는 경우(예시)에 사용하지 않습니다. MVC로 간주하기에 충분히 미완성일 수 있는 시나리오도 있습니다. 이 경우 플래그를 제공하여 엔지니어 및 고객이 새로운 기능이 충분히 성능이 나올 때까지 비활성화할 수 있도록 합니다.

제약 사항

  • default_enabled: 경우에 따라 true로 설정하여 베타에서 모든 사용 사례에 대해 “릴리스”할 수 있으며, 확장성 문제를 해결하기 위해 비활성화할 수 있어야 합니다(이상적인 경우 특정 온프레미스 설치에서만 이러한 이유로 비활성화해야 함).
  • 최대 수명: 기본 브랜치로 Merge된 후 6개월
  • 문서: 이 유형의 피처 플래그는 GitLab의 모든 피처 플래그 페이지에 반드시 문서화되어야 합니다.
  • 롤아웃 문제: 롤아웃 문제가 있어야 합니다. 피처 플래그 롤아웃 템플릿에서 생성되어야 합니다.

사용법

# 피처 플래그가 활성화되었는지 확인
Feature.enabled?(:my_beta_flag, project)

# 피처 플래그가 비활성화되었는지 확인
Feature.disabled?(:my_beta_flag, project)

# 피처 플래그를 프론트엔드로 푸시
push_frontend_feature_flag(:my_beta_flag, project)

ops 유형

ops 피처 플래그는 GitLab 제품 동작의 운영 측면을 제어하는 오래 지속되는 피처 플래그입니다. 예를 들어, Sidekiq 워커 동작과 같은 성능 영향을 미칠 수 있는 기능을 비활성화하는 피처 플래그가 있습니다.

이 유형을 사용하는 것은 인스턴스/그룹/프로젝트/사용자 설정을 도입하지 않기로 한 의도적인 결정을 따라야 한다는 것을 기억하세요.

제약 사항

  • default_enabled: 대부분의 경우에는 false로 설정되어야 하며, 일시적인 확장성 문제를 해결하거나 프로덕션 문제를 디버깅하는 데 도움을 줄 때만 활성화되어야 합니다.
  • 최대 수명: 12개월
  • 문서: 이 유형의 피처 플래그는 GitLab의 모든 피처 플래그 페이지에 문서화되어야 함과 동시에 사용 시기를 설명하는 운영용 런북과 연관되어야 합니다.
  • 롤아웃 문제: 활성화 또는 비활성화될 때 언제 활성화 또는 비활성화될지 예측하기가 어려우므로 롤아웃 문제가 필요하지 않을 가능성이 높습니다.

사용법

# 피처 플래그가 활성화되었는지 확인
Feature.enabled?(:my_ops_flag, project)

# 피처 플래그가 비활성화되었는지 확인
Feature.disabled?(:my_ops_flag, project)

# 피처 플래그를 프론트엔드로 푸시
push_frontend_feature_flag(:my_ops_flag, project)

experiment 유형

experiment 피처 플래그는 GitLab.com에서 A/B 테스트에 사용됩니다.

experiment 피처 플래그는 beta 피처 플래그와 동일한 표준을 준수해야 하지만 인터페이스에는 일부 차이가 있습니다. 실험 피처 플래그는 실험 추적 템플릿을 사용하여 생성된 롤아웃 문제를 가져야 합니다. 자세한 정보는 실험 가이드에서 확인할 수 있습니다.

제약 사항

  • default_enabled: 반드시 true로 설정되어서는 안 됩니다.
  • 최대 수명: 기본 브랜치로 Merge된 후 6개월

worker 유형

worker 피처 플래그는 Sidekiq 워커 동작과 같이 Sidekiq 작업을 연기하는 등 특별한 ops 플래그입니다.

worker 피처 플래그는 이름 자체를 사용하여 동적으로 생성될 수 있기 때문에 일반적으로 YAML 정의가 없을 것으로 예상됩니다. 예를 들어, run_sidekiq_jobs_AuthorizedProjectsWorker와 같은 이름을 사용하여 worker 유형 피처 플래그를 사용하는 몇 가지 예시는 Sidekiq 작업 연기에서 찾을 수 있습니다.

(폐기됨) development 유형

development 유형은 gitlab_com_derisk, wipbeta 피처 플래그 유형을 선호합니다.

피처 플래그 정의 및 유효성 검사

개발(RAILS_ENV=development) 또는 테스트(RAILS_ENV=test) 중에는 모든 피처 플래그 사용이 엄격히 검증됩니다.

이 절차는 코드베이스에서 일관된 피처 플래그 사용을 보장하기 위한 것입니다. 모든 피처 플래그는 다음과 같습니다:

  • 알려진 피처 플래그. 명시적으로 정의된 피처 플래그만 사용합니다(experiment, workerundefined 유형의 피처 플래그를 제외).
  • 두 번 정의하지 않습니다. FOSS 또는 EE 중 하나에서 정의해야 합니다.
  • 정의 파일이 없는 피처 플래그의 경우 모든 호출에서 유효하고 일관된 type:을 사용합니다.
  • 소유자가 있어야 합니다.

GitLab에서 알려진 모든 피처 플래그는 다음과 같은 YAML 파일에 자체 문서화되어 있습니다:

각 피처 플래그는 몇 가지 필드로 구성된 별도의 YAML 파일에 정의됩니다:

필드 필수 여부 설명
name 피처 플래그의 이름
type 피처 플래그 유형
default_enabled 피처 플래그의 기본 상태
introduced_by_url 피처 플래그를 소개한 Merge Request의 URL
milestone 피처 플래그가 생성된 마일스톤
group 피처 플래그를 소유하는 그룹
feature_issue_url 아니오 원본 기능 이슈의 URL
rollout_issue_url 아니오 피처 플래그 롤아웃을 다루는 이슈의 URL
log_state_changes 아니오 피처 플래그의 상태를 기록하는 데 사용
note
RAILS_ENV=production에서 실행 중에는 모든 유효성 검사가 건너뜁니다.

새로운 피처 플래그 만들기

note
GitLab Pages는 다른 프로세스를 사용하여 피처 플래그를 사용합니다.

GitLab 코드베이스는 새로운 피처 플래그 정의를 만들기 위한 전용 도구인 bin/feature-flag을 제공합니다. 이 도구는 새로운 피처 플래그에 대해 다양한 질문을 하고, 그런 다음 config/feature_flags 또는 ee/config/feature_flags에 YAML 정의를 만듭니다.

개발 또는 테스트 환경에서만 실행 중인 경우 YAML 정의 파일이 있는 피처 플래그만 사용할 수 있습니다.

$ bin/feature-flag my_feature_flag
>> 피처 플래그 유형을 지정하세요
?> beta
유형 'beta'를 선택하셨습니다.

>> 피처 플래그가 속하는 그룹 레이블을 다음 디렉터리에서 지정하세요:

1. group::group1
2. group::group2
?> 2
그룹 'group::group2'을 선택하셨습니다.

>> 원본 기능 이슈의 URL(건너뛰려면 입력):
?> https://gitlab.com/gitlab-org/gitlab/-/issues/435435

>> 피처 플래그를 소개하는 MR의 URL(건너뛰고 위험 요소가 MR에서 제안하도록 하려면 입력):
?> https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141023

>> 피처 플래그 DRI의 사용자 이름(건너뛰려면 입력):
?> bob

>> 이것은 EE 전용 기능입니까(건너뛰려면 입력):
?> [Return]

>> 임의의 키를 누르고 클립보드에 복사된 내용을 붙여넣으세요! 🚀
?> [Return를 누르면 "새 이슈" 페이지가 열려 클립보드에 복사한 내용을 붙여넣기만 하면 됩니다]

>> 롤아웃 이슈의 URL(건너뛰려면 입력):
?> https://gitlab.com/gitlab-org/gitlab/-/issues/437162

config/feature_flags/beta/my_feature_flag.yml가 생성됨
---
name: my_feature_flag
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/435435
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141023
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/437162
milestone: '16.9'
group: group::composition analysis
type: beta
default_enabled: false

새롭게 도입된 피처 플래그는 기본적으로 비활성화되어야 합니다.

기능을 개발하고 피처 플래그 뒤에 숨겨지고 Merge된 기능에는 변경 로그 항목을 포함해서는 안 됩니다. 변경 로그 항목은 피처 플래그가 제거된 Merge Request이나 피처 플래그의 기본값이 활성화된 Merge Request에서 추가되어야 합니다. 기능에 데이터베이스 마이그레이션이 포함된 경우 데이터베이스 변경에 대한 변경 로그 항목을 포함해야 합니다.

note
EE에서만 사용되는 피처 플래그를 만들려면 --ee 플래그를 추가하세요: bin/feature-flag --ee

새로운 플래그 이름 지정

새로운 피처 플래그의 이름을 선택할 때 다음 가이드라인을 고려하세요:

  • 간결하지만 혼란스러운 것보다는 길고 설명이 잘 된 이름이 더 좋습니다.
  • 이름을 snake case로 작성하세요(my_cool_feature_flag).
  • 부정적인 단어를 피하기 위해 이름에 disable을 사용하지 마세요. 두 번 부정적으로 생각하거나 문서를 작성하기를 피하기 위함입니다. 대신 이름을 hide_, remove_, 또는 disallow_로 시작하도록 고려하세요.

    소프트웨어 엔지니어링에서 이 문제는 “부울 변수를 위한 부정적인 이름”로 알려져 있습니다. 그러나 부정적인 단어를 완전히 금지할 수 없으며, 기본적으로 비활성화된 플래그를 소개하거나, 플래그 뒤로 기능을 옮겨 플래그를 제거하거나 배우자 별로 플래그 비활성화를 위해 사용할 수 있습니다.

손상된 마스터 (main) 브랜치의 위험

caution
피처 플래그는 도입되는 MR에서 반드시 사용되어야 합니다. 그렇지 않으면 rspec:feature-flags 작업이 master 브랜치에서만 실행되기 때문에 손상된 마스터 시나리오를 유발합니다.

.patch 파일을 선택적으로 추가하여 피처 플래그 자동 제거

gitlab-housekeeperDeleteOldFeatureFlags keep를 사용하여 피처 플래그 코드를 자동으로 제거할 수 있습니다. 이 도구는 주기적으로 실행되어 코드에서 오래된 피처 플래그를 자동으로 정리합니다.

이 도구를 사용하여 코드에서 피처 플래그 사용을 자동으로 제거하려면 피처 플래그 YAML 파일과 동일하지만 .yml 확장자 대신 .patch 확장자를 사용한 파일을 추가할 수 있습니다.

예를들어 다음 단계를 통해 config/feature_flags/beta/my_feature_flag.yml에 대한 패치 파일을 생성할 수 있습니다.

  1. 피처 플래그 my_feature_flag 사용을 제거하기 위해 코드를 로컬에서 편집하고, 이미 활성화된 경우라고 가정합니다.
  2. git diff > config/feature_flags/beta/my_feature_flag.patch를 실행합니다.
  3. 피처 플래그 사용을 제거한 파일의 변경 사항을 되돌립니다.
  4. 피처 플래그를 추가하는 브랜치에 config/feature_flags/beta/my_feature_flag.patch 파일을 커밋합니다.

그러면 gitlab-housekeeper가 이 패치를 적용하여 자동으로 피처 플래그를 정리할 것입니다.

모든 피처 플래그 나열

특정 환경에서 모든 피처 플래그를 Slack으로 출력하기 위해 ChatOps를 사용하려면 run feature list 명령을 사용할 수 있습니다. 예를들어:

/chatops run feature list --dev
/chatops run feature list --staging

피처 플래그 전환

피처 플래그를 전환하는 방법에 대한 자세한 내용은 변경 사항 롤아웃을 참조하세요.

피처 플래그 삭제

피처 플래그를 삭제하는 자세한 내용은 피처 플래그 정리를 참조하세요.

피처 플래그로 개발

GitLab 코드베이스에서 피처 플래그를 사용하는 주요 방법은 두 가지가 있습니다:

백엔드

피처 플래그 인터페이스는 lib/feature.rb에 정의되어 있습니다. 이 인터페이스는 피처 플래그가 활성화되었는지 비활성화되었는지를 확인하기 위한 일련의 메소드를 제공합니다:

if Feature.enabled?(:my_feature_flag, project)
  # 피처 플래그가 활성화되었을 때 코드 실행
else
  # 피처 플래그가 비활성화되었을 때 코드 실행
end

if Feature.disabled?(:my_feature_flag, project)
  # 피처 플래그가 비활성화되었을 때 코드 실행
end

default_enabled:로 구성되지 않은 피처 플래그의 기본 동작은 YAML 정의로 제어됩니다.

만약 피처 플래그에 YAML 정의가 없다면, 개발 또는 테스트 환경에서 오류가 발생하며, 프로덕션 환경에서는 false가 반환됩니다.

정의 파일이 없는 피처 플래그 (experiment, worker, undefined 타입에만 허용됨)의 경우 Feature.enabled?Feature.disabled? 호출 시 type:을 전달해야 합니다.

if Feature.enabled?(:experiment_feature_flag, project, type: :experiment)
  # 피처 플래그가 활성화되었을 때 코드 실행
end

if Feature.disabled?(:worker_feature_flag, project, type: :worker)
  # 피처 플래그가 비활성화되었을 때 코드 실행
end

WARNING:
애플리케이션 로드 시점에 피처 플래그를 사용하지 마세요. 예를들어, `config/initializers/*`에서 `Feature` 클래스를 사용하거나 클래스 레벨에서 사용하는 것은 예기치 않은 오류를 유발할  있습니다.  오류는 플래그 어댑터가 의존하는 데이터베이스가 로드 시간에 존재하지 않아 발생합니다(특히 신규 설치 ). 호출자가 데이터베이스의 존재를 확인하는 것은 권장되지 않으며, 일부 어댑터는 데이터베이스를 전혀 필요로 하지 않을  있습니다(: HTTP 어댑터). 따라서 피처 플래그 설정 확인은 `Feature` 네임스페이스에 추상화되어야 합니다.  접근 방식은 피처 플래그가 변경될  애플리케이션을 다시로드해야 한다는 점을 의미합니다. 이에 따라 프로덕션에서 SRE에게 /API/Sidekiq 펠릿을 리로드해야 하며, 변화를 완전히 전개/롤백하는  시간이 소요됩니다. 이러한 이유로 환경 변수(: `ENV['YOUR_FEATURE_NAME']`) 또는 `gitlab.yml` 사용하세요.

다음은 피해야  패턴의 예시입니다:

```ruby
class MyClass
  if Feature.enabled?(:...)
    new_process
  else
    legacy_process
  end
end

#### 재귀 탐지

많은 피처 플래그가 있는 경우, 어떻게 호출되는지 항상 분명하지 않습니다. 하나의 피처 플래그 평가가 다른 피처 플래그의 평가를 요구하는 순환을 피하세요. 이로 인해 순환이 발생하면 이를 중단하고 기본 값을 반환합니다.

이러한 재귀 탐지가 정상적으로 작동하도록 하려면 항상 `Feature::enabled?`를 통해 기능 값을 접근하고, `Feature::get`의 하위 수준 사용을 피하세요. 이렇게 되면 우리는 `Feature::RecursionError` 예외를 에러 트래커에 기록합니다.

### 프론트엔드

UI 요소에 대한 피처 플래그를 사용할 때, 가능한 경우 기능이 활성화되기 전까지 절대 사용할 수 없도록하려면 백엔드 코드의 피처 플래그에 대해서도 반드시 사용해야 합니다.

`ApplicationController`를 상속하는 모든 컨트롤러에서 사용 가능한 `push_frontend_feature_flag` 메소드를 사용하세요. 이 메소드를 사용하여 피처 플래그의 상태를 노출할 수 있습니다. 예를들어:

```ruby
before_action do
  # 프로젝트나 사용자 범위로 지정하는 것을 선호합니다
  push_frontend_feature_flag(:vim_bindings, project)
end

def index
  # ...
end

def edit
  # ...
end

그런 다음 JavaScript에서 피처 플래그의 상태를 다음과 같이 확인할 수 있습니다:

if ( gon.features.vimBindings ) {
  // ...
}

JavaScript에서의 피처 플래그의 이름은 항상 camelCase이므로 gon.features.vim_bindings를 확인하는 것은 작동하지 않습니다.

experiment, worker, undefined 타입을 가진 피처 플래그의 경우, push_frontend_feature_flag 호출 시 type:을 전달해야 합니다:

before_action do
  push_frontend_feature_flag(:vim_bindings, project, type: :experiment)

기능 액터

특성 플래그는 액터와 사용하는 것이 강력히 권장됩니다. 액터는 특성 플래그를 특정 프로젝트, 그룹 또는 사용자에만 활성화하는 간단한 방법을 제공합니다. 이렇게 함으로써 로그 및 오류를 필터링할 수 있으므로 디버깅이 쉬워지며, 예를 들어 특성에 따라 로그와 오류를 필터링할 수 있습니다. 또한 사용자들이 영향을 받지 않도록 하는 동안 ‘gitlab-org’ 또는 ‘gitlab-com’ 그룹에서 먼저 특성을 활성화할 수 있게 됩니다.

액터는 특성을 단계적으로 롤아웃하는 간단한 방법도 제공합니다. 예를 들어 1%의 롤아웃이 특정 액터에 대해 특성을 활성화하면 해당 액터는 10%, 50%, 100%에서도 계속해서 해당 특성을 활성화할 것입니다.

GitLab은 현재 다음과 같은 특성 플래그 액터를 지원합니다:

  • User 모델
  • Project 모델
  • Group 모델
  • 현재 요청

액터는 Feature.enabled? 호출의 두 번째 매개변수입니다. Feature.enabled?의 모든 호출에서 동일한 액터 유형을 일관되게 사용해야 합니다.

# 잘못된 방식
Feature.enabled?(:feature_flag, project)
Feature.enabled?(:feature_flag, group)
Feature.enabled?(:feature_flag, user)

# 올바른 방식
Feature.enabled?(:feature_flag, group_a)
Feature.enabled?(:feature_flag, group_b)

# 또한 좋은 방식 - 각 액터 유형에 대해 별도의 플래그 사용
Feature.enabled?(:feature_flag_group, group)
Feature.enabled?(:feature_flag_user, user)

FeatureGate를 포함하는 모델은 .actor_from_id 클래스 메서드를 갖습니다. 모델의 ID가 있고 특성 플래그 상태를 확인하는 것 이외에 모델이 필요 없는 경우, 데이터베이스 쿼리를 실행하지 않고 .actor_from_id를 사용하여 특성 플래그 상태를 확인할 수 있습니다.

# 나쁜 방식 -- 불필요한 쿼리가 실행됨
Feature.enabled?(:feature_flag, Project.find(project_id))

# 좋은 방식 -- 프로젝트에 대한 쿼리 없음
Feature.enabled?(:feature_flag, Project.actor_from_id(project_id))

# 좋은 방식 -- 특성 플래그 확인 후 프로젝트 모델 사용
project = Project.find(project_id)
return unless Feature.enabled?(:feature_flag, project)
project.update!(column: value)

GitLab에서 제공하는 staging 및 production과 같은 환경에서 특성 플래그를 선택적으로 활성화 또는 비활성화하는 방법에 대한 자세한 내용은 GitLab 개발에서의 특성 플래그을 참고하세요.

현재 요청 액터

시간 단위 롤아웃을 사용하는 것은 권장되지 않으며, 각 호출별로 일관되지 않은 결과가 반환될 수 있습니다.

대신 현재 요청을 액터로 사용하는 것이 권장됩니다.

# 나쁜 방식
Feature.enable_percentage_of_time(:feature_flag, 40)
Feature.enabled?(:feature_flag)

# 좋은 방식
Feature.enable_percentage_of_actors(:feature_flag, 40)
Feature.enabled?(:feature_flag, Feature.current_request)

현재 요청을 액터로 사용할 때는 요청의 맥락 내에서 동일한 값을 반환해야 합니다. 현재 요청 액터는 SafeRequestStore를 사용하여 구현되었기 때문에 다음과 같은 곳에서 일관된 특성 플래그 값을 가져야 합니다:

  • Rack 요청
  • Sidekiq 워커 실행
  • ActionCable 워커 실행

기존의 시간 단위로부터 현재 요청 액터로의 특성 이동을 위해서는 새로운 특성 플래그를 생성하는 것이 권장됩니다. 기존 percentage_of_time 값과 코드 변경의 배포 및 percentage_of_actors로 전환하는 타이밍을 통제하는 것이 어려울 수 있기 때문입니다.

운영 환경에서 확인하기 위해 액터 사용

caution
운영 환경에서 테스트 환경으로 사용하는 것은 권장되지 않습니다. 제품화되지 않은 기능을 테스트하기 위해서는 테스트 환경을 사용하세요.

스테이징 환경을 사용하면 제품화된 환경과 유사한 환경에서 기능을 테스트할 수 있지만, 제품화된 환경에 특정한 이전 및 이후 성능 지표를 비교할 수는 없습니다. 개발 중인 특성 플래그를 사용하여 제품화한 프로젝트를 운영 환경에 두는 것은 유용할 수 있습니다. 이렇게 하면 Sitespeed 보고서와 같은 도구를 사용하여 새 코드의 지표를 확인할 수 있습니다.

이 접근 방식은 이미 이전 코드베이스를 Sitespeed로 추적하고 있는 경우에 더욱 유용하며 특성 플래그 롤아웃 전후의 성능을 정확하게 비교할 수 있게 합니다.

추가 객체를 액터로 활성화

특성 게이트를 기반으로 액터를 사용하려면 해당 모델이 flipper_id에 반응해야 합니다. 예를 들어 Foo 모델을 활성화하려면:

class Foo < ActiveRecord::Base
  include FeatureGate
end

FeatureGate를 포함하거나 flipper_id 메서드를 노출하는 모델만을 Feature.enabled?의 액터로 사용할 수 있습니다.

라이선스 기능에 대한 특성 플래그

라이선스 기능 이름과 동일한 특성 플래그 이름을 사용할 수 없습니다. 그렇게 한다면 네이밍 충돌이 발생할 수 있기 때문에 폭넓게 논의되었고 제거되었습니다.

라이선스 기능을 확인하려면 다른 이름으로 전용 특성 플래그를 추가하고 명시적으로 확인해야 합니다. 예를 들어:

Feature.enabled?(:licensed_feature_feature_flag, project) &&
  project.feature_available?(:licensed_feature)

특성 그룹

특성 그룹은 lib/feature.rb (.register_feature_groups 메서드)에 정적으로 정의되어야 하지만, 구현은 동적일 수 있습니다 (예: DB 쿼리).

lib/feature.rb에 정의한 후, 특정 특성을 특성 그룹에 활성화하기 위해 여기에서 features API의 feature_group 파라미터를 통해 활성화할 수 있습니다.

사용 가능한 특성 그룹은 다음과 같습니다:

그룹 이름 대상 범주 설명
gitlab_team_members 사용자 gitlab-com의 구성원에게 해당 기능을 활성화합니다.

특성 그룹은 다음과 같이 그룹 이름을 통해 활성화할 수 있습니다:

Feature.enable(:feature_flag_name, :gitlab_team_members)

로컬에서 특성 플래그 제어

레일스 콘솔에서

레일스 콘솔 (rails c)에서 특성 플래그를 활성화하려면 다음 명령을 입력하세요:

Feature.enable(:feature_flag_name)

비슷하게, 다음 명령은 특성 플래그를 비활성화합니다:

Feature.disable(:feature_flag_name)

특성 플래그를 지정된 게이트에 대해 활성화할 수도 있습니다:

Feature.enable(:feature_flag_name, Project.find_by_full_path("root/my-project"))

레일스 콘솔에서 매뉴얼으로 특성 플래그를 활성화 또는 비활성화할 때는 기본값이 덮어쓰입니다. 이로 인해 플래그의 default_enabled 속성을 변경하면 혼란스러울 수 있습니다.

특성 플래그를 기본 상태로 재설정하려면:

Feature.remove(:feature_flag_name)

브라우저에서

http://gdk.test:3000/rails/features에 액세스하여 로컬에서 특성 플래그를 관리할 수 있습니다.

로깅

특성 플래그의 사용 및 상태는 다음 중 하나의 경우에 로깅됩니다:

  • 특성 플래그 정의에서 log_state_changestrue로 설정된 경우
  • milestone이 현재 GitLab 버전보다 크거나 같은 마일스톤을 참조하는 경우

특성 플래그의 상태가 로깅되면 Kibana에서 "json.feature_flag_states": "feature_flag_name:1" 또는 "json.feature_flag_states": "feature_flag_name:0" 조건을 사용하여 식별할 수 있습니다. 예시는 여기에서에서 확인할 수 있습니다.

note
전체 요청의 20%만이 특성 플래그의 상태를 로깅합니다. 이는 feature_flag_state_logs 특성 플래그로 제어됩니다.

변경 로그

기능이 사용자에게 직접적으로(예: 기능 사용 가능) 또는 간접적으로(예: 배경 작업, 성능 향상 또는 데이터베이스 마이그레이션 업데이트의 이점을 살리는 능력) 접근할 수 없을 때에는 변경 로그를 도입하지 않으려 합니다.

  • 데이터베이스 마이그레이션은 Self-Managed형 고객이 업그레이드 전에 데이터베이스 변경 사항을 인식해야 하기 때문에 간접적으로 모든 사용자에게 접근 가능합니다. 이러한 이유로 인해 변경 로그 항목이 있어야 합니다.
  • 피처 플래그 뒤에 있는 변경 사항 중 기본적으로 비활성화된 피처 플래그에는 변경 로그 항목이 없어야 합니다.
  • 기본적으로 활성화된 피처 플래그 뒤에 있는 변경 사항은 변경 로그 항목이 있어야 합니다.
  • 피처 플래그 자체를 변경(플래그 제거, 기본 켜짐 설정)해야 할 때는 변경 로그 항목이 있어야 합니다. 변경 로그 항목의 유형을 결정하려면 플로차트를 사용하세요.

    flowchart LR FDOFF(플래그는 현재\n`기본: 꺼짐`) FDON(플래그는 현재\n`기본: 켜짐`) CDO{`기본: 켜짐`으로 변경} ACF(추가됨/변경됨/수정됨/'...') RF{플래그 제거} RF2{플래그 제거} NC(변경 로그 없음) RC(제거됨/변경됨) OTHER(기타) FDOFF -->CDO-->ACF FDOFF -->RF RF-->|새 코드 유지?| ACF RF-->|이전 코드 유지?| NC FDON -->RF2 RF2-->|이전 코드 유지?| RC RF2-->|새 코드 유지?| OTHER
  • 피처 플래그의 변경 로그는 플래그가 아닌 기능을 설명해야 하며, 기본 켜짐 피처 플래그가 새 코드를 유지하도록 제거된 경우를 제외하고는 피처 플래그를 설명해야 합니다(상기 플로차트에서 다른).
  • 피처 플래그는 버그 수정 또는 유지 관리 작업을 롤아웃하기 위해 사용될 수도 있습니다. 이러한 시나리오에서 변경 로그는 그것과 관련돼야 하며, 예를 들어 수정됨 또는 기타여야 합니다.

테스트에서의 피처 플래그

코드베이스에 피처 플래그를 도입하면 테스트해야 할 추가적인 코드 경로가 만들어집니다. 피처 플래그에 영향을 받는 모든 코드에 대한 자동화된 테스트를 포함하는 것이 매우 권장됩니다. 이를 통해 기능이 올바르게 작동하는지를 확인할 수 있습니다. 자동화된 테스트가 양상별로 포함되지 않은 경우, 테스트되지 않은 코드 경로와 연관된 기능은 프로덕션 배포 전에 매뉴얼으로 테스트되어야 합니다.

테스트 환경에서는 모든 피처 플래그가 기본적으로 활성화됩니다. 피처 플래그는 spec/spec_helper.rb 파일에서 기본적으로 비활성화할 수 있습니다. 필요한 경우 플래그가 왜 비활성화되어야 하는지에 대한 인라인 주석을 추가할 수 있습니다. 가능하다면 참조용 이슈 URL도 첨부할 수 있습니다.

caution
이 규칙은 기본적으로 피처 플래그를 활성화하지 않습니다. 엔드투엔드(품질 보증) 테스트에는 다른 피처 플래그 사용 프로세스가 있습니다.

테스트에서 피처 플래그를 비활성화하려면 stub_feature_flags 도우미를 사용하세요. 예를 들어, 테스트에서 ci_live_trace 피처 플래그를 전역적으로 비활성화하려면 다음과 같이 사용합니다.

stub_feature_flags(ci_live_trace: false)

Feature.enabled?(:ci_live_trace) # => false

각 경로의 두 가지 테스트를 수행하는 흔한 패턴은 다음과 같습니다.

it 'ci_live_trace works' do
  # 기본적으로 테스트에서 ci_live_trace가 활성화된 것으로 가정한 테스트
  Feature.enabled?(:ci_live_trace) # => true
end

context 'when ci_live_trace is disabled' do
  before do
    stub_feature_flags(ci_live_trace: false)
  end
  
  it 'ci_live_trace does not work' do
    Feature.enabled?(:ci_live_trace) # => false
  end
end

특정 액터에 대해 피처 플래그가 활성화되도록 테스트를 설정하려면 도우미에 전달되는 옵션에서 이를 지정할 수 있습니다. 예를 들어, 특정 프로젝트에 ci_live_trace 피처 플래그를 활성화하려면 다음과 같이 지정할 수 있습니다.

project1, project2 = build_list(:project, 2)

# 기능은 project1에서만 활성화됨
stub_feature_flags(ci_live_trace: project1)

Feature.enabled?(:ci_live_trace) # => false
Feature.enabled?(:ci_live_trace, project1) # => true
Feature.enabled?(:ci_live_trace, project2) # => false

FlipperGate의 동작은 다음과 같습니다:

  1. 특정 액터에 대한 오버라이드를 활성화할 수 있습니다.
  2. 특정 액터에 대한 오버라이드를 비활성화(제거)할 수 있고, 기본 상태로 돌아갑니다.
  3. 특정 액터를 명시적으로 비활성화한 상태를 모델링할 방법은 없습니다.
Feature.enable(:my_feature)
Feature.disable(:my_feature, project1)
Feature.enabled?(:my_feature) # => true
Feature.enabled?(:my_feature, project1) # => true

Feature.disable(:my_feature2)
Feature.enable(:my_feature2, project1)
Feature.enabled?(:my_feature2) # => false
Feature.enabled?(:my_feature2, project1) # => true

have_pushed_frontend_feature_flags

have_pushed_frontend_feature_flags를 사용하여 push_frontend_feature_flag가 HTML에 피처 플래그를 추가했는지를 테스트하세요.

예를 들어,

stub_feature_flags(value_stream_analytics_path_navigation: false)

visit group_analytics_cycle_analytics_path(group)

expect(page).to have_pushed_frontend_feature_flags(valueStreamAnalyticsPathNavigation: false)

stub_feature_flags vs Feature.enable*

테스트 환경에서 피처 플래그를 활성화하기 위해 stub_feature_flags를 사용하는 것이 좋습니다. 이 메서드는 간단하고 명확하게 설명된 인터페이스를 제공합니다.

그러나 경우에 따라 더 복잡한 동작을 테스트해야 할 필요가 있는데, 이런 경우 .enable_percentage_of_time 또는 .enable_percentage_of_actors를 사용하여 피처 플래그의 백분율 출시를 테스트할 수 있습니다.

# Good: 명시적으로 비활성화해야 하는 기능, 미지정시 기본적으로 활성화되기 때문
stub_feature_flags(my_feature: false)
stub_feature_flags(my_feature: true)
stub_feature_flags(my_feature: project)
stub_feature_flags(my_feature: [project, project2])

# Bad
Feature.enable(:my_feature_2)

# Good: 50% 확률로 my_feature를 활성화
Feature.enable_percentage_of_time(:my_feature_3, 50)

# Good: 50%의 액터/게이트/무언가로 my_feature를 활성화
Feature.enable_percentage_of_actors(:my_feature_4, 50)

각 정의된 상태를 갖는 피처 플래그는 테스트 실행 시 유지됩니다.

Feature.persisted_names.include?('my_feature') => true
Feature.persisted_names.include?('my_feature_2') => true
Feature.persisted_names.include?('my_feature_3') => true
Feature.persisted_names.include?('my_feature_4') => true

액터 스텁

특정 액터에 대해 피처 플래그를 활성화하려면 해당 표시를 스텁 처리할 수 있습니다. Feature.enabled?Feature.disabled?에 전달되는 게이트는 FeatureGate를 포함하는 객체여야 합니다.

스펙에서는 stub_feature_flag_gate 메서드를 사용하여 사용자 정의 액터를 빠르게 만들 수 있습니다.

gate = stub_feature_flag_gate('CustomActor')

stub_feature_flags(ci_live_trace: gate)

Feature.enabled?(:ci_live_trace) # => false
Feature.enabled?(:ci_live_trace, gate) # => true

특정 액터에 대해 피처 플래그를 비활성화할 수도 있습니다.

gate = stub_feature_flag_gate('CustomActor')

stub_feature_flags(ci_live_trace: false, thing: gate)

테스트에서 피처 플래그 엔진 제어

테스트 환경에서 Flipper 엔진은 메모리 모드 Flipper::Adapters::Memory로 작동합니다. productiondevelopment 모드는 Flipper::Adapters::ActiveRecord를 사용합니다.

Flipper::Adapters::Memory 또는 ActiveRecord 모드를 사용할지 제어할 수 있습니다.

stub_feature_flags: true (기본 및 선호)

이 모드에서 Flipper는 Flipper::Adapters::Memory를 사용하도록 구성되어 모든 피처 플래그를 기본적으로 켜고 처음 사용될 때 유지됩니다.

특정하지 않은 컨텍스트에서 피처 플래그의 동작이 테스트되지 않도록 해야 합니다.

stub_feature_flags: false

이 설정은 메모리 스털된 flipper를 비활성화하고 Flipper::Adapters::ActiveRecord를 사용하여 productiondevelopment이 사용하는 모드를 사용합니다.

이 모드는 실제로 ActiveRecord와의 상호 작용을 테스트하려는 경우에만 사용해야 합니다.

End-to-end (QA) 테스트

End-to-end 테스트에서의 피처 플래그 전환은 다르게 작동합니다. End-to-end 테스트 프레임워크는 Rails나 데이터베이스에 직접 액세스할 수 없기 때문에 Flipper를 사용할 수 없습니다. 대신 공개 API를 사용합니다. 각 end-to-end 테스트는 테스트 중에 피처 플래그를 활성화하거나 비활성화할 수 있습니다. 또는 GitLab 리포지터리의 qa 디렉터리에서 실행할 때 또는 GitLab QA를 통해 테스트를 실행할 때 하나 이상의 테스트 전에 피처 플래그를 활성화 또는 비활성화할 수 있습니다.

위에서 언급했듯이, end-to-end 테스트에서 피처 플래그는 기본적으로 활성화되지 않습니다. 이는 end-to-end 테스트가 소스 코드에 구현된 기본 상태의 피처 플래그 또는 테스트 중인 GitLab 인스턴스의 현재 상태에서 실행됨을 의미합니다. 피처 플래그가 명시적으로 활성화/비활성화되도록 테스트가 작성되지 않는 한입니다.

피처 플래그가 Staging 또는 GitLab.com에서 변경되면 Slack 메시지가 #qa-staging 또는 #qa-production 채널에 게시되어 파이프라인 담당자가 피처 플래그 변경과 관련된 장애가 있는지 쉽게 판단할 수 있도록 도와줍니다. 그러나 변경 작업 중에 예기치 않은 실패를 피하려면 피처 플래그 활성화 상태에서 end-to-end 테스트가 통과하는지 확인하여 도울 수 있습니다.

피처 플래그로 Sidekiq 워커 동작 제어

worker 유형의 피처 플래그를 사용하여 Sidekiq 워커의 동작을 제어할 수 있습니다.

Sidekiq 작업 연기

비활성화된 상태에서 run_sidekiq_jobs_{WorkerName} 형식의 피처 플래그는 워커의 실행을 나중에 예약함으로써 작업을 연기합니다. 해당 피처 플래그는 모든 워커에 대해 기본적으로 활성화되어 있습니다. 작업을 연기하면 워커 인스턴스의 경합 행동이 인프라 리소스(데이터베이스 및 데이터베이스 연결 풀과 같은)를 포화시키는 사건 발생 시 유용할 수 있습니다. 구현은 SkipJobs Sidekiq server middleware에서 찾을 수 있습니다.

note
피처 플래그가 비활성화되어 있으면 작업이 무기한으로 연기됩니다. 워커가 안전하게 처리될 때 피처 플래그를 제거하는 것이 중요합니다.

false로 설정하면 작업의 100%가 연기됩니다. 처리를 재개하려면 시간의 백분율 롤아웃을 사용할 수 있습니다. 예:

# 어떤 작업도 실행하지 않고 모든 작업을 연기
/chatops run feature set run_sidekiq_jobs_SlowRunningWorker false

# 작업의 10%만 실행, 작업의 90%를 연기
/chatops run feature set run_sidekiq_jobs_SlowRunningWorker 10

# 작업의 50%만 실행, 작업의 50%를 연기
/chatops run feature set run_sidekiq_jobs_SlowRunningWorker 50

# 모든 작업 정상 실행으로 복귀
/chatops run feature delete run_sidekiq_jobs_SlowRunningWorker

Sidekiq 작업 삭제

작업을 연기하는 대신, drop_sidekiq_jobs_{WorkerName} 피처 플래그를 활성화하여 작업을 완전히 삭제할 수 있습니다. 작업이 미래에 처리되어야 할 필요가 없는 경우에만 이 피처 플래그를 사용합니다.

# 모든 작업 삭제
/chatops run feature set drop_sidekiq_jobs_SlowRunningWorker true

# 작업 정상 처리
/chatops run feature delete drop_sidekiq_jobs_SlowRunningWorker
note
작업을 연기하는 피처 플래그(run_sidekiq_jobs_{WorkerName})보다 작업을 삭제하는 피처 플래그(drop_sidekiq_jobs_{WorkerName})가 우선합니다. 즉, drop_sidekiq_jobs가 활성화되고 run_sidekiq_jobs가 비활성화된 경우 모든 작업이 완전히 삭제됩니다.