bext.dev
DocsApplication Toolkit
Application Toolkit3 min read
On this page

Feature Flags

@bext-stack/framework/flags is a Pennant-style feature-flag client for PRISM apps. bext already has a Rust flag engine behind /__bext/sdk/flags/evaluate, but it's server-file configured and there was no typed client — apps hand-rolled fetch(). This module gives you two modes, and they compose:

  1. Local, code-defined flags — define a flag as a boolean, a predicate over the request context, or a deterministic, sticky percentage() rollout. Pure, no server, unit-testable — this is the Pennant headline bext was missing.
  2. Remote fall-through — for keys you didn't define locally, evaluate against the Feature Flag capability's SDK endpoint (when a provider is configured).

When To Use It#

  • Ship code dark and turn it on for a fraction of users.
  • Gate a beta behind an allowlist or an attribute.
  • Run an A/B/n experiment with sticky assignment.

Defining & evaluating#

ts
import { defineFlags, percentage, forUsers, forAttribute } from "@bext-stack/framework/flags";

const flags = defineFlags({
  flags: {
    "new-checkout":    percentage(30),                    // sticky 30% rollout
    "beta-banner":     forUsers("u_1", "u_2"),
    "holiday-theme":   forAttribute("country", "FR"),
    "priority-queue":  (ctx) => ctx.attributes?.plan === "enterprise",
  },
  remote: { appId: "my-site" },                           // optional server fallback
  default: false,
});

if (await flags.isEnabled("new-checkout", { userId: user.id })) { /* … */ }

const arm = flags.variant("home-hero", ["control", "b", "c"], { userId: user.id });
Member Returns
isEnabled(key, ctx?) Promise<boolean> — local wins, else remote, else default
isEnabledSync(key, ctx?) boolean — local only (remote keys → default)
variant(experiment, variants, ctx?) a sticky arm for the subject
all(ctx?) { flag: boolean } for every local flag — bootstrap a UI
defined the local flag names

Rule helpers: percentage(pct) (deterministic, salted by the flag key so two 50% flags don't enable the identical half), forUsers(...ids), forAttribute(key, value), or any (ctx) => boolean.

Deterministic & sticky#

Percentage rollouts and variant assignment hash the subject (userIdsessionId → none) with the flag/experiment key, so:

  • the same user always gets the same answer (no flip-flop between requests),
  • a user with no id can't be bucketed stickily, so a percentage flag stays off for them (fail-closed), and
  • two independent flags at the same percentage don't enable the same cohort.
Tip

Prefer userId in the context for logged-in users, and a stable sessionId (e.g. from a signed session) for anonymous ones — that keeps a rollout sticky across a visitor's requests.

Relationship to the Feature Flag capability#

The Rust Feature Flag capability is the server-side, file/provider-configured engine (also powers experiment includes). This module is the in-code TypeScript client: define flags next to the code they gate, evaluate them with no round-trip, and fall through to the engine only for keys you leave to ops.

Try It#

The live feature-flags demo lets you type a user id and watch which flags evaluate on — deterministic and sticky, so the same name always resolves the same way. Source in sites/demo/src/app/examples/feature-flags/page.tsx.

See Also#

Edit this page ↗Need a hand? ↗
FIND YOUR NEXT STEP

Start with a topic, a command, or a question.