A flag is a named switch with a set of possible values called variations. Code asks for a flag’s value; Shoal answers based on the environment and the context you pass in.
Flag types
| Type | Variations | Typical use |
|---|---|---|
boolean | true / false | Release toggles, kill switches |
string | Any list of strings | A/B variants, copy experiments |
number | Any list of numbers | Limits, timeouts, prices |
json | Any JSON objects | Remote config bundles |
const theme = await shoal.variation("checkout-theme", ctx, "classic");
const limit = await shoal.variation("upload-limit-mb", ctx, 25);
const config = await shoal.variation("pricing-page", ctx, { showAnnual: false });The last argument is the fallback. It is returned when the flag doesn’t exist, the SDK hasn’t loaded yet, or the value has the wrong type — your app never crashes because of a flag.
Evaluation order
For every call, Shoal walks these steps and stops at the first match:
- Flag off? Return the environment’s off variation.
- Individual targets. Is the user’s key listed on a variation? Return it.
- Rules. Evaluate targeting rules top to bottom.
- Default rule. Return the default variation or percentage rollout.
Lifecycle
Flags are meant to be temporary. Shoal marks a flag stale when it has served the same variation everywhere for 30 days, and shoal flags stale lists them so you can delete the code path.
- if (await shoal.isOn("new-checkout", ctx)) {
- return renderNewCheckout();
- }
- return renderClassicCheckout();
+ return renderNewCheckout();Permanent flags
Some flags never go away — kill switches, plan entitlements, operational limits. Mark them permanent to hide them from stale reports.