Skip to content

Feature flags

Kilden feature flags are boolean or multivariate, targeted by person properties and cohorts, rolled out by percentage, and evaluated in a dedicated low-latency service (/decide). This guide covers the mechanics; the SDK methods are in the SDK reference.

In the panel, under your project’s Feature flags, create a flag with a key (what you’ll check in code), optional targeting, and a rollout percentage. Viewing flags requires project membership; creating or editing them requires an admin role — flags change product behavior, so they’re settings-grade.

Targeting is a list of condition groups. Groups combine with OR; conditions inside a group with AND. No groups means “everyone”.

Conditions can match:

  • Person properties: plan exact "pro", company icontains "acme". Operators are exact and icontains (case-insensitive contains); non-string values compare as their text form.
  • Cohort membership: the person is in a cohort. Membership is precomputed and refreshed about every 10 minutes — flags don’t evaluate cohort definitions live.

The rollout percentage applies after targeting matches: “50% of pro-plan users” means half the users who match the conditions.

For multivariate flags, define variants with weights that sum to 100 (control: 50, test: 50); the evaluated value is the variant key as a string.

Whether a given user falls inside a rollout is a pure function of the flag key and their distinct id — a hash mapped to [0, 100). No state, no coordination, no storage:

  • The same user gets the same value on every request, every SDK, every deploy.
  • Raising a rollout 30% → 50% only adds users; nobody who had the flag loses it.
  • Variant assignment uses an independent hash, so variants don’t correlate with the rollout cut.

One accepted trade-off: the bucket depends on the distinct id, so when identify() switches a user from their anonymous id to their user id, someone near the rollout boundary can change sides. This is the documented cost of stateless evaluation (persisting assignments would mean writes in the hot path and drift from config). The SDK reloads flags immediately after identify() and reset() so the value converges right away.

kilden.onFeatureFlags((flags) => {
render(flags['new-checkout'] === true);
});
// or point-in-time checks:
kilden.isFeatureEnabled('new-checkout'); // false until loaded
kilden.getFeatureFlag('pricing-test'); // 'control' | 'test-b' | true | false | undefined

Flags load asynchronously from POST {apiHost}/decide at init — there is no synchronous bootstrap, so gate flag-dependent rendering on onFeatureFlags. Every response carries all the project’s flags (inactive ones as false), letting the SDK distinguish “off” from “unknown”.

You can also call /decide directly from code that doesn’t use the SDK. Evaluation never writes anything: person_properties in the request override stored traits for that one evaluation only.

The first time a session reads a flag, the SDK emits $feature_flag_called with $flag_key and $flag_value. The panel charts these exposures per flag (daily series by value, 7-day totals with variant breakdown), so you can confirm a rollout is actually reaching users before trusting it.

  • Flag config changes reach /decide within ~10 seconds (it serves from an in-memory replica of the flag table).
  • Cohort membership is materialized about every 10 minutes.
  • Person property changes propagate through the event pipeline, then a short evaluation cache (~30 s). If you need an override now, pass person_properties in the /decide request.