Experience Platform
Feature Flags

Feature Flags

The central place for builders to create feature flags, with an evaluation engine that determines which users see which features. Supports gradual rollouts and A/B experiments tied to event tracking.

Concepts

TermDefinition
FlagA named toggle or value that controls app behaviour. Has a key (e.g. new-checkout-ui), type (boolean/string/number/JSON), and default value.
RuleA condition-based override for a flag. "If user is in group X, return true." Rules have priority — highest priority rule that matches wins.
ConditionA single check within a rule. "profile_id IN [abc, def]" or "country EQ GB". Multiple conditions in a rule are ANDed.
RolloutA percentage-based gradual release. "Enable for 25% of users." Uses consistent hashing so a user always gets the same result.
ExperimentAn A/B test linked to a flag. Defines variants, traffic split, and success metrics.

Flag Types

TypeExample UseDefault Value
BOOLEANEnable/disable a featuretrue / false
STRINGFeature variant names, text overrides"control" / "variant-a"
NUMBERNumeric thresholds, limits10 / 99.99
JSONComplex configuration objects{"maxItems": 5, "layout": "grid"}

Rule Evaluation

When a flag is evaluated for a specific profile:

1. Get all rules for the flag, sorted by priority (highest first)
2. For each rule:
   a. Evaluate all conditions (AND logic — all must match)
   b. If conditions match:
      - If rollout_percentage < 100:
        - Hash(profile_id + flag_key) mod 100
        - If hash < rollout_percentage → return rule's value
        - Else → continue to next rule
      - If rollout_percentage == 100 → return rule's value
3. If no rules match → return flag's default value

Consistent Hashing for Rollouts

The hash of profile_id + flag_key is deterministic:

  • A user in the 25% rollout stays in the 25% (no flickering)
  • Increasing from 25% to 50% adds new users without removing existing ones
  • Different flags have independent hashes (being in 25% for flag A doesn't determine flag B)

Rule Conditions

AttributeDescriptionExample
profile_idSpecific profile targetingprofile_id IN [abc, def]
group_idTarget a groupgroup_id EQ engineering-team
countryGeo-targetingcountry IN [GB, US, DE]
app_versionVersion targetingapp_version GT 2.0.0
created_atAccount agecreated_at GT 2026-01-01
custom.*Custom attributes set by the appcustom.plan EQ premium

Operators: eq, neq, in, not_in, gt, lt, gte, lte, contains, regex


Pre-Computed Flag Distribution

Feature flags are NOT evaluated on every request. Instead:

Flag created/updated


  Evaluation Engine
  (background job)


  Compute resolved flags
  for ALL affected profiles


  Store in DB:
  { profileId, appId } → { flagKey: value, ... }


  Available instantly via:
  - GET /api/v1/flags/:appId/resolve?profileId=xxx
  - Included in auth session data on next login/refresh
  - SSE stream pushes update to connected clients

This means:

  • Flag reads are a simple DB lookup (fast, no rule evaluation at request time)
  • Flag writes trigger background recomputation (async, can handle millions of users)
  • The auth session includes resolved flags — apps don't need a separate API call

Experiments (A/B Testing)

An experiment links a feature flag to tracked events to measure impact:

Experiment: "New Checkout Flow"
├── Flag: new-checkout-ui (BOOLEAN)
├── Variants:
│   ├── Control (false) — 50% of users
│   └── Treatment (true) — 50% of users
├── Success Metric: checkout_completed event
├── Secondary Metrics: checkout_started, cart_abandoned
└── Duration: 14 days

The experiment results dashboard shows:

  • Conversion rate per variant
  • Statistical significance (p-value)
  • Confidence interval
  • Sample size per variant
  • Recommendation: "Treatment shows +12% conversion (p=0.003, significant)"

Usage in Code

// React SDK
const showNewCheckout = useFlag('new-checkout-ui', false);
const checkoutLimit = useFlag('checkout-limit', 100);
 
{showNewCheckout ? <NewCheckout /> : <OldCheckout />}
// JS SDK
const showNewUI = exp.getFlag('new-checkout-ui', false);
const checkoutLimit = exp.getFlag('checkout-limit', 100);

See the JS SDK and React SDK for full integration details.


API Endpoints

MethodPathAuthDescription
POST/api/v1/flagsBearerCreate flag
GET/api/v1/flagsBearerList flags for app
GET/api/v1/flags/:idBearerFlag detail + rules
PUT/api/v1/flags/:idBearerUpdate flag
DELETE/api/v1/flags/:idBearerDelete flag
POST/api/v1/flags/:id/rulesBearerAdd rule
PUT/api/v1/flags/:id/rules/:ruleIdBearerUpdate rule
DELETE/api/v1/flags/:id/rules/:ruleIdBearerDelete rule
GET/api/v1/flags/:appId/resolveAPI Key/BearerGet resolved flags for profile
GET/api/v1/flags/:appId/resolve/streamAPI KeySSE stream for real-time updates
POST/api/v1/experimentsBearerCreate experiment
GET/api/v1/experiments/:idBearerExperiment detail
GET/api/v1/experiments/:id/resultsBearerExperiment results + stats

See the full API Reference and Data Models for schemas.


© 2026 Shell Technology. All rights reserved.