Feature Flags

Roll out features gradually without deploying new code. Feature flags let you control what users see from the CADENCE dashboard.

Feature flags vs. experiments

Both control what users see, but they serve different purposes:

| | Feature Flags | Experiments | |---|---|---| | Purpose | Ship safely | Measure impact | | Output | Boolean (on/off) | Variant name (string) | | Tracking | No automatic tracking | Auto exposure tracking | | Rollout | Percentage-based (0–100%) | Traffic allocation + variant weights | | Dashboard | Enable/disable toggle | Full results with statistics |

Use feature flags when you want to gradually enable a feature. Use experiments when you want to measure whether a change improves a metric.

Creating a feature flag

  1. Navigate to your project in the dashboard
  2. Create a new feature flag
  3. Set a key (kebab-case, e.g., new-checkout-flow)
  4. Set enabled status and rollout percentage

The rollout percentage controls what fraction of users see the feature. Set it to 5% to test with a small group, then increase to 25%, 50%, and eventually 100%.

Using isFeatureEnabled()

The simplest way to check a flag. Returns true or false:

typescript
await cadence.ready()

if (cadence.isFeatureEnabled('new-checkout-flow')) {
  return <NewCheckout />
} else {
  return <LegacyCheckout />
}

The check is deterministic: the same user always gets the same result for the same flag, based on a hash of their user ID and the feature key.

Using getFeatureValue()

For feature values beyond simple booleans, use getFeatureValue():

typescript
const price = cadence.getFeatureValue('hero-price', 9.99)
const limit = cadence.getFeatureValue('upload-limit-mb', 10)
const theme = cadence.getFeatureValue('color-theme', 'light')

getFeatureValue() has a two-step resolution:

  1. Checks experiment overrides first. If an active experiment variant has a matching feature_overrides key, that value is returned.
  2. Falls back to the feature flag. If the flag is enabled for this user, returns true. Otherwise returns your defaultValue.

This means experiments can override feature values dynamically, which is useful for testing different configurations.

Common patterns

Dark launches

Deploy a feature behind a flag, then enable it gradually:

typescript
// Day 1: deployed but off (0% rollout)
// Day 2: enable for 5% of users
// Day 5: increase to 25%
// Day 10: increase to 100%

if (cadence.isFeatureEnabled('redesigned-dashboard')) {
  return <NewDashboard />
}
return <CurrentDashboard />

Kill switches

Wrap risky features in a flag so you can disable them instantly from the dashboard:

typescript
if (cadence.isFeatureEnabled('ai-recommendations')) {
  return <AIRecommendations />
}
return <ManualRecommendations />

If the AI service goes down, disable the flag from the dashboard. No deploy needed.

Experiment-driven overrides

Combine experiments with feature values. Create an experiment where each variant sets different feature override values:

typescript
// The experiment assigns a variant, and the variant's feature_overrides
// set the price value. getFeatureValue() picks it up automatically.
cadence.getVariant('pricing-experiment')
const price = cadence.getFeatureValue('monthly-price', 29)

Best practices

Name flags descriptively. new-checkout-flow is better than flag-1 or test. Your future self will thank you.

Clean up after full rollout. Once a feature is at 100% and stable, remove the flag from your code. Flag debt is tech debt.

Don't nest flags. Avoid checking one flag inside another. It makes behavior hard to predict and debug.

Use experiments for measurement. If you want to know whether a feature improves a metric, use an experiment with getVariant() instead of a flag. Experiments track exposures automatically and provide statistical analysis.

Next steps

  • API Reference — Full method signatures for isFeatureEnabled() and getFeatureValue()
  • Creating Tests — When you need measurement, not just rollout