Feature Flag Tools for Progressive Rollouts
Feature Flag Tools for Progressive Rollouts
Feature flags decouple deployment from release. You merge code to main and deploy it to production, but the new behavior is hidden behind a flag. You turn it on for 1% of users, watch your metrics, bump to 10%, then 50%, then 100%. If something breaks, you flip the flag off -- no rollback, no revert, no hotfix deploy. This guide covers the tools that make this workflow practical.
Why Feature Flags Matter
Without feature flags, deployment and release are the same event. You ship code and everyone gets it simultaneously. If something goes wrong, your options are revert (slow, risky) or push a fix forward (slower, riskier).
Feature flags give you a kill switch and a dimmer. You control who sees what, when, without touching your deployment pipeline. This enables:
- Progressive rollouts: Roll out to internal users, then beta users, then percentages of production traffic.
- A/B testing: Show variant A to half your users, variant B to the other half, measure which performs better.
- Canary deployments: Route a small percentage of traffic to the new code path, monitor for errors.
- Operational flags: Kill switches for expensive features during traffic spikes.
- Trunk-based development: Merge incomplete features to main behind flags instead of maintaining long-lived branches.
LaunchDarkly
LaunchDarkly is the market leader and the most feature-rich option. It supports every major language and framework, handles billions of evaluations daily, and has the deepest targeting and analytics capabilities.
Basic Usage
import * as ld from "@launchdarkly/node-server-sdk";
const client = ld.init("sdk-key-from-dashboard");
await client.waitForInitialization();
const context = {
kind: "user",
key: "user-123",
email: "[email protected]",
custom: { plan: "enterprise", region: "us-west" },
};
// Simple boolean flag
const showNewDashboard = await client.variation("new-dashboard", context, false);
if (showNewDashboard) {
renderNewDashboard();
} else {
renderOldDashboard();
}
Targeting Rules
LaunchDarkly's targeting is where it shines. You can target by user attributes, percentages, segments, and complex rule combinations -- all from the dashboard, no code changes.
# Example targeting rules (configured in dashboard)
1. If email ends with @ourcompany.com -> true (internal dogfooding)
2. If segment = "beta-testers" -> true
3. If plan = "enterprise" AND region = "us-west" -> 25% true
4. Default -> false
Strengths: Best-in-class targeting, real-time flag evaluation with streaming, excellent SDKs for every language, audit logs, approval workflows, experimentation (A/B testing), and strong compliance features.
Weaknesses: Expensive. Pricing starts around $10/seat/month for the Pro plan, and Enterprise pricing is opaque. For a small team, this can be hard to justify.
Best for: Teams that need enterprise-grade flag management with compliance, experimentation, and multi-environment support.
Unleash
Unleash is the leading open-source feature flag platform. You can self-host it for free or use their managed offering.
Self-Hosting
# Docker Compose for local development
docker compose up -d
# Or run directly with Docker
docker run -p 4242:4242 \
-e DATABASE_URL=postgres://user:pass@db:5432/unleash \
unleashorg/unleash-server
SDK Usage
import { initialize } from "unleash-client";
const unleash = initialize({
url: "https://unleash.yourcompany.com/api",
appName: "my-app",
customHeaders: { Authorization: "your-api-token" },
});
unleash.on("ready", () => {
const enabled = unleash.isEnabled("new-dashboard", {
userId: "user-123",
properties: { plan: "enterprise" },
});
});
Activation Strategies
Unleash has built-in activation strategies that cover most use cases:
- Standard: Simple on/off toggle.
- Gradual rollout: Percentage-based with sticky assignment (same user always gets the same value).
- UserIDs: Enable for a specific list of user IDs.
- IPs: Enable for specific IP addresses or ranges.
- Hostname: Enable on specific server instances.
You can also define custom strategies for domain-specific targeting.
Strengths: Open source (self-hostable), good SDK coverage, built-in gradual rollout, reasonable managed pricing, active community.
Weaknesses: The dashboard UI is less polished than LaunchDarkly. Experimentation features are limited to the paid plan. Self-hosting means you manage the infrastructure.
Best for: Teams that want control over their flag infrastructure and don't need enterprise compliance features.
Flagsmith
Flagsmith is another open-source option with a slightly different philosophy. It combines feature flags with remote configuration, letting you manage both boolean flags and configuration values in one place.
import flagsmith from "flagsmith/isomorphic";
await flagsmith.init({
environmentID: "your-env-id",
});
// Boolean flag
if (flagsmith.hasFeature("new_dashboard")) {
renderNewDashboard();
}
// Remote config value
const maxItems = flagsmith.getValue("max_items_per_page");
Strengths: Open source, combines flags with remote config, simpler than Unleash for basic use cases, edge API for low-latency evaluation.
Weaknesses: Smaller community than Unleash, fewer activation strategies out of the box.
Best for: Teams that want both feature flags and remote configuration in a single tool.
OpenFeature: The Vendor-Neutral Standard
OpenFeature is a CNCF project that defines a standard API for feature flag evaluation. Instead of coding directly against LaunchDarkly or Unleash, you code against OpenFeature and swap providers.
import { OpenFeature } from "@openfeature/server-sdk";
import { LaunchDarklyProvider } from "@launchdarkly/openfeature-node-server";
// Configure the provider (swap this to change vendors)
await OpenFeature.setProviderAndWait(new LaunchDarklyProvider("sdk-key"));
const client = OpenFeature.getClient();
const showDashboard = await client.getBooleanValue("new-dashboard", false, {
targetingKey: "user-123",
});
If you later switch from LaunchDarkly to Unleash, you change the provider initialization -- your application code stays the same. This is worth adopting early if you think you might switch providers or want to avoid vendor lock-in.
Rolling Your Own (When It Makes Sense)
For simple use cases, a feature flag system doesn't need to be complicated:
// Simple file-based flags
const flags = {
"new-dashboard": {
enabled: true,
percentage: 25, // 25% of users
allowlist: ["user-1"], // always enabled for these users
},
};
function isEnabled(flag: string, userId: string): boolean {
const config = flags[flag];
if (!config?.enabled) return false;
if (config.allowlist?.includes(userId)) return true;
// Deterministic percentage based on user ID
const hash = hashCode(`${flag}:${userId}`);
return (hash % 100) < config.percentage;
}
This works for small teams with simple needs. You lose the dashboard, targeting rules, audit logs, and analytics -- but you gain simplicity and zero dependencies.
Roll your own when: You have fewer than 10 flags, don't need a dashboard, and your targeting is simple (on/off or percentage-based).
Use a platform when: You have multiple teams managing flags, need audit trails, want A/B testing, or your targeting rules are complex.
Comparison
| Feature | LaunchDarkly | Unleash | Flagsmith | DIY |
|---|---|---|---|---|
| Open Source | No | Yes | Yes | N/A |
| Self-Hosted | No | Yes | Yes | Yes |
| Targeting | Excellent | Good | Good | Basic |
| A/B Testing | Built-in | Paid | Basic | No |
| SDKs | 25+ | 15+ | 12+ | N/A |
| Dashboard | Excellent | Good | Good | No |
| OpenFeature | Yes | Yes | Yes | No |
| Starting Price | ~$10/seat/mo | Free (OSS) | Free (OSS) | Free |
Best Practices
Name flags well. Use a consistent convention like enable-new-dashboard or experiment-checkout-flow-v2. Include the type (feature, experiment, operational) in the name.
Set expiration dates. Every flag should have a planned removal date. Flags that live forever become tech debt -- nobody remembers what they do, and nobody dares remove them. Most platforms let you set reminders or enforce cleanup.
Keep flag evaluation fast. SDKs cache flag values locally and evaluate them in-process. Don't make a network call on every evaluation. If you're rolling your own, cache aggressively.
Test both paths. Every flag creates two code paths. Write tests for both the enabled and disabled states. If you only test the enabled path, the disabled path will silently rot.
Use flags for operations, not just features. Create kill switches for expensive operations: disable real-time notifications during a traffic spike, fall back to cached data when your database is stressed, turn off non-critical background jobs during an incident.
Recommendations
- Small team, simple needs: Start with Unleash self-hosted or even a DIY solution. Migrate to a platform when your needs grow.
- Mid-size team: Unleash or Flagsmith. Both are open-source with managed options. Unleash has more activation strategies; Flagsmith is simpler.
- Enterprise: LaunchDarkly. The compliance, audit, and experimentation features justify the price at scale.
- Vendor-neutral: Adopt OpenFeature from day one. The abstraction cost is minimal and switching providers later is trivial.
- General principle: Ship behind flags, roll out gradually, measure before going to 100%. Feature flags are the cheapest insurance policy you'll ever buy.