Open-Source Wikis

/

GitLab

/

Systems

/

Feature flags

gitlab-org/gitlab

Feature flags

Runtime gating built on Flipper, with strict YAML metadata, lifecycle stages, and chatops-driven rollout.

Purpose

Ship code that's not 100% ready or that needs gradual rollout, A/B-style measurement, or rapid kill-switch capability.

Source

Concern Location
Core API lib/feature.rb (~18K LoC)
Flag declaration YAMLs config/feature_flags/*/<name>.yml (463 flags)
Lifecycle types config/feature_flags/{development,beta,gitlab_com_derisk,experiment,wip,worker,ops,undefined}/
Validation lib/gitlab/feature.rb, danger rules in danger/feature_flag/

Lifecycle types (subdirectories)

Type When to use
development New code being rolled out gradually
beta Public beta, expected GA path
gitlab_com_derisk De-risk a GitLab.com-only rollout
experiment A/B experiment
wip Internal-only, not ready for any rollout
worker Toggling a Sidekiq worker class on/off
ops Operational kill-switch (no expected removal)
undefined Legacy bucket; new flags must not land here

A flag YAML

---
name: my_new_feature
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/123456
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100000
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/123457
milestone: '17.4'
group: group::source code
type: development
default_enabled: false

Required metadata is validated by Danger and a CI lint job.

Checking a flag

if Feature.enabled?(:my_new_feature, current_user)
  # ...
end

# With a project as actor
Feature.enabled?(:my_new_feature, project)

# Disabled-style check
return if Feature.disabled?(:my_new_feature, group)

The actor argument routes the check to a stable percentage:

  • :my_user — by user.
  • :my_project — by project.
  • :my_group — by group.
  • :instance — global toggle.

Frontend access

Flags are pushed to the frontend by including them in the page's gon payload:

push_frontend_feature_flag(:my_new_feature, current_user)

In Vue:

import { glFeatureFlagMixin } from '~/vue_shared/mixins/gl_feature_flag_mixin';

this.glFeatures.myNewFeature;

Rollout via chatops

A staff engineer enables the flag in production via Slack chatops:

/chatops run feature set my_new_feature true
/chatops run feature set my_new_feature 25      # 25% rollout

The rollout issue (linked in the YAML) tracks decisions and dashboards.

Removing a flag

When a flag has been enabled in production for at least one milestone:

  1. Delete the YAML.
  2. Remove Feature.enabled?(:flag, ...) and dead code branches.
  3. Update tests that stubbed the flag.
  4. Open an MR labelled feature flag so Danger acknowledges removal.

The Housekeeper (gems/gitlab-housekeeper/, keeps/feature_flag/) automates this for stale flags.

Strict checks

Cops enforce flag hygiene:

  • Gitlab/StrictFeatureFlag — flag actor must be present.
  • Gitlab/FeatureFlagWithoutDefault — disallow ad-hoc Flipper calls.
  • Gitlab/FeatureFlagDeprecation — flag YAMLs older than 6 months issue warnings.

Specs

Force a flag value:

before do
  stub_feature_flags(my_new_feature: true)
  # or
  stub_feature_flags(my_new_feature: project)  # only enabled for this project
end

Always test both states.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Feature flags – GitLab wiki | Factory