InfoGrab DocsInfoGrab Docs

GitLab 개발에서의 피처 플래그

요약

이 페이지는 개발자들이 피처 플래그를 통해 GitLab 제품의 개발 및 운영에 기여하는 방법을 설명합니다. 새로 도입된 피처 플래그는 모두 기본적으로 비활성화되어야 하며 액터와 함께 사용해야 합니다. (최신) GitLab 개발 및 운영에서 피처 플래그 사용

이 페이지는 개발자들이 피처 플래그를 통해 GitLab 제품의 개발 및 운영에 기여하는 방법을 설명합니다. 자신의 애플리케이션에서 기능을 표시하거나 숨기기 위한 커스텀 피처 플래그를 생성하려면 피처 플래그 생성을 참조하세요. GitLab에서 사용 가능한 피처 플래그 전체 목록도 확인할 수 있습니다.

새로 도입된 피처 플래그는 모두 기본적으로 비활성화되어야 하며 액터와 함께 사용해야 합니다.

설계 문서:

이 문서는 피처 플래그의 내부 사용 개선 에픽의 일환으로 지속적으로 작업 중입니다. 제안 사항은 새 이슈로 등록하고 해당 에픽에 연결해 주세요.

피처 플래그 라이프사이클 개요를 확인하거나, 피처 플래그 사용 여부를 결정하는 데 도움이 필요하다면 피처 플래그 라이프사이클 핸드북 페이지를 참조하세요.

피처 플래그를 사용해야 하는 경우#

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

외부 API 소비자에서 피처 플래그를 사용하지 않기#

피처 플래그는 내부 구현 세부 사항이며 공개 API 계약의 일부가 아닙니다. GraphQL metadata.featureFlags 필드나 더 이상 사용되지 않는 featureFlagEnabled 필드를 통해 피처 플래그를 조회하는 외부 API 소비자(IDE 확장, Duo CLI, CI 통합 등)는 특정 위험에 직면합니다. 플래그가 모노리스에서 제거될 때 외부 소비자가 아직 업데이트되지 않을 수 있으며, 이는 사소한 UI 문제부터 고객에게 영향을 미치는 인시던트에 이르기까지 다양한 문제를 초래할 수 있습니다.

외부 API 소비자에서 피처 플래그로 작업할 때는 다음 지침을 따르세요:

  • API 필드 또는 Application Settings를 선호하세요. 가능하다면 외부 API 소비자에서 피처 플래그를 조회하지 않도록 하세요. 대신 소비자가 조회할 수 있는 전용 API 필드나 Application Setting을 도입하세요. 이 값들은 플래그가 제거된 후에도 유지됩니다.

  • 실패 개방(fail-open) 동작을 구현하세요. 외부 API 소비자에서 피처 플래그를 반드시 사용해야 한다면, "fail-open" 메커니즘을 구현하세요. 롤아웃 마일스톤이 확정되면 소비자는 기본적으로 플래그가 활성화된 것으로 처리해야 합니다. 롤아웃 마일스톤이 확인되는 즉시 소비자를 업데이트하세요. GitLab Language Server의 예시를 참조하세요.

  • 제거 전에 사용자 업그레이드 패턴을 고려하세요. 외부 API 소비자가 사용하는 플래그를 제거하기 전에, 사용자가 클라이언트를 얼마나 빨리 업데이트하는지 평가하고 제거에 가장 안전한 시점을 결정하세요.

장기 설정에 피처 플래그를 사용하지 않기#

피처 플래그는 단기적으로 사용되도록 설계되었습니다. 오랜 기간 동안 사용자/그룹/프로젝트별로 기능을 활성화할 수 있도록 피처 플래그를 추가하려는 경우, Cascading Settings 또는 Application Settings 도입을 고려하세요. 설정은 고객이 GitLab.com 또는 Self-managed에서 직접 기능을 활성화하거나 비활성화할 수 있게 하며 필요한 만큼 코드베이스에 유지될 수 있습니다. 반면에 사용자는 GitLab.com에서 스스로 피처 플래그를 활성화하거나 비활성화할 방법이 없으며 Self-managed 관리자만 피처 플래그를 변경할 수 있습니다. 또한 GitLab Dedicated에서는 피처 플래그가 지원되지 않으므로, 피처 플래그를 설정의 대체재로 사용하지 않아야 하는 또 다른 이유입니다.

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

피처 플래그 활용 여부를 결정할 때 다음 사항을 고려해야 합니다:

  • 피처 플래그는 기본적으로 비활성화되어야 합니다.

  • 피처 플래그 관리의 필요성을 줄이기 위해 피처 플래그는 가능한 한 짧은 기간 동안 코드베이스에 유지되어야 합니다.

  • 피처 플래그를 운영하는 담당자는 문서와 다른 이해관계자들에게 피처 플래그 뒤에 있는 기능의 상태를 명확히 전달할 책임이 있습니다. 피처 플래그가 필요하다는 것이 명확해지는 즉시 이슈 설명에 피처 플래그 이름과 기본값(활성화 또는 비활성화)을 업데이트해야 합니다.

  • 피처 플래그를 도입하거나, 상태를 업데이트하거나, 기능이 안정적이라고 판단되어 기존 피처 플래그를 제거하는 머지 리퀘스트에는 ~"feature flag" 라벨이 할당되어야 합니다.

기능 구현이 여러 머지 리퀘스트에 걸쳐 제공되는 경우:

  • 플래그를 사용하는 첫 번째 머지 리퀘스트에서 기본적으로 비활성화새 피처 플래그를 생성하세요. 플래그는 별도로 추가해서는 안 됩니다.

  • 하나 이상의 머지 리퀘스트를 통해 점진적인 변경 사항을 제출하고, 추가된 새 코드는 피처 플래그가 활성화된 경우에만 실행될 수 있도록 하세요. 개발 중에 로컬 GDK에서 피처 플래그를 활성화 상태로 유지할 수 있습니다.

  • 기능이 다른 팀원에 의해 테스트될 준비가 되면 초기 문서를 작성하세요. 피처 플래그의 상태에 대한 세부 정보를 포함하세요.

  • 특정 그룹/프로젝트/사용자에 대해 피처 플래그를 활성화하고 구현에 문제가 없는지 확인하세요. 문서가 없는 경우 gitlab-org/gitlab과 같은 공개 프로젝트에 피처 플래그를 활성화하지 마세요. 팀원과 기여자들이 공개 프로젝트에서 활성화된 것을 보면 기능 사용 방법에 대한 문서를 검색할 수 있습니다.

  • 기능이 GitLab Self-Managed 인스턴스를 포함한 프로덕션 사용에 준비가 되면 하나의 머지 리퀘스트를 열어 다음 작업을 수행하세요:

최신 플래그 상태를 설명하는 문서를 업데이트하세요.

  • 변경 로그 항목을 추가하세요.

  • 새로운 동작을 활성화하기 위해 피처 플래그를 제거하거나, 피처 플래그를 기본 활성화로 전환하세요(opsbeta 피처 플래그에만 해당).

피처 플래그 제거가 여러 머지 리퀘스트에 걸쳐 제공되는 경우:

  • 피처 플래그의 값 변경은 머지 리퀘스트에서 유일한 변경이어야 합니다. 피처 플래그가 코드베이스에 존재하는 한, 두 상태 모두 완전히 기능해야 합니다(기능이 켜져 있을 때와 꺼져 있을 때).

  • 피처 플래그의 모든 언급이 제거된 후 레거시 코드를 제거할 수 있습니다. 피처 플래그 롤아웃 이슈의 단계를 따라야 하며, 단계를 건너뛰어야 하는 경우 이슈에 이유를 설명하는 댓글을 추가해야 합니다.

피처 플래그가 기능 릴리스를 적어도 한 달(= 한 번의 릴리스) 지연시킬 것이라고 생각하기 쉽습니다. 하지만 그렇지 않습니다. 피처 플래그는 특정 기간(예: 적어도 한 번의 릴리스) 동안 유지될 필요가 없으며, 대신 기능이 안정적이라고 판단될 때까지 유지되어야 합니다. 안정적이라는 것은 정전과 같은 문제를 일으키지 않고 GitLab.com에서 작동한다는 것을 의미합니다.

기본 브랜치 손상 위험#

피처 플래그는 도입하는 MR에서 반드시 사용해야 합니다. 그렇지 않으면 기본 브랜치에서만 실행되는 rspec:feature-flags job으로 인해 기본 브랜치 손상 시나리오가 발생합니다.

피처 플래그 유형#

예상 사용 방식에 맞는 피처 플래그 유형을 선택하세요.

gitlab_com_derisk 유형#

gitlab_com_derisk 피처 플래그는 GitLab.com 배포의 위험을 줄이기 위해 사용되는 단기 피처 플래그입니다. GitLab에서 사용되는 대부분의 피처 플래그는 gitlab_com_derisk 유형입니다.

제약 조건#

  • default_enabled: true로 설정해서는 안 됩니다. 이 종류의 피처 플래그는 GitLab.com의 위험을 줄이기 위한 것이므로, GitLab.com에서 활성화된 후에는 코드베이스에 플래그를 유지할 필요가 없습니다. default_enabled: true는 이 유형의 피처 플래그에 효과가 없습니다.

  • 최대 수명: 기본 브랜치에 병합된 후 2개월

  • 문서화: 이 유형의 피처 플래그는 단기적이고 배포 관련이므로 GitLab의 모든 피처 플래그 페이지에 문서화할 필요가 없습니다.

  • 롤아웃 이슈: Feature flag Roll Out 템플릿에서 생성된 롤아웃 이슈가 반드시 있어야 합니다.

사용법#

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

GitLab Rails 콘솔에서 활성화 및 비활성화하려면 다음을 실행하세요:

# To enable it for the instance:
Feature.enable(:<dev_flag_name>)

# To disable it for the instance:
Feature.disable(:<dev_flag_name>)

# To enable for a specific project:
Feature.enable(:<dev_flag_name>, Project.find(<project id>))

# To disable for a specific project:
Feature.disable(:<dev_flag_name>, Project.find(<project id>))

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

# Check if the feature flag is enabled
Feature.enabled?(:dev_flag_name)

# Check if the feature flag is disabled
Feature.disabled?(:dev_flag_name)

wip 유형#

일부 기능은 복잡하여 여러 MR을 통해 구현해야 합니다. 완전히 구현되기 전까지는 누구에게도 숨겨야 합니다. 이 경우 wip("Work In Progress"의 약자) 피처 플래그를 사용하면 실제로 기능을 사용하지 않고도 모든 변경 사항을 main 브랜치에 병합할 수 있습니다.

기능이 완성되면 기능이 고객에게 어떻게 제공/문서화될지에 따라 피처 플래그 유형을 gitlab_com_derisk 또는 beta 유형으로 변경할 수 있습니다.

제약 조건#

  • default_enabled: true로 설정해서는 안 됩니다. 필요한 경우 기능이 완성되면 이 유형을 beta로 변경할 수 있습니다.

  • 최대 수명: 기본 브랜치에 병합된 후 4개월

  • 문서화: 이 유형의 피처 플래그는 대부분 미완성 코드를 숨기고 있으므로 GitLab의 모든 피처 플래그 페이지에 문서화할 필요가 없습니다.

  • 롤아웃 이슈: wip 피처 플래그는 활성화되기 전에 다른 유형으로 전환되어야 하므로 롤아웃 이슈가 필요하지 않을 가능성이 높습니다.

사용법#

# Check if feature flag is enabled
Feature.enabled?(:my_wip_flag, project)

# Check if feature flag is disabled
Feature.disabled?(:my_wip_flag, project)

# Push feature flag to Frontend
push_frontend_feature_flag(:my_wip_flag, project)

beta 유형#

현재 형태로 설계된 모든 사용 사례에 대해 기능을 확장, 지원, 유지할 수 있을지 확신하지 못할 수 있습니다(예시). 기능이 MVC로 간주될 만큼 충분하지 않은 시나리오도 있습니다. 이 경우 플래그를 제공하면 엔지니어와 고객이 성능이 충분해질 때까지 새 기능을 비활성화할 수 있습니다.

