ShipOS docs
Flags Evaluation

Feature Flags Overview

A flag is a named switch with a kind and a per-environment config. This page covers what a flag is, what states it holds, and exactly how a value gets chosen at evaluation time.

Flag kinds

Every flag is one of four kinds. The kind fixes the type of valueOn, valueOff, and defaultValue.

KindServesExample value
booleanOn/off gatetrue
stringA variant name or label"variant-b"
numberA tunable value25
jsonA structured config blob{ "limit": 100 }

States

Within an environment, a flag carries three states that gate what it serves:

  • enabled, the on/off master switch for the environment. Off means the off value is served to everyone.
  • rolloutPct, 0–100. The share of users who get the on value when no targeting rule decides for them. See Rollouts.
  • killed, an emergency override that serves OFF to 100% of traffic in seconds, independent of the above. See Kill Switch.

defaultValue behavior

Two defaults exist and they are not the same thing:

  • The flag's defaultValue is served by the edge when the flag is missing or archived, a server-side fallback.
  • The default you pass to flag() is returned by the SDK on any failure, network error, timeout, unknown key. The call never throws.

Always pass a default

Because flag() returns your default on any failure, your code has a deterministic answer even when ShipOS is unreachable. Treat it as the value your app runs on when nothing else is known.

flag() vs flagDetail()

flag() returns just the value. flagDetail() returns the value plus why, the reason, the variation served, and, for percentage gates, the matched rule and the user's bucket.

detail.tstypescript
const value = await shipos.flag("checkout-v2", {
  userId: "u_42",
  default: false,
});
// => true

const detail = await shipos.flagDetail("checkout-v2", { userId: "u_42" });
// => {
//   value: true,
//   reason: "rule_match",
//   variation: "on",
//   ruleId: "rule_pro_users",
//   bucket: 37
// }

How a value is chosen

At evaluation time the edge composes the environment config into a single value in this order:

  • Killed? Serve OFF to everyone. Stop.
  • Not enabled? Serve the off value. Stop.
  • Targeting rules, evaluated top to bottom. The first rule whose conditions all match decides the value, optionally gated by that rule's own rolloutPct. See Targeting.
  • No rule matched? Fall back to the base enabled + rolloutPct, a stable percentage bucket on the user decides on vs. off.

Reading more