Home/Learn/Microservices/Feature Flags in Microservices

Feature Flags in Microservices

Intermediate
Patterns

Feature flags decouple deployment from release; toggle features per environment or user segment without redeploying, enabling dark launches and A/B tests.

Overview

Feature flags (also called feature toggles or feature switches) decouple code deployment from feature release. You deploy code with a new feature wrapped in a flag check — the feature is off by default. Operations or product teams enable it for specific environments, user segments, or percentages of traffic without redeploying. This enables: dark launches (deploy to production, enable for 1% of users to test at scale), A/B testing (compare two implementations in production), kill switches (instantly disable a problematic feature without rollback), and gradual rollouts (canary via traffic percentage). Popular platforms: LaunchDarkly, Unleash, Flagsmith, OpenFeature (vendor-neutral SDK standard).

Simple feature flag with Spring Boot and properties

The simplest approach uses configuration properties — a boolean that can be changed via environment variable, Kubernetes ConfigMap reload, or Spring Cloud Config. This works well for on/off flags that change infrequently. For user-segment or percentage-based flags, use a dedicated feature flag platform.

YAML + Java — property-based feature flags with ConfigMap reload
// application.yml — feature flags as properties
features:
  new-checkout-flow: true
  experimental-pricing: false
  max-order-size-check: true

// Configuration properties class
@ConfigurationProperties(prefix = "features")
@Component
public class FeatureFlags {
    private boolean newCheckoutFlow;
    private boolean experimentalPricing;
    private boolean maxOrderSizeCheck;
    // getters/setters or use @ConstructorBinding record
}

// Usage in service
@Service
public class CheckoutService {

    private final FeatureFlags flags;

    public CheckoutResult checkout(Cart cart, User user) {
        if (flags.isNewCheckoutFlow()) {
            return newCheckoutFlowService.process(cart, user);
        }
        return legacyCheckoutService.process(cart, user);
    }
}

// Toggle without redeployment via K8s ConfigMap reload
# kubectl patch configmap app-config
# --patch '{"data":{"features.new-checkout-flow":"true"}}'
# (requires spring.config.import=kubernetes: + @RefreshScope or restart)

OpenFeature SDK — vendor-neutral feature flags

OpenFeature is a CNCF standard SDK that provides a vendor-neutral API for feature flag evaluation. You write code against the OpenFeature API; the provider (LaunchDarkly, Unleash, Flagsmith, or a custom in-process provider) handles flag storage and evaluation. This prevents vendor lock-in and allows switching providers without changing application code.

Java — OpenFeature SDK with Unleash provider and evaluation context
<!-- pom.xml -->
<dependency>
    <groupId>dev.openfeature</groupId>
    <artifactId>sdk</artifactId>
    <version>1.7.0</version>
</dependency>
<dependency>
    <groupId>dev.openfeature.contrib.providers</groupId>
    <artifactId>unleash</artifactId>
    <version>0.0.4</version>
</dependency>

// Configure provider (Unleash example)
@Configuration
public class FeatureFlagConfig {

    @Bean
    public OpenFeatureAPI openFeatureAPI() {
        OpenFeatureAPI api = OpenFeatureAPI.getInstance();
        UnleashProvider provider = new UnleashProvider(
            UnleashConfig.newBuilder()
                .appName("order-service")
                .unleashAPI("http://unleash:4242/api")
                .customHttpHeader("Authorization", "*:development.secret")
                .build()
        );
        api.setProvider(provider);
        return api;
    }
}

// Usage — evaluated at runtime, context-aware
@Service
public class PricingService {

    private final Client featureClient;

    public BigDecimal calculatePrice(Order order, User user) {
        EvaluationContext ctx = new MutableContext()
            .add("userId", user.getId())
            .add("country", user.getCountry())
            .add("plan", user.getPlan());

        boolean useNewPricing = featureClient.getBooleanValue(
            "new-pricing-algorithm", false, ctx);

        return useNewPricing
            ? newPricingEngine.calculate(order, user)
            : legacyPricingEngine.calculate(order, user);
    }
}

Kill switches and gradual rollout strategies

Kill switches are flags designed for emergency shutdown of a feature that is causing incidents — they should be testable before needed, have clear ownership, and be executable by on-call engineers without code changes. Gradual rollout (percentage-based) enables canary testing at the application logic level, independent of infrastructure-level canary deployments.

Java — kill switch pattern and gradual rollout strategy
// Kill switch pattern — operational readiness
@Component
public class PaymentGatewayKillSwitch {

    private final Client featureClient;
    private final MeterRegistry meterRegistry;

    public void processPayment(PaymentRequest req) {
        boolean gatewayEnabled = featureClient.getBooleanValue(
            "payment-gateway-v2-enabled", true);

        if (!gatewayEnabled) {
            // Kill switch activated — fall back to v1
            meterRegistry.counter("payment.kill_switch.activated").increment();
            log.warn("Payment gateway v2 kill switch activated — using v1");
            paymentGatewayV1.process(req);
            return;
        }
        paymentGatewayV2.process(req);
    }
}

// Percentage rollout (in Unleash / LaunchDarkly configuration)
// Flag: "new-order-summary"
// Strategy: gradualRollout
//   percentage: 10%  → enable for 10% of users
//   stickiness: userId  → same user always gets same variant

// Increment rollout without redeployment:
// Day 1: 1%  → verify error rates, latency
// Day 2: 10% → monitor at scale
// Day 3: 50% → confirm acceptable metrics
// Day 4: 100% → full rollout
// Day 5: remove flag from code (don't leave dead flags)

Key Points to Remember

  • 1Feature flags decouple deployment (code merged and deployed) from release (feature visible to users)
  • 2OpenFeature provides a vendor-neutral SDK — swap providers (LaunchDarkly, Unleash, Flagsmith) without code changes
  • 3Evaluation context (userId, country, plan) enables targeting: specific user segments get different flag values
  • 4Kill switches must be pre-tested and accessible to on-call engineers — not just developers — for true operational value
  • 5Gradual rollout (1% → 10% → 50% → 100%) enables safe production validation before full release
  • 6Remove flags from code after full rollout — dead flags become technical debt and confuse new developers

Interview Questions

Sign in to ask Aria
1

What is the difference between a feature flag and a configuration property?

EasyThoughtworks
2

How do feature flags enable dark launches and why is this valuable?

MediumNetflix
3

What is OpenFeature and why would you use it over a vendor SDK directly?

MediumGoogle
4

What is a kill switch flag and how should it be designed for operational readiness?

MediumAmazon
5

How would you implement a gradual rollout of a new checkout flow to 5% of users in production?

HardUber

Ask Aria about Feature Flags in Microservices

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…