ScaleBun
Skip to article

Flags, config & experiments

webDeveloper

Evaluate feature flags, read remote config, run rollouts and experiments with deterministic, offline-safe bucketing that matches web, mobile, and backend byte-for-byte.

Updated Reviewed

Flags, remote config, rollouts, and experiments share one feature. Bucketing is computed locally and deterministically (fnv1a32), so the same user lands in the same variant on the web, in your app, and on your backend — and it works offline, with SSR bootstrap and live updates over SSE.

Feature flags#

TypeScript
if (ScaleBun.flags.isEnabled('new-checkout')) {  renderNewCheckout();}
const color = ScaleBun.flags.variation('cta-color', '#111');

Wait for the first flag payload when you must:

TypeScript
await ScaleBun.flags.ready();

variationDetail returns why a user was served what they were served, which is what lets you segment exposures by reason rather than just by value:

TypeScript
const { value, variant, reason } = ScaleBun.flags.variationDetail('cta-color', '#111');// reason: 'TARGETING_MATCH' | 'ROLLOUT' | 'DEFAULT' | 'FLAG_NOT_FOUND' | …

Reacting to changes#

Flag edits and kill-switches arrive over a realtime stream within seconds. That updates the cache — to update the screen, subscribe:

TypeScript
const unsubscribe = ScaleBun.flags.onChange(() => rerender());

React has a hook for this (useFlag); other frameworks wire it by hand. See Integrations.

No flash of default#

On a cold cache the first paint happens before the config request returns, so a flagged element renders its fallback and then swaps — the flicker users notice. Seed the SDK from your server render to remove it:

TypeScript
await ScaleBun.init({  clientKey,  apiBaseUrl,  flags: {    bootstrap: serverConfigPayload,  // same shape as GET /config    realtime: true,                  // default; false = poll only, no stream  },});

Experiments and rollouts#

TypeScript
const arm = ScaleBun.experiments.variant('pricing-test'); // 'control' | 'treatment' | nullconst on = ScaleBun.rollouts.isOn('beta-banner');         // percentage rollout / kill-switch

Exposures#

Reading a flag records an exposure once per (flag, value) per session: it stamps $feature/<key> as a super-property, so every later event carries the variant without a join, and emits one $feature_flag_called.

Reads are cheap and idempotent — call them at the point of use rather than caching the value in your own state, or the exposure will not line up with what the user actually saw.

Remote config#

Read dashboard-set values at runtime without shipping a release. A fallback is required — it is the value used before the first fetch, when offline, when the key is missing, or when the stored value fails to coerce:

TypeScript
const maxItems = ScaleBun.config.get('max_items', 10);

Overrides (QA)#

TypeScript
ScaleBun.flags.override('cta-color', 'blue'); // persisted, for local testingScaleBun.flags.clearOverride('cta-color');ScaleBun.flags.clearOverrides();              // drop all of them

Overrides persist across reloads, which is what makes them useful for QA and also what makes them easy to forget — clearOverrides() is the reset.

Remote config refresh#

TypeScript
await ScaleBun.config.refresh();  // force a re-read, e.g. after a context change

Next#

Flags, config & experiments · Web SDK · ScaleBun