제약 조건#

  • default_enabled: 확장성 문제가 발생할 경우 비활성화 가능성과 함께 베타 버전의 모든 사람에게 기능을 "릴리스"할 수 있도록 true로 설정할 수 있습니다(이상적으로는 특정 온프레미스 설치에 대해서만 비활성화).

  • 최대 수명: 기본 브랜치에 병합된 후 6개월

  • 문서화: 이 유형의 피처 플래그는 GitLab의 모든 피처 플래그 페이지에 반드시 문서화되어야 합니다. 해당 페이지는 YAML 정의 파일에서 문서 빌드 중에 자동 생성되므로 수동으로 편집할 필요가 없습니다.

  • 롤아웃 이슈: Feature flag Roll Out 템플릿에서 생성된 롤아웃 이슈가 반드시 있어야 합니다.

사용법#

# Check if feature flag is enabled
Feature.enabled?(:my_beta_flag, project)

# Check if feature flag is disabled
Feature.disabled?(:my_beta_flag, project)

# Push feature flag to Frontend
push_frontend_feature_flag(:my_beta_flag, project)

ops 유형#

ops 피처 플래그는 GitLab 제품 동작의 운영적 측면을 제어하는 장기 피처 플래그입니다. 예를 들어 Sidekiq 워커 동작과 같이 성능에 영향을 미칠 수 있는 기능을 비활성화하는 피처 플래그입니다.

이 유형 사용은 인스턴스/그룹/프로젝트/사용자 설정을 도입하지 않겠다는 의식적인 결정을 따라야 합니다.

ops 유형 플래그는 무제한 수명을 가지지만, 12개월마다 여전히 필요한지 평가해야 합니다. 그렇다면 ops 피처 플래그가 여전히 사용 중임을 확인하기 위해 milestone 필드를 최신 마일스톤으로 업데이트해야 합니다.

제약 조건#

  • default_enabled: 대부분의 경우 false로 설정해야 하며, 임시 확장성 문제를 해결하거나 프로덕션 문제를 디버그하는 경우에만 활성화해야 합니다.

  • 최대 수명: 무제한, 단 12개월마다 평가 필요

  • 문서화: 이 유형의 피처 플래그는 GitLab의 모든 피처 플래그 페이지에 반드시 문서화되어야 하며, 사용 가능한 상황을 설명하는 운영 런북과 연결되어야 합니다. 해당 페이지는 YAML 정의 파일에서 문서 빌드 중에 자동 생성되므로 수동으로 편집할 필요가 없습니다.

  • 롤아웃 이슈: 활성화 또는 비활성화 시점을 예측하기 어려우므로 롤아웃 이슈가 필요하지 않을 가능성이 높습니다.

사용법#

# Check if feature flag is enabled
Feature.enabled?(:my_ops_flag, project)

# Check if feature flag is disabled
Feature.disabled?(:my_ops_flag, project)

# Push feature flag to Frontend
push_frontend_feature_flag(:my_ops_flag, project)

experiment 유형#

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

experiment 피처 플래그는 beta 피처 플래그와 동일한 기준을 따라야 하지만 인터페이스에 몇 가지 차이점이 있습니다. experiment 피처 플래그에는 Experiment 추적 템플릿을 사용하여 생성된 롤아웃 이슈가 있어야 합니다. 자세한 내용은 실험 가이드에서 확인할 수 있습니다.

제약 조건#

  • default_enabled: true로 설정해서는 안 됩니다.

  • 최대 수명: 기본 브랜치에 병합된 후 6개월

worker 유형#

worker 피처 플래그는 Sidekiq job 지연과 같이 Sidekiq 워커 동작을 제어할 수 있는 특수 ops 플래그입니다.

worker 피처 플래그는 워커 이름 자체를 사용하여 동적으로 생성될 수 있으므로(예: run_sidekiq_jobs_AuthorizedProjectsWorker) YAML 정의가 없을 수 있습니다. worker 유형 피처 플래그 사용 예시는 Sidekiq job 지연에서 확인할 수 있습니다.

markdown_cache 유형#

markdown_cache 피처 플래그는 Gitlab::MarkdownCache::CACHE_COMMONMARK_VERSION 버전 상향의 단계적 롤아웃을 주도합니다. worker 플래그와 마찬가지로 YAML 정의가 없으며, 이름은 대상 캐시 버전에서 생성됩니다(예: markdown_cache_stochastic_rollout_34). 모든 버전 상향이 자체 플래그를 사용하기 때문에 새 플래그는 비활성화된 상태로 시작하며, 따라서 이전 버전 상향 플래그에 설정된 백분율은 다음 버전으로 이어지지 않습니다. 롤아웃 절차는 CACHE_COMMONMARK_VERSION 버전 상향의 단계적 롤아웃을 참조하세요.

(사용 중단) development 유형#

development 유형은 gitlab_com_derisk, wip, beta 피처 플래그 유형으로 대체되어 사용 중단되었습니다.

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

개발 환경(RAILS_ENV=development) 또는 테스트 환경(RAILS_ENV=test)에서 실행할 때 모든 피처 플래그 사용이 엄격하게 검증됩니다.

이 프로세스는 코드베이스에서 일관된 피처 플래그 사용을 보장하기 위한 것입니다. 모든 피처 플래그는 다음을 반드시 충족해야 합니다:

  • 알려진 것이어야 합니다. 명시적으로 정의된 피처 플래그만 사용하세요(experiment, worker, undefined 유형의 피처 플래그 제외).

  • 두 번 정의되지 않아야 합니다. FOSS 또는 EE 중 하나에만 정의해야 하며, 둘 다에 정의해서는 안 됩니다.

  • 정의 파일이 없는 피처 플래그의 경우, 모든 호출에서 유효하고 일관된 type:을 사용하세요.

  • 소유자가 있어야 합니다.

GitLab이 알고 있는 모든 피처 플래그는 다음에 저장된 YAML 파일에 자체 문서화되어 있습니다:

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

필드 필수 여부 설명
name 피처 플래그 이름.
description 피처 플래그 이유에 대한 간단한 설명.
type 피처 플래그 유형.
default_enabled 피처 플래그의 기본 상태.
introduced_by_url 피처 플래그를 도입한 머지 리퀘스트의 URL.
milestone 피처 플래그가 생성된 마일스톤.
group 피처 플래그를 소유한 그룹.
feature_issue_url 아니오 원본 기능 이슈의 URL.
rollout_issue_url 아니오 피처 플래그 롤아웃을 다루는 이슈의 URL.
log_state_changes 아니오 피처 플래그의 상태를 로깅하는 데 사용.

RAILS_ENV=production에서 실행 시 모든 유효성 검사가 건너뜁니다.

새 피처 플래그 생성#

GitLab Pages는 피처 플래그에 대해 다른 프로세스를 사용합니다.

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

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

$ bin/feature-flag my_feature_flag
>> Specify the feature flag type
?> beta
You picked the type 'beta'

>> Specify the group label to which the feature flag belongs, from the following list:
1. group::group1
2. group::group2
?> 2
You picked the group 'group::group2'

>> URL of the original feature issue (enter to skip):
?> https://gitlab.com/gitlab-org/gitlab/-/issues/435435

>> URL of the MR introducing the feature flag (enter to skip and let Danger provide a suggestion directly in the MR):
?> https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141023

>> Username of the feature flag DRI (enter to skip):
?> bob

>> Is this an EE only feature (enter to skip):
?> [Return]

>> Press any key and paste the issue content that we copied to your clipboard! 🚀
?> [Return automatically opens the "New issue" page where you only have to paste the issue content]

>> URL of the rollout issue (enter to skip):
?> https://gitlab.com/gitlab-org/gitlab/-/issues/437162

create 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

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

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

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

새 플래그 이름 짓기#

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

피처 플래그가 보호하는 기능을 설명하세요.

길고 설명적인 이름이 짧지만 혼란스러운 이름보다 낫습니다.

_mvc, _alpha, _beta 등과 같이 기능의 상태/단계를 나타내는 이름을 피하세요.

snake case로 이름을 작성하세요(my_cool_feature_flag).

이중 부정을 생각하거나 문서화해야 하는 것을 피하기 위해 이름에 disable을 사용하지 마세요. 대신 hide_, remove_, 또는 disallow_로 시작하는 이름을 고려하세요.

소프트웨어 엔지니어링에서 이 문제는 "부울 변수에 부정적인 이름"으로 알려져 있습니다. 하지만 기본적으로 비활성화된 플래그를 도입하거나, 피처 플래그 뒤로 이동하여 기능을 제거하거나, 액터별로 플래그를 선택적으로 비활성화하기 위해 부정적인 단어를 완전히 금지할 수는 없습니다.

master(main) 브랜치 손상 위험#

피처 플래그는 도입하는 MR에서 반드시 사용해야 합니다. 그렇지 않으면 master 브랜치에서만 실행되는 rspec:feature-flags job으로 인해 master 손상 시나리오가 발생합니다.

피처 플래그 자동 제거를 위한 .patch 파일 선택적 추가#

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

이 도구가 코드에서 피처 플래그 사용을 자동으로 제거하려면 피처 플래그 YAML 파일과 함께 .patch 파일을 추가할 수 있습니다. 파일 이름은 .yml 확장자 대신 .patch 확장자를 사용한다는 점을 제외하고 동일해야 합니다.

예를 들어 다음 단계를 사용하여 config/feature_flags/beta/my_feature_flag.yml에 대한 패치 파일을 만들 수 있습니다:

  • 깨끗한 Git 작업 디렉터리가 있는지 확인하세요.

  • config/feature_flags/beta/my_feature_flag.yml을 삭제하세요.

  • 피처 플래그가 이미 활성화되어 있고 기능이 진행 중인 것처럼 my_feature_flag의 모든 사용을 제거하기 위해 코드를 로컬에서 편집하세요.

  • git diff > config/feature_flags/beta/my_feature_flag.patch를 실행하세요. 피처 플래그가 beta 플래그가 아닌 경우, 패치 파일이 피처 플래그를 정의하는 YAML 파일과 동일한 디렉터리에 있는지 확인하세요.

  • config/feature_flags/beta/my_feature_flag.yml 삭제를 취소하세요.

  • 피처 플래그 사용을 제거하기 위해 편집한 파일의 변경 사항을 취소하세요.

  • 피처 플래그를 추가하는 브랜치에 패치 파일을 커밋하세요.

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

모든 피처 플래그 나열#

ChatOps를 사용하여 환경의 모든 피처 플래그를 Slack으로 출력하려면 run feature list 명령을 사용할 수 있습니다. 예:

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

피처 플래그 토글#

피처 플래그 토글에 대한 자세한 내용은 변경 사항 롤아웃을 참조하세요.

피처 플래그 삭제#

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

ops 피처 플래그를 애플리케이션 설정으로 마이그레이션#

애플리케이션 설정을 백필하고 코드에서 설정을 사용하는 변경 사항은 동일한 마일스톤에 병합되어야 합니다.

ops 피처 플래그를 애플리케이션 설정으로 마이그레이션하려면:

  • 애플리케이션 설정에서 설정을 저장할 기존 JSONB 칼럼을 생성하거나 식별하세요.

  • 애플리케이션 설정 기본값은 피처 플래그 YAML 정의의 default_enabled:와 일치해야 합니다.

  • 칼럼을 백필하는 마이그레이션을 작성하세요. 이를 통해 기본 동작을 거부한 인스턴스가 동일한 상태를 유지할 수 있습니다. 마이그레이션에서 Feature.enabled? 또는 Feature.disabled?를 사용하지 마세요. Gitlab::Database::MigrationHelpers::FeatureFlagMigratorHelpers 마이그레이션 헬퍼를 사용하세요. 이 헬퍼는 명시적으로 true 또는 false로 설정된 피처 플래그만 마이그레이션합니다. 피처 플래그가 비율이나 특정 액터에 대해 설정된 경우 기본값이 사용됩니다.

  • 관리자 영역에서 기능을 활성화하거나 비활성화하는 설정을 만드세요.

  • 모든 곳에서 피처 플래그를 애플리케이션 설정으로 교체하세요.

  • 관련된 모든 문서 페이지를 업데이트하세요. 프론트엔드 변경 사항이 이후 마일스톤에 병합되는 경우 애플리케이션 설정 API 또는 Rails 콘솔을 사용하여 설정을 업데이트하는 방법에 대한 문서를 추가해야 합니다.

JSONB 칼럼에 대한 마이그레이션 예시:

# default_enabled copied from feature flag definition YAML before it is removed
DEFAULT_ENABLED = true

def up
  up_migrate_to_jsonb_setting(feature_flag_name: :my_flag_name,
    setting_name: :my_setting,
    jsonb_column_name: :settings,
    default_enabled: DEFAULT_ENABLED)
