Master GitLab Feature Flags: learn how to roll out features safely, ship experiments, and automate progressive delivery with GitLab's built-in flag management platform.
Table of Contents
1. What Are GitLab Feature Flags?
GitLab Feature Flags are runtime switches that let you enable or disable application functionality without redeploying code. GitLab's built-in service acts as a centralized control plane for progressive delivery, experimentation, and controlled rollouts across environments.
Feature flags wrap new code paths or risky behavior with conditional logic. Teams merge code to production but keep it dark until they're ready to expose it, significantly reducing release risk.
2. Why Use Feature Flags?
- Safe deploys: decouple deployment from release, allowing instant rollback by toggling the flag.
- Progressive delivery: release gradually to user cohorts, regions, or environments.
- Experimentation: A/B test experiences using percentage rollouts.
- Ops coordination: sync feature availability with marketing launches or maintenance windows.
- Chaos testing: quickly disable unstable integrations to protect SLAs.
- Compliance: audit who changed flag states and when, directly in GitLab.
3. Architecture Overview
GitLab's feature flag service uses server-side evaluation backed by a Redis cache and REST API. Applications query the service on startup (or poll periodically) to download flag definitions and strategies.
3.1 High-Level Flow
This architecture keeps evaluation close to the application while centralizing control, auditing, and automation in GitLab.
4. Core Components
Key building blocks when working with GitLab feature flags:
4.1 Feature Configuration
A flag definition includes a human-readable name, description, boolean default, and a set of environments. You can scope flags to projects or groups and reuse patterns via templates.
4.2 Rollout Strategies
GitLab supports multiple strategies such as All Users, Percentage Rollout, User IDs, and Advanced Group rules. Strategies are evaluated top-down, making it easy to combine gradual rollout with explicit allow/deny lists.
4.3 Environments
Flags can be enabled per environment (development, staging, production). GitLab automatically mirrors your Environments definitions, ensuring deployments and flag states stay in sync.
4.4 Audit & Analytics
Every toggle is recorded in GitLab's audit log. You can integrate with metrics vendors or GitLab Observability to correlate flag changes with error budgets, latency, or user behavior.
5. Project Setup
Feature flags live under Deployments → Feature Flags in each GitLab project. You'll define flags in the UI or via API and evaluate them from your application using the GitLab Feature Flag SDK.
5.1 Enable Feature Flags
- Navigate to your project in GitLab.
- Go to
Deployments → Feature Flags. - Click New feature flag, provide name and description.
- Set default state and environment-specific strategies.
5.2 Spring Boot Dependency Setup
Add the GitLab Feature Flags client starter (community-supported) to your build so the client and auto-refresh scheduler are available in the Spring context.
// build.gradle
dependencies {
implementation "com.gitlab.featureflags:client-spring-boot-starter:1.2.0"
}
<dependency>
<groupId>com.gitlab.featureflags</groupId>
<artifactId>client-spring-boot-starter</artifactId>
<version>1.2.0</version>
</dependency>
The starter provides configuration properties, auto-refresh scheduling, and a type-safe GitlabFeatureFlagClient bean that your services can inject.
6. Configuration
Applications authenticate via personal access tokens or project access tokens. You configure the client with your GitLab instance base URL and token.
6.1 Spring Boot Configuration
# application.yml
gitlab:
url: https://gitlab.example.com
project-id: 123
token: ${GITLAB_FF_TOKEN}
environment: ${SPRING_PROFILES_ACTIVE:production}
refresh-interval: 30s
Properties define how the client talks to your GitLab instance, which project owns the flags, and how frequently the local cache refreshes.
6.2 Client Bean (Spring Boot)
@Bean
public GitlabFeatureFlagClient gitlabFeatureFlagClient(
@Value("${gitlab.url}") String gitlabUrl,
@Value("${gitlab.project-id}") Integer projectId,
@Value("${gitlab.token}") String token,
Environment environment) {
return GitlabFeatureFlagClient.builder()
.gitlabUrl(gitlabUrl)
.projectId(projectId)
.token(token)
.environment(environment.getActiveProfiles()[0])
.build();
}
Clients typically cache flag states locally and refresh every 30 seconds (configurable).
7. Real-World Examples
7.1 Spring Boot Progressive Delivery
Wire GitLab feature flags into a Spring Boot service to gradually roll out a feature to paying customers while keeping it disabled for everyone else.
@Configuration
public class FeatureFlagConfig {
@Bean
GitlabFeatureFlagClient gitlabFeatureFlagClient(GitlabProperties props) {
return GitlabFeatureFlagClient.builder()
.gitlabUrl(props.url())
.projectId(props.projectId())
.token(props.token())
.environment(props.environment())
.refreshInterval(Duration.ofSeconds(30))
.build();
}
}
@Service
public class ProgressiveDeliveryService {
private final GitlabFeatureFlagClient flags;
public ProgressiveDeliveryService(GitlabFeatureFlagClient flags) {
this.flags = flags;
}
public String getNavigationVariant(Customer customer) {
boolean enabled = flags.isEnabled("new_navbar", customer.id());
return enabled && customer.plan().equals("PRO")
? "new-navigation"
: "classic-navigation";
}
}
@RestController
@RequestMapping("/ui")
public class NavigationController {
private final ProgressiveDeliveryService service;
public NavigationController(ProgressiveDeliveryService service) {
this.service = service;
}
@GetMapping("/navigation")
public ResponseEntity<NavigationResponse> navigation(@AuthenticationPrincipal Customer customer) {
String variant = service.getNavigationVariant(customer);
return ResponseEntity.ok(new NavigationResponse(variant));
}
public record NavigationResponse(String variant) {}
}
7.2 Backend Kill Switch
@RestController
public class PaymentsController {
private final GitlabFeatureFlagClient flags;
public PaymentsController(GitlabFeatureFlagClient flags) {
this.flags = flags;
}
@PostMapping("/payments")
public ResponseEntity<?> charge(@RequestBody PaymentRequest request) {
if (!flags.isEnabled("payments_v2", request.customerId())) {
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
.body("Payments temporarily disabled");
}
return paymentService.charge(request);
}
}
7.3 GitLab CI Toggle
Use the API within CI/CD pipelines to flip a flag after a successful deployment:
feature_toggle:
stage: deploy
script:
- |
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
--data "active=true" \
"https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/feature_flags/new_navbar"
when: manual
environment: production
8. Best Practices
- Name carefully: use consistent prefixes (e.g.,
checkout_) to keep flags searchable. - Document intent: include links to issues, epics, or experiments directly in the description.
- Set expiry dates: schedule cleanup tasks so flags do not accumulate technical debt.
- Automate toggles: combine with GitLab Deployments or incident automations.
- Observe impact: wire flag state to metrics (errors, conversions) to catch regressions fast.
- Limit scope: avoid wrapping entire applications; keep flag conditions close to the smallest affected component.
9. Testing Feature Flags
Treat flag states as part of your test matrix. GitLab clients expose APIs you can stub or override in tests.
9.1 Unit Testing
@Test
void rendersNewNavbarWhenFlagEnabled() {
when(flags.isEnabled("new_navbar", "user-1")).thenReturn(true);
assertTrue(component.shouldRenderNewNavbar("user-1"));
}
9.2 Integration Testing
Use GitLab's Test Environment feature to create ephemeral environments with known flag states. Seed flags through the API in your test jobs before running end-to-end suites.
10. Advanced Concepts
10.1 Percentage Rollouts
Deterministic hashing ensures the same user ID consistently receives the same variation. Adjust the percentage to expand coverage without shifting existing cohorts.
10.2 Segmentation
Combine User IDs, IP ranges, or Custom contexts (e.g., subscription tiers) for precise control. GitLab evaluates the first matching rule, so order rules from most specific to least specific.
10.3 Multi-Project Flags
Create group-level feature flag templates for cross-project consistency. Templates standardize naming, strategy defaults, and documentation.
10.4 Cleanup Automation
Use the /feature_flags/:id API to archive flags after the code paths are fully rolled out. Pair with Danger or custom linting to alert when a flag exists longer than its expiry date.
11. Production Considerations
- Latency: cache flag evaluations in-memory and refresh asynchronously to avoid blocking requests.
- Fail-open vs fail-closed: decide what happens if the flag service is unreachable; most teams prefer defaults that keep core functionality available.
- Secrets: store API tokens in GitLab CI/CD variables or secret managers; rotate regularly.
- Monitoring: alert when a flag toggles outside approved change windows.
- Cost: each flag adds complexity—budget cleanup time on your roadmap.
0 Comments