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: falseRequired 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% rolloutThe 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:
- Delete the YAML.
- Remove
Feature.enabled?(:flag, ...)and dead code branches. - Update tests that stubbed the flag.
- Open an MR labelled
feature flagso 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
endAlways test both states.
Related
- Patterns and conventions.
- Tooling — Danger feature flag rules.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.