end

def down
  down_migrate_to_jsonb_setting(setting_name: :my_setting, jsonb_column_name: :settings)
end

boolean 칼럼에 대한 마이그레이션 예시:

# default_enabled copied from feature flag definition YAML before it is removed
DEFAULT_ENABLED = true

def up
  up_migrate_to_setting(feature_flag_name: :my_flag_name,
    setting_name: :my_setting,
    default_enabled: DEFAULT_ENABLED)
end

def down
  down_migrate_to_setting(setting_name: :my_setting, default_enabled: DEFAULT_ENABLED)
end

피처 플래그로 개발하기#

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

백엔드#

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

if Feature.enabled?(:my_feature_flag, project)
  # execute code if feature flag is enabled
else
  # execute code if feature flag is disabled
end

if Feature.disabled?(:my_feature_flag, project)
  # execute code if feature flag is disabled
end

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

피처 플래그에 YAML 정의가 없는 경우 개발 또는 테스트 환경에서는 오류가 발생하고 프로덕션에서는 false를 반환합니다.

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

if Feature.enabled?(:experiment_feature_flag, project, type: :experiment)
  # execute code if feature flag is enabled
end

if Feature.disabled?(:worker_feature_flag, project, type: :worker)
  # execute code if feature flag is disabled
end

애플리케이션 로드 시간에 피처 플래그를 사용하지 마세요. 예를 들어 config/initializers/*에서 또는 클래스 레벨에서 Feature 클래스를 사용하면 예기치 않은 오류가 발생할 수 있습니다. 이 오류는 피처 플래그 어댑터가 의존할 수 있는 데이터베이스가 로드 시간에 존재하지 않기 때문에 발생합니다 (특히 새로 설치된 경우). 호출자에서 데이터베이스 존재 여부를 확인하는 것은 권장하지 않습니다. 일부 어댑터는 데이터베이스가 전혀 필요하지 않기 때문입니다(예: HTTP 어댑터). 피처 플래그 설정 확인은 Feature 네임스페이스에서 추상화되어야 합니다. 이 방식은 피처 플래그가 변경될 때 애플리케이션 재로딩도 필요합니다. 따라서 프로덕션에서 Web/API/Sidekiq 플리트를 재로딩하도록 SRE에 요청해야 하며, 변경 사항을 완전히 롤아웃/롤백하는 데 시간이 걸립니다. 이러한 이유로 환경 변수(예: ENV['YOUR_FEATURE_NAME']) 또는 gitlab.yml을 대신 사용하세요.

피해야 할 패턴의 예시:

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

재귀 감지#

피처 플래그가 많을 때 어디서 호출되는지 항상 명확하지 않습니다. 한 피처 플래그의 평가가 다른 피처 플래그의 평가를 필요로 하는 사이클을 피하세요. 이로 인해 사이클이 발생하면 중단되고 기본값이 반환됩니다.

이 재귀 감지가 올바르게 작동하려면 항상 Feature::enabled?를 통해 피처 값에 접근하고 Feature::get의 저수준 사용을 피하세요. 이런 상황이 발생하면 오류 추적기에 Feature::RecursionError 예외를 기록합니다.

프론트엔드#

UI 요소에 피처 플래그를 사용할 때 백엔드 코드가 있는 경우 기반 백엔드 코드에도 피처 플래그를 사용해야 합니다. 이를 통해 기능이 활성화될 때까지 절대 사용할 수 없도록 합니다.

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

before_action do
  # Prefer to scope it per project or user, for example
  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를 확인하는 것은 작동하지 않습니다.

Vue 컴포넌트에서 피처 플래그에 접근하는 방법에 대한 자세한 내용은 Vue 가이드를 참조하세요.

정의 파일이 없는 피처 플래그(experiment, worker, undefined 유형에만 허용됨)의 경우 push_frontend_feature_flag를 호출할 때 type:을 전달해야 합니다:

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

피처 액터#

피처 플래그에 액터를 사용하는 것을 강력히 권장합니다. 액터는 특정 프로젝트, 그룹 또는 사용자에 대해서만 피처 플래그를 활성화하는 간단한 방법을 제공합니다. 이를 통해 예를 들어 액터를 기반으로 로그와 오류를 필터링할 수 있어 디버깅이 더 쉬워집니다. 또한 나머지 사용자에게 영향을 미치지 않으면서 먼저 gitlab-org 또는 gitlab-com 그룹에서 기능을 활성화하는 것도 가능합니다.

액터는 또한 고정적인 방식으로 기능의 비율 롤아웃을 수행하는 쉬운 방법을 제공합니다. 1% 롤아웃이 특정 액터에 대해 기능을 활성화하면 해당 액터는 10%, 50%, 100%에서도 계속 기능이 활성화됩니다.

GitLab은 다음 피처 플래그 액터를 지원합니다:

  • User 모델

  • Project 모델

  • Group 모델

  • Ci::Runner 모델

  • 현재 요청

액터는 Feature.enabled? 호출의 두 번째 매개변수입니다. 예:

Feature.enabled?(:feature_flag, project)

FeatureGateinclude하는 모델에는 .actor_from_id 클래스 메서드가 있습니다. 모델의 ID가 있고 피처 플래그 상태 확인 외에 다른 용도로 모델이 필요하지 않다면 데이터베이스 쿼리 없이 피처 플래그 상태를 확인하기 위해 .actor_from_id를 사용할 수 있습니다.

# Bad -- Unnecessary query is executed
Feature.enabled?(:feature_flag, Project.find(project_id))

# Good -- No query for projects
Feature.enabled?(:feature_flag, Project.actor_from_id(project_id))

# Good -- Project model is used after feature flag check
project = Project.find(project_id)
return unless Feature.enabled?(:feature_flag, project)
project.update!(column: value)

GitLab이 제공하는 환경(스테이징 및 프로덕션 등)에서 선택적으로 피처 플래그를 활성화하거나 비활성화하기 위해 ChatOps를 사용하는 방법에 대한 자세한 내용은 ChatOps를 사용하여 피처 플래그 활성화 및 비활성화를 참조하세요.

플래그 상태는 그룹에서 하위 그룹이나 프로젝트로 상속되지 않습니다. 전체 그룹 계층에서 플래그 상태가 일관되어야 하는 경우 최상위 그룹을 액터로 사용하는 것을 고려하세요. 이 그룹은 그룹이나 프로젝트에서 #root_ancestor를 호출하여 찾을 수 있습니다.

Feature.enabled?(:feature_flag, group.root_ancestor)

액터 유형 혼합#

일반적으로 특정 피처 플래그에 대한 Feature.enabled?의 모든 호출에서 하나의 액터 유형만 사용하고 다른 액터 유형을 혼합하지 않아야 합니다.

액터 유형을 혼합하면 버그를 일으킬 수 있는 일관성 없는 방식으로 기능이 활성화되거나 비활성화될 수 있습니다. 예를 들어 컨트롤러 수준에서 그룹 액터를 사용하여 플래그를 확인하고 서비스 수준에서 사용자 액터를 사용하여 확인하는 경우 동일한 요청의 다른 지점에서 기능이 활성화 및 비활성화될 수 있습니다.

일관성 없는 결과로 이어지지 않는다는 것을 알고 있다면 일부 상황에서 액터 유형을 안전하게 혼합할 수 있습니다. 예를 들어 웹훅은 그룹 또는 프로젝트와 연결될 수 있으므로 웹훅에 대한 피처 플래그가 동일한 피처 플래그를 사용하여 그룹 및 프로젝트 웹훅에 대한 기능을 롤아웃할 수 있습니다.

상황에서 다른 액터 유형을 사용해야 하고 안전하게 혼합할 수 없는 경우 각 액터 유형에 별도의 플래그를 사용해야 합니다. 예:

Feature.enabled?(:feature_flag_group, group)
Feature.enabled?(:feature_flag_user, user)

인스턴스 액터#

인스턴스 전체 피처 플래그는 기능이 전체 인스턴스에 연결된 경우에만 사용해야 합니다. 항상 다른 액터를 먼저 우선시하세요.

일부 경우에는 액터를 기반으로 하지 않고 전체 인스턴스에 대해 피처 플래그를 활성화하고 싶을 수 있습니다. Admin 설정이 좋은 예인데, 그룹이나 프로젝트 모두 undefined이므로 그룹이나 프로젝트를 기반으로 피처 플래그를 활성화하는 것이 불가능합니다.

사용자 액터는 혼란을 초래할 수 있는데, 관리자가 아닌 사용자에게 피처 플래그가 활성화되었지만 관리자인 사용자에게는 비활성화될 수 있기 때문입니다.

대신 GitLab 인스턴스로 정제되는 두 번째 인수로 :instance 기호를 Feature.enabled?에 사용할 수 있습니다.

Feature.enabled?(:feature_flag, :instance)

현재 요청 액터#

히스토리

시간 비율 롤아웃을 사용하는 것은 권장하지 않습니다. 각 호출이 일관성 없는 결과를 반환할 수 있기 때문입니다.

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

# Bad
Feature.enable_percentage_of_time(:feature_flag, 40)
Feature.enabled?(:feature_flag)

# Good
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 사용으로 전환 사이의 타이밍을 제어하기 어렵기 때문입니다.

프로덕션에서 검증을 위한 액터 사용#

프로덕션을 테스트 환경으로 사용하는 것은 권장하지 않습니다. 프로덕션에 준비되지 않은 기능을 테스트하려면 테스트 환경을 사용하세요.

스테이징 환경이 프로덕션과 유사한 환경에서 기능을 테스트하는 방법을 제공하지만, 프로덕션 환경에 특정한 이전/이후 성능 메트릭을 비교할 수 없습니다. 피처 플래그 아래 새 코드의 메트릭을 Sitespeed 보고서와 같은 도구가 드러낼 수 있도록 프로덕션에서 개발 피처 플래그가 활성화된 프로젝트를 보유하는 것이 유용할 수 있습니다.

이미 Sitespeed에서 이전 코드베이스를 추적하고 있는 경우 이 방식이 더욱 유용합니다. 피처 플래그 롤아웃 전후의 성능을 정확하게 비교할 수 있기 때문입니다.

추가 객체를 액터로 활성화#

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

class Foo < ActiveRecord::Base
  include FeatureGate
end

FeatureGateinclude하거나 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에 정의되면 기능 API의 feature_group 매개변수를 통해 특정 피처 그룹에 대해 기능을 활성화할 수 있습니다.

사용 가능한 피처 그룹은 다음과 같습니다:

그룹 이름 범위 설명
gitlab_team_members 사용자 gitlab-com 멤버인 사용자에 대해 기능을 활성화합니다.

피처 그룹은 그룹 이름으로 활성화할 수 있습니다:

Feature.enable(:feature_flag_name, :gitlab_team_members)

로컬에서 피처 플래그 제어#

Rails 콘솔에서#

rails 콘솔(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"))

Rails 콘솔에서 피처 플래그를 수동으로 활성화하거나 비활성화하면 기본값이 덮어씌워집니다. 이는 플래그의 default_enabled 속성을 변경할 때 혼란을 일으킬 수 있습니다.

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

Feature.remove(:feature_flag_name)

YAML 정의에서 모든 피처 플래그를 기본 상태로 재설정하려면:

Feature.all.each(&:remove)

브라우저에서#

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" 조건을 사용하여 식별할 수 있습니다. 링크에서 예시를 볼 수 있습니다.

요청의 20%만 피처 플래그 상태를 로깅합니다. 이는 feature_flag_state_logs 피처 플래그로 제어됩니다.

변경 로그#

엔드 유저가 직접(예: 기능 사용 능력) 또는 간접적(예: 백그라운드 job 활용 능력, 성능 개선, 또는 데이터베이스 마이그레이션 업데이트)으로 접근할 수 없는 기능에 대한 변경 로그 도입을 피하려 합니다.

데이터베이스 마이그레이션은 Self-managed 고객이 업그레이드 전에 데이터베이스 변경 사항을 알아야 하므로 항상 엔드 유저가 간접적으로 접근할 수 있습니다. 이러한 이유로 변경 로그 항목이 있어야 합니다.

기본적으로 비활성화된 피처 플래그 뒤의 변경 사항에는 변경 로그 항목이 없어야 합니다.

기본적으로 활성화된 피처 플래그 뒤의 변경 사항에는 변경 로그 항목이 있어야 합니다.

피처 플래그 자체 변경(플래그 제거, 기본 활성화 설정)에는 변경 로그 항목이 있어야 합니다. 변경 로그 항목 유형을 결정하려면 플로차트를 사용하세요.

flowchart LR FDOFF(Flag is currently
'default: off') FDON(Flag is currently
'default: on') CDO{Change to
'default: on'} ACF(added / changed / fixed / '...') RF{Remove flag} RF2{Remove flag} RC(removed / changed) OTHER(other)

FDOFF -->CDO-->ACF FDOFF -->RF RF-->|Keep new code?| ACF RF-->|Keep old code?| OTHER

FDON -->RF2 RF2-->|Keep old code?| RC RF2-->|Keep new code?| OTHER

피처 플래그의 변경 로그는 플래그가 아닌 기능을 설명해야 합니다. 단, 새 코드를 유지하면서 기본 활성화 피처 플래그가 제거되는 경우(위 플로차트의 other)는 예외입니다.

피처 플래그는 버그 수정이나 유지 관리 작업 롤아웃에도 사용될 수 있습니다. 이 시나리오에서는 변경 로그가 해당 작업과 관련되어야 합니다. 예: fixed 또는 other.

테스트에서의 피처 플래그#

코드베이스에 피처 플래그를 도입하면 테스트해야 하는 추가 코드 경로가 생성됩니다. 제대로 작동하는지 확인하기 위해 활성화비활성화 상태 모두에서 피처 플래그의 영향을 받는 모든 코드에 대한 자동화된 테스트를 포함하는 것을 강력히 권장합니다. 두 상태 모두에 대한 자동화된 테스트가 포함되지 않은 경우 테스트되지 않은 코드 경로와 관련된 기능을 프로덕션 배포 전에 수동으로 테스트해야 합니다.

테스트 환경을 사용할 때 모든 피처 플래그는 기본적으로 활성화됩니다. 플래그는 spec/spec_helper.rb 파일에서 기본적으로 비활성화할 수 있습니다. 플래그를 비활성화해야 하는 이유를 설명하는 인라인 댓글을 추가하세요. 가능하면 참조를 위해 이슈 URL을 첨부할 수도 있습니다.

이는 기본적으로 피처 플래그를 활성화하지 않는 end-to-end(QA) 테스트에는 적용되지 않습니다. end-to-end 테스트에서 피처 플래그를 사용하는 데는 다른 프로세스가 있습니다.

테스트에서 피처 플래그를 비활성화하려면 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
  # tests assuming ci_live_trace is enabled in tests by default
  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)

# Feature will only be enabled for 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의 동작은 다음과 같습니다:

  • 지정된 액터에 대해 활성화 오버라이드를 설정할 수 있습니다.

  • 지정된 액터에 대한 오버라이드를 비활성화(제거)하여 기본 상태로 돌아갈 수 있습니다.

  • 지정된 액터를 명시적으로 비활성화했다는 것을 모델링할 방법이 없습니다.

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#

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

예:

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 대 Feature.enable*#

테스트 환경에서는 모든 피처 플래그가 기본적으로 활성화되어 있으므로, 일반적으로 stub_feature_flags를 사용하여 플래그를 비활성화하거나 특정 액터에 대해서만 활성화합니다. 이 메서드는 이러한 사용 사례에 대해 간단하고 잘 설명된 인터페이스를 제공합니다:

# Good: disable the flag to test the disabled code path
stub_feature_flags(my_feature: false)

# Good: enable the flag only for specific actors, leaving it disabled elsewhere
stub_feature_flags(my_feature: project)
stub_feature_flags(my_feature: [project, project2])

# Redundant: the flag is already enabled by default in tests, so this has no
# effect unless the flag was disabled by default in spec/spec_helper.rb
stub_feature_flags(my_feature: true)

비율 롤아웃과 같이 더 복잡한 동작의 경우, stub_feature_flags 대신 .enable_percentage_of_time 또는 .enable_percentage_of_actors를 사용하세요:

# Bad: prefer stub_feature_flags for simple enable/disable
Feature.enable(:my_feature_2)

# Good: enable my_feature for 50% of time
Feature.enable_percentage_of_time(:my_feature_3, 50)

# Good: enable my_feature for 50% of actors/gates/things
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

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

테스트 환경의 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를 비활성화하고 productiondevelopment에서 사용하는 모드인 Flipper::Adapters::ActiveRecord를 사용합니다.

ActiveRecord와 상호 작용하는 방식의 Flipper 측면을 정말로 테스트하고 싶을 때만 이 모드를 사용해야 합니다.

End-to-end(QA) 테스트#

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

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

Staging 또는 GitLab.com에서 피처 플래그가 변경되면 파이프라인 트리아지 DRI가 장애가 피처 플래그 변경과 관련이 있는지 더 쉽게 파악할 수 있도록 #e2e-run-staging 또는 #e2e-run-production 채널에 Slack 메시지가 게시됩니다. 그러나 변경 사항을 작업 중인 경우 피처 플래그가 활성화된 상태에서 end-to-end 테스트가 통과하는지 확인하여 예기치 않은 실패를 방지하는 데 도움을 줄 수 있습니다.

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

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

Sidekiq job 지연#

비활성화되면 run_sidekiq_jobs_{WorkerName} 형식의 피처 플래그는 나중에 job을 예약하여 워커의 실행을 지연시킵니다. 이 피처 플래그는 모든 워커에 대해 기본적으로 활성화되어 있습니다. job 지연은 워커 인스턴스의 경합 동작이 인프라 리소스(데이터베이스 및 데이터베이스 연결 풀 등)를 포화시키는 인시던트 중에 유용할 수 있습니다. 구현은 SkipJobs Sidekiq 서버 미들웨어에서 찾을 수 있습니다.

피처 플래그가 비활성화된 동안 job은 무기한 지연됩니다. 워커가 계속 처리하기에 안전하다고 판단되면 피처 플래그를 제거하는 것이 중요합니다.

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

# not running any jobs, deferring all 100% of the jobs
/chatops gitlab run feature set run_sidekiq_jobs_SlowRunningWorker false

# only running 10% of the jobs, deferring 90% of the jobs
/chatops gitlab run feature set run_sidekiq_jobs_SlowRunningWorker 10

# running 50% of the jobs, deferring 50% of the jobs
/chatops gitlab run feature set run_sidekiq_jobs_SlowRunningWorker 50

# back to running all jobs normally
/chatops gitlab run feature delete run_sidekiq_jobs_SlowRunningWorker

Sidekiq job 삭제#

job 지연 대신 drop_sidekiq_jobs_{WorkerName} 피처 플래그를 활성화하여 job을 완전히 삭제할 수 있습니다. job이 향후에 처리될 필요가 없고 따라서 안전하게 삭제할 수 있다고 확신할 때 이 피처 플래그를 사용하세요.

# drop all the jobs
/chatops gitlab run feature set drop_sidekiq_jobs_SlowRunningWorker true

# process jobs normally
/chatops gitlab run feature delete drop_sidekiq_jobs_SlowRunningWorker

삭제 피처 플래그(drop_sidekiq_jobs_{WorkerName})가 지연 피처 플래그(run_sidekiq_jobs_{WorkerName})보다 우선합니다. drop_sidekiq_jobs가 활성화되고 run_sidekiq_jobs가 비활성화되면 job이 완전히 삭제됩니다.

피처 플래그 이벤트#

Feature.enable 또는 Feature.disable이 호출되면 Gitlab::FeatureFlags::FeatureFlagModifiedEvent가 게시됩니다.

이벤트는 Feature.enable 또는 Feature.disable이 상태 변경이 발생했음을 나타내는 true를 반환할 때만 게시됩니다. 비율 기반 롤아웃 메서드(Feature.enable_percentage_of_actorsFeature.enable_percentage_of_time)는 이벤트를 게시하지 않습니다.

이벤트에는 다음이 포함됩니다:

  • feature_key (String): 피처 플래그의 이름.

  • operation (String): 변경 유형. Feature::OPERATION_ENABLED_GLOBALLY, Feature::OPERATION_DISABLED_GLOBALLY, Feature::OPERATION_ENABLED_ACTOR, 또는 Feature::OPERATION_DISABLED_ACTOR 중 하나.

  • actor (String 또는 nil): 이 작업의 영향을 받는 특정 액터(예: "User:123", "Group:456") 또는 전역 작업의 경우 nil.

  • state (String): 작업 후 피처 플래그의 현재 상태("on", "off", 또는 "conditional").

actor 필드에는 현재 작업의 영향을 받는 단일 액터만 포함되며, 플래그에 대해 활성화된 모든 액터가 포함되지 않습니다. 전체 액터 목록을 가져오려면 Feature.get(:flag_name)을 사용하여 데이터베이스를 쿼리하세요.

특정 피처 플래그 구독#

워커는 조건부 필터를 사용하여 특정 피처 플래그를 구독해야 합니다. 이를 통해 GitLab의 모든 피처 플래그 변경에 대해 워커가 큐에 추가되는 것을 방지합니다. 워커는 또한 동일한 작성자의 피처 플래그를 활성화하거나 비활성화하는 여러 호출이 여러 이벤트를 초래할 수 있으므로 멱등성이 있어야 합니다.

피처 플래그 이벤트를 구독하려면:

Gitlab::EventStore::Subscriber를 포함하는 워커를 생성하세요:

module MyFeature
  class MyFeatureFlagWorker
    include ApplicationWorker
    include Gitlab::EventStore::Subscriber

    data_consistency :always
    feature_category :your_category
    urgency :low

    idempotent!

    def handle_event(event)
      feature_key = event.data[:feature_key]
      operation = event.data[:operation]
      actor = event.data[:actor]

      case operation
      when Feature::OPERATION_ENABLED_ACTOR
        # Handle actor-specific enable
      when Feature::OPERATION_DISABLED_ACTOR
        # Handle actor-specific disable
      when Feature::OPERATION_ENABLED_GLOBALLY
        # Handle global enable
      when Feature::OPERATION_DISABLED_GLOBALLY
        # Handle global disable
      end
    end
  end
end

lib/gitlab/event_store/subscriptions/feature_subscriptions.rb에 구독을 등록하세요:

def register
  # Subscribe to all changes for a specific feature flag
  store.subscribe ::MyFeature::MyFeatureFlagWorker,
    to: ::Gitlab::FeatureFlags::FeatureFlagModifiedEvent,
    if: ->(event) { event.data[:feature_key] == 'my_specific_flag' }

  # Subscribe to multiple feature flags
  store.subscribe ::MyFeature::MyFeatureFlagWorker,
    to: ::Gitlab::FeatureFlags::FeatureFlagModifiedEvent,
    if: ->(event) { %w[flag_one flag_two].include?(event.data[:feature_key]) }

  # Only trigger for actor-specific enables
  store.subscribe ::MyFeature::MyFeatureFlagWorker,
    to: ::Gitlab::FeatureFlags::FeatureFlagModifiedEvent,
    if: ->(event) do
      event.data[:feature_key] == 'my_specific_flag' &&
        event.data[:operation] == Feature::OPERATION_ENABLED_ACTOR
    end

  # Only trigger for global enables
  store.subscribe ::MyFeature::MyFeatureFlagWorker,
    to: ::Gitlab::FeatureFlags::FeatureFlagModifiedEvent,
    if: ->(event) do
      event.data[:feature_key] == 'my_specific_flag' &&
        event.data[:operation] == Feature::OPERATION_ENABLED_GLOBALLY
    end
end

액터 형식#

actor 필드에는 작업의 영향을 받는 특정 액터에 대한 Flipper ID 문자열이 포함됩니다. 전역 작업(OPERATION_ENABLED_GLOBALLY 또는 OPERATION_DISABLED_GLOBALLY)의 경우 actor 필드는 nil입니다.

일반적인 액터 형식:

  • User:123 - ID가 123인 사용자

  • Project:456 - ID가 456인 프로젝트

  • Group:789 - ID가 789인 그룹

  • Namespace:101 - ID가 101인 네임스페이스

  • Ci::Runner:202 - ID가 202인 CI Runner

  • Organizations::Organization:303 - ID가 303인 조직

액터 ID를 파싱하려면:

actor = event.data[:actor]
# => "Group:456"

return if actor.nil? # Global operation

actor_type, actor_id = actor.split(':', 2)

case actor_type
when 'User'
  user = User.find(actor_id)
  # Process user
when 'Group'
  group = Group.find(actor_id)
  # Process group
when 'Project'
  project = Project.find(actor_id)
  # Process project
end

GitLab 개발에서의 피처 플래그

GitLab v19.2
원문 보기
요약

이 페이지는 개발자들이 피처 플래그를 통해 GitLab 제품의 개발 및 운영에 기여하는 방법을 설명합니다. 새로 도입된 피처 플래그는 모두 기본적으로 비활성화되어야 하며 액터와 함께 사용해야 합니다. (최신) GitLab 개발 및 운영에서 피처 플래그 사용

이 페이지는 개발자들이 피처 플래그를 통해 GitLab 제품의 개발 및 운영에 기여하는 방법을 설명합니다. 자신의 애플리케이션에서 기능을 표시하거나 숨기기 위한 커스텀 피처 플래그를 생성하려면 피처 플래그 생성을 참조하세요. GitLab에서 사용 가능한 피처 플래그 전체 목록도 확인할 수 있습니다.

새로 도입된 피처 플래그는 모두 기본적으로 비활성화되어야 하며 액터와 함께 사용해야 합니다.

설계 문서:

이 문서는 피처 플래그의 내부 사용 개선 에픽의 일환으로 지속적으로 작업 중입니다. 제안 사항은 새 이슈로 등록하고 해당 에픽에 연결해 주세요.

피처 플래그 라이프사이클 개요를 확인하거나, 피처 플래그 사용 여부를 결정하는 데 도움이 필요하다면 피처 플래그 라이프사이클 핸드북 페이지를 참조하세요.

피처 플래그를 사용해야 하는 경우#

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

외부 API 소비자에서 피처 플래그를 사용하지 않기#

피처 플래그는 내부 구현 세부 사항이며 공개 API 계약의 일부가 아닙니다. GraphQL metadata.featureFlags 필드나 더 이상 사용되지 않는 featureFlagEnabled 필드를 통해 피처 플래그를 조회하는 외부 API 소비자(IDE 확장, Duo CLI, CI 통합 등)는 특정 위험에 직면합니다. 플래그가 모노리스에서 제거될 때 외부 소비자가 아직 업데이트되지 않을 수 있으며, 이는 사소한 UI 문제부터 고객에게 영향을 미치는 인시던트에 이르기까지 다양한 문제를 초래할 수 있습니다.

외부 API 소비자에서 피처 플래그로 작업할 때는 다음 지침을 따르세요:

  • API 필드 또는 Application Settings를 선호하세요. 가능하다면 외부 API 소비자에서 피처 플래그를 조회하지 않도록 하세요. 대신 소비자가 조회할 수 있는 전용 API 필드나 Application Setting을 도입하세요. 이 값들은 플래그가 제거된 후에도 유지됩니다.

  • 실패 개방(fail-open) 동작을 구현하세요. 외부 API 소비자에서 피처 플래그를 반드시 사용해야 한다면, "fail-open" 메커니즘을 구현하세요. 롤아웃 마일스톤이 확정되면 소비자는 기본적으로 플래그가 활성화된 것으로 처리해야 합니다. 롤아웃 마일스톤이 확인되는 즉시 소비자를 업데이트하세요. GitLab Language Server의 예시를 참조하세요.

  • 제거 전에 사용자 업그레이드 패턴을 고려하세요. 외부 API 소비자가 사용하는 플래그를 제거하기 전에, 사용자가 클라이언트를 얼마나 빨리 업데이트하는지 평가하고 제거에 가장 안전한 시점을 결정하세요.

장기 설정에 피처 플래그를 사용하지 않기#

피처 플래그는 단기적으로 사용되도록 설계되었습니다. 오랜 기간 동안 사용자/그룹/프로젝트별로 기능을 활성화할 수 있도록 피처 플래그를 추가하려는 경우, Cascading Settings 또는 Application Settings 도입을 고려하세요. 설정은 고객이 GitLab.com 또는 Self-managed에서 직접 기능을 활성화하거나 비활성화할 수 있게 하며 필요한 만큼 코드베이스에 유지될 수 있습니다. 반면에 사용자는 GitLab.com에서 스스로 피처 플래그를 활성화하거나 비활성화할 방법이 없으며 Self-managed 관리자만 피처 플래그를 변경할 수 있습니다. 또한 GitLab Dedicated에서는 피처 플래그가 지원되지 않으므로, 피처 플래그를 설정의 대체재로 사용하지 않아야 하는 또 다른 이유입니다.

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

피처 플래그 활용 여부를 결정할 때 다음 사항을 고려해야 합니다:

  • 피처 플래그는 기본적으로 비활성화되어야 합니다.

  • 피처 플래그 관리의 필요성을 줄이기 위해 피처 플래그는 가능한 한 짧은 기간 동안 코드베이스에 유지되어야 합니다.

  • 피처 플래그를 운영하는 담당자는 문서와 다른 이해관계자들에게 피처 플래그 뒤에 있는 기능의 상태를 명확히 전달할 책임이 있습니다. 피처 플래그가 필요하다는 것이 명확해지는 즉시 이슈 설명에 피처 플래그 이름과 기본값(활성화 또는 비활성화)을 업데이트해야 합니다.

  • 피처 플래그를 도입하거나, 상태를 업데이트하거나, 기능이 안정적이라고 판단되어 기존 피처 플래그를 제거하는 머지 리퀘스트에는 ~"feature flag" 라벨이 할당되어야 합니다.

기능 구현이 여러 머지 리퀘스트에 걸쳐 제공되는 경우:

  • 플래그를 사용하는 첫 번째 머지 리퀘스트에서 기본적으로 비활성화새 피처 플래그를 생성하세요. 플래그는 별도로 추가해서는 안 됩니다.

  • 하나 이상의 머지 리퀘스트를 통해 점진적인 변경 사항을 제출하고, 추가된 새 코드는 피처 플래그가 활성화된 경우에만 실행될 수 있도록 하세요. 개발 중에 로컬 GDK에서 피처 플래그를 활성화 상태로 유지할 수 있습니다.

  • 기능이 다른 팀원에 의해 테스트될 준비가 되면 초기 문서를 작성하세요. 피처 플래그의 상태에 대한 세부 정보를 포함하세요.

  • 특정 그룹/프로젝트/사용자에 대해 피처 플래그를 활성화하고 구현에 문제가 없는지 확인하세요. 문서가 없는 경우 gitlab-org/gitlab과 같은 공개 프로젝트에 피처 플래그를 활성화하지 마세요. 팀원과 기여자들이 공개 프로젝트에서 활성화된 것을 보면 기능 사용 방법에 대한 문서를 검색할 수 있습니다.

  • 기능이 GitLab Self-Managed 인스턴스를 포함한 프로덕션 사용에 준비가 되면 하나의 머지 리퀘스트를 열어 다음 작업을 수행하세요:

최신 플래그 상태를 설명하는 문서를 업데이트하세요.

  • 변경 로그 항목을 추가하세요.

  • 새로운 동작을 활성화하기 위해 피처 플래그를 제거하거나, 피처 플래그를 기본 활성화로 전환하세요(opsbeta 피처 플래그에만 해당).

피처 플래그 제거가 여러 머지 리퀘스트에 걸쳐 제공되는 경우:

  • 피처 플래그의 값 변경은 머지 리퀘스트에서 유일한 변경이어야 합니다. 피처 플래그가 코드베이스에 존재하는 한, 두 상태 모두 완전히 기능해야 합니다(기능이 켜져 있을 때와 꺼져 있을 때).

  • 피처 플래그의 모든 언급이 제거된 후 레거시 코드를 제거할 수 있습니다. 피처 플래그 롤아웃 이슈의 단계를 따라야 하며, 단계를 건너뛰어야 하는 경우 이슈에 이유를 설명하는 댓글을 추가해야 합니다.

피처 플래그가 기능 릴리스를 적어도 한 달(= 한 번의 릴리스) 지연시킬 것이라고 생각하기 쉽습니다. 하지만 그렇지 않습니다. 피처 플래그는 특정 기간(예: 적어도 한 번의 릴리스) 동안 유지될 필요가 없으며, 대신 기능이 안정적이라고 판단될 때까지 유지되어야 합니다. 안정적이라는 것은 정전과 같은 문제를 일으키지 않고 GitLab.com에서 작동한다는 것을 의미합니다.

기본 브랜치 손상 위험#

피처 플래그는 도입하는 MR에서 반드시 사용해야 합니다. 그렇지 않으면 기본 브랜치에서만 실행되는 rspec:feature-flags job으로 인해 기본 브랜치 손상 시나리오가 발생합니다.

피처 플래그 유형#

예상 사용 방식에 맞는 피처 플래그 유형을 선택하세요.

gitlab_com_derisk 유형#

gitlab_com_derisk 피처 플래그는 GitLab.com 배포의 위험을 줄이기 위해 사용되는 단기 피처 플래그입니다. GitLab에서 사용되는 대부분의 피처 플래그는 gitlab_com_derisk 유형입니다.

제약 조건#

  • default_enabled: true로 설정해서는 안 됩니다. 이 종류의 피처 플래그는 GitLab.com의 위험을 줄이기 위한 것이므로, GitLab.com에서 활성화된 후에는 코드베이스에 플래그를 유지할 필요가 없습니다. default_enabled: true는 이 유형의 피처 플래그에 효과가 없습니다.

  • 최대 수명: 기본 브랜치에 병합된 후 2개월

  • 문서화: 이 유형의 피처 플래그는 단기적이고 배포 관련이므로 GitLab의 모든 피처 플래그 페이지에 문서화할 필요가 없습니다.

  • 롤아웃 이슈: Feature flag Roll Out 템플릿에서 생성된 롤아웃 이슈가 반드시 있어야 합니다.

사용법#

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

GitLab Rails 콘솔에서 활성화 및 비활성화하려면 다음을 실행하세요:

# To enable it for the instance:
Feature.enable(:<dev_flag_name>)

# To disable it for the instance:
Feature.disable(:<dev_flag_name>)

# To enable for a specific project:
Feature.enable(:<dev_flag_name>, Project.find(<project id>))

# To disable for a specific project:
Feature.disable(:<dev_flag_name>, Project.find(<project id>))

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

# Check if the feature flag is enabled
Feature.enabled?(:dev_flag_name)

# Check if the feature flag is disabled
Feature.disabled?(:dev_flag_name)

wip 유형#

일부 기능은 복잡하여 여러 MR을 통해 구현해야 합니다. 완전히 구현되기 전까지는 누구에게도 숨겨야 합니다. 이 경우 wip("Work In Progress"의 약자) 피처 플래그를 사용하면 실제로 기능을 사용하지 않고도 모든 변경 사항을 main 브랜치에 병합할 수 있습니다.

기능이 완성되면 기능이 고객에게 어떻게 제공/문서화될지에 따라 피처 플래그 유형을 gitlab_com_derisk 또는 beta 유형으로 변경할 수 있습니다.

제약 조건#

  • default_enabled: true로 설정해서는 안 됩니다. 필요한 경우 기능이 완성되면 이 유형을 beta로 변경할 수 있습니다.

  • 최대 수명: 기본 브랜치에 병합된 후 4개월

  • 문서화: 이 유형의 피처 플래그는 대부분 미완성 코드를 숨기고 있으므로 GitLab의 모든 피처 플래그 페이지에 문서화할 필요가 없습니다.

  • 롤아웃 이슈: wip 피처 플래그는 활성화되기 전에 다른 유형으로 전환되어야 하므로 롤아웃 이슈가 필요하지 않을 가능성이 높습니다.

사용법#

# Check if feature flag is enabled
Feature.enabled?(:my_wip_flag, project)

# Check if feature flag is disabled
Feature.disabled?(:my_wip_flag, project)

# Push feature flag to Frontend
push_frontend_feature_flag(:my_wip_flag, project)

beta 유형#

현재 형태로 설계된 모든 사용 사례에 대해 기능을 확장, 지원, 유지할 수 있을지 확신하지 못할 수 있습니다(예시). 기능이 MVC로 간주될 만큼 충분하지 않은 시나리오도 있습니다. 이 경우 플래그를 제공하면 엔지니어와 고객이 성능이 충분해질 때까지 새 기능을 비활성화할 수 있습니다.

제약 조건#

  • default_enabled: 확장성 문제가 발생할 경우 비활성화 가능성과 함께 베타 버전의 모든 사람에게 기능을 "릴리스"할 수 있도록 true로 설정할 수 있습니다(이상적으로는 특정 온프레미스 설치에 대해서만 비활성화).

  • 최대 수명: 기본 브랜치에 병합된 후 6개월

  • 문서화: 이 유형의 피처 플래그는 GitLab의 모든 피처 플래그 페이지에 반드시 문서화되어야 합니다. 해당 페이지는 YAML 정의 파일에서 문서 빌드 중에 자동 생성되므로 수동으로 편집할 필요가 없습니다.

  • 롤아웃 이슈: Feature flag Roll Out 템플릿에서 생성된 롤아웃 이슈가 반드시 있어야 합니다.

사용법#

# Check if feature flag is enabled
Feature.enabled?(:my_beta_flag, project)

# Check if feature flag is disabled
Feature.disabled?(:my_beta_flag, project)

# Push feature flag to Frontend
push_frontend_feature_flag(:my_beta_flag, project)

ops 유형#

ops 피처 플래그는 GitLab 제품 동작의 운영적 측면을 제어하는 장기 피처 플래그입니다. 예를 들어 Sidekiq 워커 동작과 같이 성능에 영향을 미칠 수 있는 기능을 비활성화하는 피처 플래그입니다.

이 유형 사용은 인스턴스/그룹/프로젝트/사용자 설정을 도입하지 않겠다는 의식적인 결정을 따라야 합니다.

ops 유형 플래그는 무제한 수명을 가지지만, 12개월마다 여전히 필요한지 평가해야 합니다. 그렇다면 ops 피처 플래그가 여전히 사용 중임을 확인하기 위해 milestone 필드를 최신 마일스톤으로 업데이트해야 합니다.

제약 조건#

  • default_enabled: 대부분의 경우 false로 설정해야 하며, 임시 확장성 문제를 해결하거나 프로덕션 문제를 디버그하는 경우에만 활성화해야 합니다.

  • 최대 수명: 무제한, 단 12개월마다 평가 필요

  • 문서화: 이 유형의 피처 플래그는 GitLab의 모든 피처 플래그 페이지에 반드시 문서화되어야 하며, 사용 가능한 상황을 설명하는 운영 런북과 연결되어야 합니다. 해당 페이지는 YAML 정의 파일에서 문서 빌드 중에 자동 생성되므로 수동으로 편집할 필요가 없습니다.

  • 롤아웃 이슈: 활성화 또는 비활성화 시점을 예측하기 어려우므로 롤아웃 이슈가 필요하지 않을 가능성이 높습니다.

사용법#

# Check if feature flag is enabled
Feature.enabled?(:my_ops_flag, project)

# Check if feature flag is disabled
Feature.disabled?(:my_ops_flag, project)

# Push feature flag to Frontend
push_frontend_feature_flag(:my_ops_flag, project)

experiment 유형#

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

experiment 피처 플래그는 beta 피처 플래그와 동일한 기준을 따라야 하지만 인터페이스에 몇 가지 차이점이 있습니다. experiment 피처 플래그에는 Experiment 추적 템플릿을 사용하여 생성된 롤아웃 이슈가 있어야 합니다. 자세한 내용은 실험 가이드에서 확인할 수 있습니다.

제약 조건#

  • default_enabled: true로 설정해서는 안 됩니다.

  • 최대 수명: 기본 브랜치에 병합된 후 6개월

worker 유형#

worker 피처 플래그는 Sidekiq job 지연과 같이 Sidekiq 워커 동작을 제어할 수 있는 특수 ops 플래그입니다.

worker 피처 플래그는 워커 이름 자체를 사용하여 동적으로 생성될 수 있으므로(예: run_sidekiq_jobs_AuthorizedProjectsWorker) YAML 정의가 없을 수 있습니다. worker 유형 피처 플래그 사용 예시는 Sidekiq job 지연에서 확인할 수 있습니다.

markdown_cache 유형#

markdown_cache 피처 플래그는 Gitlab::MarkdownCache::CACHE_COMMONMARK_VERSION 버전 상향의 단계적 롤아웃을 주도합니다. worker 플래그와 마찬가지로 YAML 정의가 없으며, 이름은 대상 캐시 버전에서 생성됩니다(예: markdown_cache_stochastic_rollout_34). 모든 버전 상향이 자체 플래그를 사용하기 때문에 새 플래그는 비활성화된 상태로 시작하며, 따라서 이전 버전 상향 플래그에 설정된 백분율은 다음 버전으로 이어지지 않습니다. 롤아웃 절차는 CACHE_COMMONMARK_VERSION 버전 상향의 단계적 롤아웃을 참조하세요.

(사용 중단) development 유형#

development 유형은 gitlab_com_derisk, wip, beta 피처 플래그 유형으로 대체되어 사용 중단되었습니다.

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

개발 환경(RAILS_ENV=development) 또는 테스트 환경(RAILS_ENV=test)에서 실행할 때 모든 피처 플래그 사용이 엄격하게 검증됩니다.

이 프로세스는 코드베이스에서 일관된 피처 플래그 사용을 보장하기 위한 것입니다. 모든 피처 플래그는 다음을 반드시 충족해야 합니다:

  • 알려진 것이어야 합니다. 명시적으로 정의된 피처 플래그만 사용하세요(experiment, worker, undefined 유형의 피처 플래그 제외).

  • 두 번 정의되지 않아야 합니다. FOSS 또는 EE 중 하나에만 정의해야 하며, 둘 다에 정의해서는 안 됩니다.

  • 정의 파일이 없는 피처 플래그의 경우, 모든 호출에서 유효하고 일관된 type:을 사용하세요.

  • 소유자가 있어야 합니다.

GitLab이 알고 있는 모든 피처 플래그는 다음에 저장된 YAML 파일에 자체 문서화되어 있습니다:

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

필드 필수 여부 설명
name 피처 플래그 이름.
description 피처 플래그 이유에 대한 간단한 설명.
type 피처 플래그 유형.
default_enabled 피처 플래그의 기본 상태.
introduced_by_url 피처 플래그를 도입한 머지 리퀘스트의 URL.
milestone 피처 플래그가 생성된 마일스톤.
group 피처 플래그를 소유한 그룹.
feature_issue_url 아니오 원본 기능 이슈의 URL.
rollout_issue_url 아니오 피처 플래그 롤아웃을 다루는 이슈의 URL.
log_state_changes 아니오 피처 플래그의 상태를 로깅하는 데 사용.

RAILS_ENV=production에서 실행 시 모든 유효성 검사가 건너뜁니다.

새 피처 플래그 생성#

GitLab Pages는 피처 플래그에 대해 다른 프로세스를 사용합니다.

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

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

$ bin/feature-flag my_feature_flag
>> Specify the feature flag type
?> beta
You picked the type 'beta'

>> Specify the group label to which the feature flag belongs, from the following list:
1. group::group1
2. group::group2
?> 2
You picked the group 'group::group2'

>> URL of the original feature issue (enter to skip):
?> https://gitlab.com/gitlab-org/gitlab/-/issues/435435

>> URL of the MR introducing the feature flag (enter to skip and let Danger provide a suggestion directly in the MR):
?> https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141023

>> Username of the feature flag DRI (enter to skip):
?> bob

>> Is this an EE only feature (enter to skip):
?> [Return]

>> Press any key and paste the issue content that we copied to your clipboard! 🚀
?> [Return automatically opens the "New issue" page where you only have to paste the issue content]

>> URL of the rollout issue (enter to skip):
?> https://gitlab.com/gitlab-org/gitlab/-/issues/437162

create 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

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

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

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

새 플래그 이름 짓기#

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

피처 플래그가 보호하는 기능을 설명하세요.

길고 설명적인 이름이 짧지만 혼란스러운 이름보다 낫습니다.

_mvc, _alpha, _beta 등과 같이 기능의 상태/단계를 나타내는 이름을 피하세요.

snake case로 이름을 작성하세요(my_cool_feature_flag).

이중 부정을 생각하거나 문서화해야 하는 것을 피하기 위해 이름에 disable을 사용하지 마세요. 대신 hide_, remove_, 또는 disallow_로 시작하는 이름을 고려하세요.

소프트웨어 엔지니어링에서 이 문제는 "부울 변수에 부정적인 이름"으로 알려져 있습니다. 하지만 기본적으로 비활성화된 플래그를 도입하거나, 피처 플래그 뒤로 이동하여 기능을 제거하거나, 액터별로 플래그를 선택적으로 비활성화하기 위해 부정적인 단어를 완전히 금지할 수는 없습니다.

master(main) 브랜치 손상 위험#

피처 플래그는 도입하는 MR에서 반드시 사용해야 합니다. 그렇지 않으면 master 브랜치에서만 실행되는 rspec:feature-flags job으로 인해 master 손상 시나리오가 발생합니다.

피처 플래그 자동 제거를 위한 .patch 파일 선택적 추가#

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

이 도구가 코드에서 피처 플래그 사용을 자동으로 제거하려면 피처 플래그 YAML 파일과 함께 .patch 파일을 추가할 수 있습니다. 파일 이름은 .yml 확장자 대신 .patch 확장자를 사용한다는 점을 제외하고 동일해야 합니다.

예를 들어 다음 단계를 사용하여 config/feature_flags/beta/my_feature_flag.yml에 대한 패치 파일을 만들 수 있습니다:

  • 깨끗한 Git 작업 디렉터리가 있는지 확인하세요.

  • config/feature_flags/beta/my_feature_flag.yml을 삭제하세요.

  • 피처 플래그가 이미 활성화되어 있고 기능이 진행 중인 것처럼 my_feature_flag의 모든 사용을 제거하기 위해 코드를 로컬에서 편집하세요.

  • git diff > config/feature_flags/beta/my_feature_flag.patch를 실행하세요. 피처 플래그가 beta 플래그가 아닌 경우, 패치 파일이 피처 플래그를 정의하는 YAML 파일과 동일한 디렉터리에 있는지 확인하세요.

  • config/feature_flags/beta/my_feature_flag.yml 삭제를 취소하세요.

  • 피처 플래그 사용을 제거하기 위해 편집한 파일의 변경 사항을 취소하세요.

  • 피처 플래그를 추가하는 브랜치에 패치 파일을 커밋하세요.

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

모든 피처 플래그 나열#

ChatOps를 사용하여 환경의 모든 피처 플래그를 Slack으로 출력하려면 run feature list 명령을 사용할 수 있습니다. 예:

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

피처 플래그 토글#

피처 플래그 토글에 대한 자세한 내용은 변경 사항 롤아웃을 참조하세요.

피처 플래그 삭제#

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

ops 피처 플래그를 애플리케이션 설정으로 마이그레이션#

애플리케이션 설정을 백필하고 코드에서 설정을 사용하는 변경 사항은 동일한 마일스톤에 병합되어야 합니다.

ops 피처 플래그를 애플리케이션 설정으로 마이그레이션하려면:

  • 애플리케이션 설정에서 설정을 저장할 기존 JSONB 칼럼을 생성하거나 식별하세요.

  • 애플리케이션 설정 기본값은 피처 플래그 YAML 정의의 default_enabled:와 일치해야 합니다.

  • 칼럼을 백필하는 마이그레이션을 작성하세요. 이를 통해 기본 동작을 거부한 인스턴스가 동일한 상태를 유지할 수 있습니다. 마이그레이션에서 Feature.enabled? 또는 Feature.disabled?를 사용하지 마세요. Gitlab::Database::MigrationHelpers::FeatureFlagMigratorHelpers 마이그레이션 헬퍼를 사용하세요. 이 헬퍼는 명시적으로 true 또는 false로 설정된 피처 플래그만 마이그레이션합니다. 피처 플래그가 비율이나 특정 액터에 대해 설정된 경우 기본값이 사용됩니다.

  • 관리자 영역에서 기능을 활성화하거나 비활성화하는 설정을 만드세요.

  • 모든 곳에서 피처 플래그를 애플리케이션 설정으로 교체하세요.

  • 관련된 모든 문서 페이지를 업데이트하세요. 프론트엔드 변경 사항이 이후 마일스톤에 병합되는 경우 애플리케이션 설정 API 또는 Rails 콘솔을 사용하여 설정을 업데이트하는 방법에 대한 문서를 추가해야 합니다.

JSONB 칼럼에 대한 마이그레이션 예시:

# default_enabled copied from feature flag definition YAML before it is removed
DEFAULT_ENABLED = true

def up
  up_migrate_to_jsonb_setting(feature_flag_name: :my_flag_name,
    setting_name: :my_setting,
    jsonb_column_name: :settings,
    default_enabled: DEFAULT_ENABLED)
end

def down
  down_migrate_to_jsonb_setting(setting_name: :my_setting, jsonb_column_name: :settings)
end

boolean 칼럼에 대한 마이그레이션 예시:

# default_enabled copied from feature flag definition YAML before it is removed
DEFAULT_ENABLED = true

def up
  up_migrate_to_setting(feature_flag_name: :my_flag_name,
    setting_name: :my_setting,
    default_enabled: DEFAULT_ENABLED)
end

def down
  down_migrate_to_setting(setting_name: :my_setting, default_enabled: DEFAULT_ENABLED)
end

피처 플래그로 개발하기#

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

백엔드#

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

if Feature.enabled?(:my_feature_flag, project)
  # execute code if feature flag is enabled
else
  # execute code if feature flag is disabled
end

if Feature.disabled?(:my_feature_flag, project)
  # execute code if feature flag is disabled
end

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

피처 플래그에 YAML 정의가 없는 경우 개발 또는 테스트 환경에서는 오류가 발생하고 프로덕션에서는 false를 반환합니다.

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

if Feature.enabled?(:experiment_feature_flag, project, type: :experiment)
  # execute code if feature flag is enabled
end

if Feature.disabled?(:worker_feature_flag, project, type: :worker)
  # execute code if feature flag is disabled
end

애플리케이션 로드 시간에 피처 플래그를 사용하지 마세요. 예를 들어 config/initializers/*에서 또는 클래스 레벨에서 Feature 클래스를 사용하면 예기치 않은 오류가 발생할 수 있습니다. 이 오류는 피처 플래그 어댑터가 의존할 수 있는 데이터베이스가 로드 시간에 존재하지 않기 때문에 발생합니다 (특히 새로 설치된 경우). 호출자에서 데이터베이스 존재 여부를 확인하는 것은 권장하지 않습니다. 일부 어댑터는 데이터베이스가 전혀 필요하지 않기 때문입니다(예: HTTP 어댑터). 피처 플래그 설정 확인은 Feature 네임스페이스에서 추상화되어야 합니다. 이 방식은 피처 플래그가 변경될 때 애플리케이션 재로딩도 필요합니다. 따라서 프로덕션에서 Web/API/Sidekiq 플리트를 재로딩하도록 SRE에 요청해야 하며, 변경 사항을 완전히 롤아웃/롤백하는 데 시간이 걸립니다. 이러한 이유로 환경 변수(예: ENV['YOUR_FEATURE_NAME']) 또는 gitlab.yml을 대신 사용하세요.

피해야 할 패턴의 예시:

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

재귀 감지#

피처 플래그가 많을 때 어디서 호출되는지 항상 명확하지 않습니다. 한 피처 플래그의 평가가 다른 피처 플래그의 평가를 필요로 하는 사이클을 피하세요. 이로 인해 사이클이 발생하면 중단되고 기본값이 반환됩니다.

이 재귀 감지가 올바르게 작동하려면 항상 Feature::enabled?를 통해 피처 값에 접근하고 Feature::get의 저수준 사용을 피하세요. 이런 상황이 발생하면 오류 추적기에 Feature::RecursionError 예외를 기록합니다.

프론트엔드#

UI 요소에 피처 플래그를 사용할 때 백엔드 코드가 있는 경우 기반 백엔드 코드에도 피처 플래그를 사용해야 합니다. 이를 통해 기능이 활성화될 때까지 절대 사용할 수 없도록 합니다.

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

before_action do
  # Prefer to scope it per project or user, for example
  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를 확인하는 것은 작동하지 않습니다.

Vue 컴포넌트에서 피처 플래그에 접근하는 방법에 대한 자세한 내용은 Vue 가이드를 참조하세요.

정의 파일이 없는 피처 플래그(experiment, worker, undefined 유형에만 허용됨)의 경우 push_frontend_feature_flag를 호출할 때 type:을 전달해야 합니다:

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

피처 액터#

피처 플래그에 액터를 사용하는 것을 강력히 권장합니다. 액터는 특정 프로젝트, 그룹 또는 사용자에 대해서만 피처 플래그를 활성화하는 간단한 방법을 제공합니다. 이를 통해 예를 들어 액터를 기반으로 로그와 오류를 필터링할 수 있어 디버깅이 더 쉬워집니다. 또한 나머지 사용자에게 영향을 미치지 않으면서 먼저 gitlab-org 또는 gitlab-com 그룹에서 기능을 활성화하는 것도 가능합니다.

액터는 또한 고정적인 방식으로 기능의 비율 롤아웃을 수행하는 쉬운 방법을 제공합니다. 1% 롤아웃이 특정 액터에 대해 기능을 활성화하면 해당 액터는 10%, 50%, 100%에서도 계속 기능이 활성화됩니다.

GitLab은 다음 피처 플래그 액터를 지원합니다:

  • User 모델

  • Project 모델

  • Group 모델

  • Ci::Runner 모델

  • 현재 요청

액터는 Feature.enabled? 호출의 두 번째 매개변수입니다. 예:

Feature.enabled?(:feature_flag, project)

FeatureGateinclude하는 모델에는 .actor_from_id 클래스 메서드가 있습니다. 모델의 ID가 있고 피처 플래그 상태 확인 외에 다른 용도로 모델이 필요하지 않다면 데이터베이스 쿼리 없이 피처 플래그 상태를 확인하기 위해 .actor_from_id를 사용할 수 있습니다.

# Bad -- Unnecessary query is executed
Feature.enabled?(:feature_flag, Project.find(project_id))

# Good -- No query for projects
Feature.enabled?(:feature_flag, Project.actor_from_id(project_id))

# Good -- Project model is used after feature flag check
project = Project.find(project_id)
return unless Feature.enabled?(:feature_flag, project)
project.update!(column: value)

GitLab이 제공하는 환경(스테이징 및 프로덕션 등)에서 선택적으로 피처 플래그를 활성화하거나 비활성화하기 위해 ChatOps를 사용하는 방법에 대한 자세한 내용은 ChatOps를 사용하여 피처 플래그 활성화 및 비활성화를 참조하세요.

플래그 상태는 그룹에서 하위 그룹이나 프로젝트로 상속되지 않습니다. 전체 그룹 계층에서 플래그 상태가 일관되어야 하는 경우 최상위 그룹을 액터로 사용하는 것을 고려하세요. 이 그룹은 그룹이나 프로젝트에서 #root_ancestor를 호출하여 찾을 수 있습니다.

Feature.enabled?(:feature_flag, group.root_ancestor)

액터 유형 혼합#

일반적으로 특정 피처 플래그에 대한 Feature.enabled?의 모든 호출에서 하나의 액터 유형만 사용하고 다른 액터 유형을 혼합하지 않아야 합니다.

액터 유형을 혼합하면 버그를 일으킬 수 있는 일관성 없는 방식으로 기능이 활성화되거나 비활성화될 수 있습니다. 예를 들어 컨트롤러 수준에서 그룹 액터를 사용하여 플래그를 확인하고 서비스 수준에서 사용자 액터를 사용하여 확인하는 경우 동일한 요청의 다른 지점에서 기능이 활성화 및 비활성화될 수 있습니다.

일관성 없는 결과로 이어지지 않는다는 것을 알고 있다면 일부 상황에서 액터 유형을 안전하게 혼합할 수 있습니다. 예를 들어 웹훅은 그룹 또는 프로젝트와 연결될 수 있으므로 웹훅에 대한 피처 플래그가 동일한 피처 플래그를 사용하여 그룹 및 프로젝트 웹훅에 대한 기능을 롤아웃할 수 있습니다.

상황에서 다른 액터 유형을 사용해야 하고 안전하게 혼합할 수 없는 경우 각 액터 유형에 별도의 플래그를 사용해야 합니다. 예:

Feature.enabled?(:feature_flag_group, group)
Feature.enabled?(:feature_flag_user, user)

인스턴스 액터#

인스턴스 전체 피처 플래그는 기능이 전체 인스턴스에 연결된 경우에만 사용해야 합니다. 항상 다른 액터를 먼저 우선시하세요.

일부 경우에는 액터를 기반으로 하지 않고 전체 인스턴스에 대해 피처 플래그를 활성화하고 싶을 수 있습니다. Admin 설정이 좋은 예인데, 그룹이나 프로젝트 모두 undefined이므로 그룹이나 프로젝트를 기반으로 피처 플래그를 활성화하는 것이 불가능합니다.

사용자 액터는 혼란을 초래할 수 있는데, 관리자가 아닌 사용자에게 피처 플래그가 활성화되었지만 관리자인 사용자에게는 비활성화될 수 있기 때문입니다.

대신 GitLab 인스턴스로 정제되는 두 번째 인수로 :instance 기호를 Feature.enabled?에 사용할 수 있습니다.

Feature.enabled?(:feature_flag, :instance)

현재 요청 액터#

히스토리

시간 비율 롤아웃을 사용하는 것은 권장하지 않습니다. 각 호출이 일관성 없는 결과를 반환할 수 있기 때문입니다.

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

# Bad
Feature.enable_percentage_of_time(:feature_flag, 40)
Feature.enabled?(:feature_flag)

# Good
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 사용으로 전환 사이의 타이밍을 제어하기 어렵기 때문입니다.

프로덕션에서 검증을 위한 액터 사용#

프로덕션을 테스트 환경으로 사용하는 것은 권장하지 않습니다. 프로덕션에 준비되지 않은 기능을 테스트하려면 테스트 환경을 사용하세요.

스테이징 환경이 프로덕션과 유사한 환경에서 기능을 테스트하는 방법을 제공하지만, 프로덕션 환경에 특정한 이전/이후 성능 메트릭을 비교할 수 없습니다. 피처 플래그 아래 새 코드의 메트릭을 Sitespeed 보고서와 같은 도구가 드러낼 수 있도록 프로덕션에서 개발 피처 플래그가 활성화된 프로젝트를 보유하는 것이 유용할 수 있습니다.

이미 Sitespeed에서 이전 코드베이스를 추적하고 있는 경우 이 방식이 더욱 유용합니다. 피처 플래그 롤아웃 전후의 성능을 정확하게 비교할 수 있기 때문입니다.

추가 객체를 액터로 활성화#

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

class Foo < ActiveRecord::Base
  include FeatureGate
end

FeatureGateinclude하거나 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에 정의되면 기능 API의 feature_group 매개변수를 통해 특정 피처 그룹에 대해 기능을 활성화할 수 있습니다.

사용 가능한 피처 그룹은 다음과 같습니다:

그룹 이름 범위 설명
gitlab_team_members 사용자 gitlab-com 멤버인 사용자에 대해 기능을 활성화합니다.

피처 그룹은 그룹 이름으로 활성화할 수 있습니다:

Feature.enable(:feature_flag_name, :gitlab_team_members)

로컬에서 피처 플래그 제어#

Rails 콘솔에서#

rails 콘솔(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"))

Rails 콘솔에서 피처 플래그를 수동으로 활성화하거나 비활성화하면 기본값이 덮어씌워집니다. 이는 플래그의 default_enabled 속성을 변경할 때 혼란을 일으킬 수 있습니다.

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

Feature.remove(:feature_flag_name)

YAML 정의에서 모든 피처 플래그를 기본 상태로 재설정하려면:

Feature.all.each(&:remove)

브라우저에서#

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" 조건을 사용하여 식별할 수 있습니다. 링크에서 예시를 볼 수 있습니다.

요청의 20%만 피처 플래그 상태를 로깅합니다. 이는 feature_flag_state_logs 피처 플래그로 제어됩니다.

변경 로그#

엔드 유저가 직접(예: 기능 사용 능력) 또는 간접적(예: 백그라운드 job 활용 능력, 성능 개선, 또는 데이터베이스 마이그레이션 업데이트)으로 접근할 수 없는 기능에 대한 변경 로그 도입을 피하려 합니다.

데이터베이스 마이그레이션은 Self-managed 고객이 업그레이드 전에 데이터베이스 변경 사항을 알아야 하므로 항상 엔드 유저가 간접적으로 접근할 수 있습니다. 이러한 이유로 변경 로그 항목이 있어야 합니다.

기본적으로 비활성화된 피처 플래그 뒤의 변경 사항에는 변경 로그 항목이 없어야 합니다.

기본적으로 활성화된 피처 플래그 뒤의 변경 사항에는 변경 로그 항목이 있어야 합니다.

피처 플래그 자체 변경(플래그 제거, 기본 활성화 설정)에는 변경 로그 항목이 있어야 합니다. 변경 로그 항목 유형을 결정하려면 플로차트를 사용하세요.

flowchart LR FDOFF(Flag is currently
'default: off') FDON(Flag is currently
'default: on') CDO{Change to
'default: on'} ACF(added / changed / fixed / '...') RF{Remove flag} RF2{Remove flag} RC(removed / changed) OTHER(other)

FDOFF -->CDO-->ACF FDOFF -->RF RF-->|Keep new code?| ACF RF-->|Keep old code?| OTHER

FDON -->RF2 RF2-->|Keep old code?| RC RF2-->|Keep new code?| OTHER

피처 플래그의 변경 로그는 플래그가 아닌 기능을 설명해야 합니다. 단, 새 코드를 유지하면서 기본 활성화 피처 플래그가 제거되는 경우(위 플로차트의 other)는 예외입니다.

피처 플래그는 버그 수정이나 유지 관리 작업 롤아웃에도 사용될 수 있습니다. 이 시나리오에서는 변경 로그가 해당 작업과 관련되어야 합니다. 예: fixed 또는 other.

테스트에서의 피처 플래그#

코드베이스에 피처 플래그를 도입하면 테스트해야 하는 추가 코드 경로가 생성됩니다. 제대로 작동하는지 확인하기 위해 활성화비활성화 상태 모두에서 피처 플래그의 영향을 받는 모든 코드에 대한 자동화된 테스트를 포함하는 것을 강력히 권장합니다. 두 상태 모두에 대한 자동화된 테스트가 포함되지 않은 경우 테스트되지 않은 코드 경로와 관련된 기능을 프로덕션 배포 전에 수동으로 테스트해야 합니다.

테스트 환경을 사용할 때 모든 피처 플래그는 기본적으로 활성화됩니다. 플래그는 spec/spec_helper.rb 파일에서 기본적으로 비활성화할 수 있습니다. 플래그를 비활성화해야 하는 이유를 설명하는 인라인 댓글을 추가하세요. 가능하면 참조를 위해 이슈 URL을 첨부할 수도 있습니다.

이는 기본적으로 피처 플래그를 활성화하지 않는 end-to-end(QA) 테스트에는 적용되지 않습니다. end-to-end 테스트에서 피처 플래그를 사용하는 데는 다른 프로세스가 있습니다.

테스트에서 피처 플래그를 비활성화하려면 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
  # tests assuming ci_live_trace is enabled in tests by default
  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)

# Feature will only be enabled for 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의 동작은 다음과 같습니다:

  • 지정된 액터에 대해 활성화 오버라이드를 설정할 수 있습니다.

  • 지정된 액터에 대한 오버라이드를 비활성화(제거)하여 기본 상태로 돌아갈 수 있습니다.

  • 지정된 액터를 명시적으로 비활성화했다는 것을 모델링할 방법이 없습니다.

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#

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

예:

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 대 Feature.enable*#

테스트 환경에서는 모든 피처 플래그가 기본적으로 활성화되어 있으므로, 일반적으로 stub_feature_flags를 사용하여 플래그를 비활성화하거나 특정 액터에 대해서만 활성화합니다. 이 메서드는 이러한 사용 사례에 대해 간단하고 잘 설명된 인터페이스를 제공합니다:

# Good: disable the flag to test the disabled code path
stub_feature_flags(my_feature: false)

# Good: enable the flag only for specific actors, leaving it disabled elsewhere
stub_feature_flags(my_feature: project)
stub_feature_flags(my_feature: [project, project2])

# Redundant: the flag is already enabled by default in tests, so this has no
# effect unless the flag was disabled by default in spec/spec_helper.rb
stub_feature_flags(my_feature: true)

비율 롤아웃과 같이 더 복잡한 동작의 경우, stub_feature_flags 대신 .enable_percentage_of_time 또는 .enable_percentage_of_actors를 사용하세요:

# Bad: prefer stub_feature_flags for simple enable/disable
Feature.enable(:my_feature_2)

# Good: enable my_feature for 50% of time
Feature.enable_percentage_of_time(:my_feature_3, 50)

# Good: enable my_feature for 50% of actors/gates/things
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

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

테스트 환경의 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를 비활성화하고 productiondevelopment에서 사용하는 모드인 Flipper::Adapters::ActiveRecord를 사용합니다.

ActiveRecord와 상호 작용하는 방식의 Flipper 측면을 정말로 테스트하고 싶을 때만 이 모드를 사용해야 합니다.

End-to-end(QA) 테스트#

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

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

Staging 또는 GitLab.com에서 피처 플래그가 변경되면 파이프라인 트리아지 DRI가 장애가 피처 플래그 변경과 관련이 있는지 더 쉽게 파악할 수 있도록 #e2e-run-staging 또는 #e2e-run-production 채널에 Slack 메시지가 게시됩니다. 그러나 변경 사항을 작업 중인 경우 피처 플래그가 활성화된 상태에서 end-to-end 테스트가 통과하는지 확인하여 예기치 않은 실패를 방지하는 데 도움을 줄 수 있습니다.

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

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

Sidekiq job 지연#

비활성화되면 run_sidekiq_jobs_{WorkerName} 형식의 피처 플래그는 나중에 job을 예약하여 워커의 실행을 지연시킵니다. 이 피처 플래그는 모든 워커에 대해 기본적으로 활성화되어 있습니다. job 지연은 워커 인스턴스의 경합 동작이 인프라 리소스(데이터베이스 및 데이터베이스 연결 풀 등)를 포화시키는 인시던트 중에 유용할 수 있습니다. 구현은 SkipJobs Sidekiq 서버 미들웨어에서 찾을 수 있습니다.

피처 플래그가 비활성화된 동안 job은 무기한 지연됩니다. 워커가 계속 처리하기에 안전하다고 판단되면 피처 플래그를 제거하는 것이 중요합니다.

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

# not running any jobs, deferring all 100% of the jobs
/chatops gitlab run feature set run_sidekiq_jobs_SlowRunningWorker false

# only running 10% of the jobs, deferring 90% of the jobs
/chatops gitlab run feature set run_sidekiq_jobs_SlowRunningWorker 10

# running 50% of the jobs, deferring 50% of the jobs
/chatops gitlab run feature set run_sidekiq_jobs_SlowRunningWorker 50

# back to running all jobs normally
/chatops gitlab run feature delete run_sidekiq_jobs_SlowRunningWorker

Sidekiq job 삭제#

job 지연 대신 drop_sidekiq_jobs_{WorkerName} 피처 플래그를 활성화하여 job을 완전히 삭제할 수 있습니다. job이 향후에 처리될 필요가 없고 따라서 안전하게 삭제할 수 있다고 확신할 때 이 피처 플래그를 사용하세요.

# drop all the jobs
/chatops gitlab run feature set drop_sidekiq_jobs_SlowRunningWorker true

# process jobs normally
/chatops gitlab run feature delete drop_sidekiq_jobs_SlowRunningWorker

삭제 피처 플래그(drop_sidekiq_jobs_{WorkerName})가 지연 피처 플래그(run_sidekiq_jobs_{WorkerName})보다 우선합니다. drop_sidekiq_jobs가 활성화되고 run_sidekiq_jobs가 비활성화되면 job이 완전히 삭제됩니다.

피처 플래그 이벤트#

Feature.enable 또는 Feature.disable이 호출되면 Gitlab::FeatureFlags::FeatureFlagModifiedEvent가 게시됩니다.

이벤트는 Feature.enable 또는 Feature.disable이 상태 변경이 발생했음을 나타내는 true를 반환할 때만 게시됩니다. 비율 기반 롤아웃 메서드(Feature.enable_percentage_of_actorsFeature.enable_percentage_of_time)는 이벤트를 게시하지 않습니다.

이벤트에는 다음이 포함됩니다:

  • feature_key (String): 피처 플래그의 이름.

  • operation (String): 변경 유형. Feature::OPERATION_ENABLED_GLOBALLY, Feature::OPERATION_DISABLED_GLOBALLY, Feature::OPERATION_ENABLED_ACTOR, 또는 Feature::OPERATION_DISABLED_ACTOR 중 하나.

  • actor (String 또는 nil): 이 작업의 영향을 받는 특정 액터(예: "User:123", "Group:456") 또는 전역 작업의 경우 nil.

  • state (String): 작업 후 피처 플래그의 현재 상태("on", "off", 또는 "conditional").

actor 필드에는 현재 작업의 영향을 받는 단일 액터만 포함되며, 플래그에 대해 활성화된 모든 액터가 포함되지 않습니다. 전체 액터 목록을 가져오려면 Feature.get(:flag_name)을 사용하여 데이터베이스를 쿼리하세요.

특정 피처 플래그 구독#

워커는 조건부 필터를 사용하여 특정 피처 플래그를 구독해야 합니다. 이를 통해 GitLab의 모든 피처 플래그 변경에 대해 워커가 큐에 추가되는 것을 방지합니다. 워커는 또한 동일한 작성자의 피처 플래그를 활성화하거나 비활성화하는 여러 호출이 여러 이벤트를 초래할 수 있으므로 멱등성이 있어야 합니다.

피처 플래그 이벤트를 구독하려면:

Gitlab::EventStore::Subscriber를 포함하는 워커를 생성하세요:

module MyFeature
  class MyFeatureFlagWorker
    include ApplicationWorker
    include Gitlab::EventStore::Subscriber

    data_consistency :always
    feature_category :your_category
    urgency :low

    idempotent!

    def handle_event(event)
      feature_key = event.data[:feature_key]
      operation = event.data[:operation]
      actor = event.data[:actor]

      case operation
      when Feature::OPERATION_ENABLED_ACTOR
        # Handle actor-specific enable
      when Feature::OPERATION_DISABLED_ACTOR
        # Handle actor-specific disable
      when Feature::OPERATION_ENABLED_GLOBALLY
        # Handle global enable
      when Feature::OPERATION_DISABLED_GLOBALLY
        # Handle global disable
      end
    end
  end
end

lib/gitlab/event_store/subscriptions/feature_subscriptions.rb에 구독을 등록하세요:

def register
  # Subscribe to all changes for a specific feature flag
  store.subscribe ::MyFeature::MyFeatureFlagWorker,
    to: ::Gitlab::FeatureFlags::FeatureFlagModifiedEvent,
    if: ->(event) { event.data[:feature_key] == 'my_specific_flag' }

  # Subscribe to multiple feature flags
  store.subscribe ::MyFeature::MyFeatureFlagWorker,
    to: ::Gitlab::FeatureFlags::FeatureFlagModifiedEvent,
    if: ->(event) { %w[flag_one flag_two].include?(event.data[:feature_key]) }

  # Only trigger for actor-specific enables
  store.subscribe ::MyFeature::MyFeatureFlagWorker,
    to: ::Gitlab::FeatureFlags::FeatureFlagModifiedEvent,
    if: ->(event) do
      event.data[:feature_key] == 'my_specific_flag' &&
        event.data[:operation] == Feature::OPERATION_ENABLED_ACTOR
    end

  # Only trigger for global enables
  store.subscribe ::MyFeature::MyFeatureFlagWorker,
    to: ::Gitlab::FeatureFlags::FeatureFlagModifiedEvent,
    if: ->(event) do
      event.data[:feature_key] == 'my_specific_flag' &&
        event.data[:operation] == Feature::OPERATION_ENABLED_GLOBALLY
    end
end

액터 형식#

actor 필드에는 작업의 영향을 받는 특정 액터에 대한 Flipper ID 문자열이 포함됩니다. 전역 작업(OPERATION_ENABLED_GLOBALLY 또는 OPERATION_DISABLED_GLOBALLY)의 경우 actor 필드는 nil입니다.

일반적인 액터 형식:

  • User:123 - ID가 123인 사용자

  • Project:456 - ID가 456인 프로젝트

  • Group:789 - ID가 789인 그룹

  • Namespace:101 - ID가 101인 네임스페이스

  • Ci::Runner:202 - ID가 202인 CI Runner

  • Organizations::Organization:303 - ID가 303인 조직

액터 ID를 파싱하려면:

actor = event.data[:actor]
# => "Group:456"

return if actor.nil? # Global operation

actor_type, actor_id = actor.split(':', 2)

case actor_type
when 'User'
  user = User.find(actor_id)
  # Process user
when 'Group'
  group = Group.find(actor_id)
  # Process group
when 'Project'
  project = Project.find(actor_id)
  # Process project
end