Skip to content

Feature flags

Ship dark, roll out gradually, and remove the flag when done.

Updated View as Markdown

Statsig is the standard provider. Services use the vendor SDKs directly, with no internal wrapper. Some services still use OpenFeature with LaunchDarkly. Treat these as legacy and move them to Statsig. The web reference implementation is ninja-web-trader/docs/guides/STATSIG.md. Follow it for the React surface.

  • Ship dark and roll out gradually.
  • Keep a kill-switch that fails safe.
  • Remove the flag and dead branch when the rollout finishes.

Pick the right primitive

Need Use
A yes/no decision Feature gate (boolean)
A value, scoped to one feature Dynamic config
A value read across many surfaces Parameter store
To measure the impact of a change Experiment

If the answer is “100 rows for free tier, 500 for paid,” use two values in a dynamic config, not two gates.

Access patterns

On the web, you can reach every primitive in two ways. Use the React hook inside a component. Use the singleton outside React, such as in async orchestrators, model files, and store actions.

import { useFeatureGate } from "@statsig/react-bindings";

function PortfolioPanel() {
  const { value: showNewMetrics } = useFeatureGate("portfolio-new-metrics");
  return showNewMetrics ? <NewMetricsView /> : <LegacyMetricsView />;
}
// outside React — one-shot evaluation
const gate = StatsigClient.instance().getFeatureGate("web-trader-refresh-test-flag");

Fail safe

The SDK already fails safe. A gate uses its default. config.get(key, fallback) returns the fallback when the key is missing or the SDK is still loading. Do not wrap the SDK to add this. The safe default is the accessor’s fallback argument.

const config = useDynamicConfig("order-ticket-defaults");
const maxQty = config.get("maxQuantity", 100); // fallback on miss or load

On the backend, use the server SDK to evaluate the flag. If evaluation fails, use the proven path (illustrative):

Flag types and lifespan

Name the type. Each type lasts for a different amount of time:

Type Purpose Lifespan
Release Ship dark, then ramp a new feature Temporary, delete at 100%
Ops kill-switch Disable a path under load or failure Long-lived
Experiment Measure a change before deciding Until the experiment concludes
Permission / entitlement Gate access by plan or role Long-lived, enforced server-side

A release flag is temporary by default. It has an owner and a removal date.

Rollout

Start at 0% or with an internal allowlist. Increase exposure by percentage or targeting rule. At each step, check the error rate and key metrics before increasing exposure. See observability. Keep the ability to return to 0% instantly. This is the kill-switch. Assign users by the stable userID, resolved up front, so each user sees consistent behaviour across the Scala, Node, and web surfaces.

Authorization

A feature flag controls what a user sees. It does not control what they are allowed to do. Enforce entitlement and access control on the server. Never use a client-delivered flag as the authorization boundary. See security.

Testing

While a flag exists, test both branches. Also test the fail-safe default when the SDK is unavailable. This prevents a flag flip from shipping an untested path. Never suppress an experiment’s exposure logging. It lets the experiment connect outcomes to the variant a user saw. See testing.

Cleanup

Cleanup is mandatory. When a rollout reaches 100%, open a cleanup ticket. Removing the flag deletes both the dead code branch and the gate definition in Statsig. No central key registry exists yet. If you read the same key in three or more places, extract a constant instead of repeating string literals. Keep logEvent metadata low-cardinality (symbol, side, tier, never order IDs or timestamps).

stateDiagram-v2
direction LR
[*] --> Created
Created --> Rollout: start at 0% or allowlist
Rollout --> Ramp: increase exposure
Ramp --> Rollout: kill-switch to 0%
Ramp --> Full: reaches 100%
Full --> Cleanup: delete branch and gate
Cleanup --> [*]

References

Navigation

Type to search…

↑↓ navigate↵ selectEsc close