Multivariate Flags
Multivariate flags go beyond on/off toggles. Define weighted variants to serve different values, configurations, or UI experiences to different user segments.
Multivariate flags go beyond simple on/off toggles. Define weighted variants to serve different values, configurations, or UI experiences to different user segments.
Defining Variants#
Each variant has a key, a value, and a weight (percentage). Weights must sum to 100 (Abrova Console enforces this). All values of one flag have the flag's type: string, number, json or boolean.
{
"variants": [
{ "key": "control", "value": "old_checkout", "weight": 80 },
{ "key": "variant_a", "value": "new_checkout", "weight": 15 },
{ "key": "variant_b", "value": "minimal_checkout", "weight": 5 }
]
}How Variant Selection Works#
Variant selection is deterministic. Abrova Flags uses deterministic hashing into 10,000 buckets (0 to 9,999) and walks the variants in order by weight. This ensures each user always gets the same variant, in every SDK.
bucket = deterministic hash of the user → 0 … 9999 Variants: control (80%) | variant_a (15%) | variant_b (5%) Ranges: 0-7999 | 8000-9499 | 9500-9999 If bucket = 4200 → control (old_checkout) If bucket = 8500 → variant_a (new_checkout) If bucket = 9700 → variant_b (minimal_checkout)
- The id is the user id, or the anonymous id when no user is signed in. A user who signs in is placed again by their user id.
- The variant bucket is independent of the rollout bucket of the same flag.
- A user only gets a variant when the flag is on for them. Otherwise the flag's off value is served and
variantis empty. - Without any id, a split cannot be decided: the flag is served off with reason
NO_BUCKETING_ID. Client SDKs always send an anonymous id, so this only happens on a server when you pass neitheruserIdnoranonymousId.
Using Variants in Code#
Typed getter — get the variant value
// Client SDKs
const checkoutFlow = flags.getString('checkout_flow', 'old_checkout');
// Returns: 'old_checkout', 'new_checkout', or 'minimal_checkout'
// Server SDKs: pass the context
const checkoutFlow = flags.getString('checkout_flow', { userId: 'user-123' }, 'old_checkout');Use the getter that matches the flag type: getString, getNumber, getJson or getBoolean (getBool in Flutter). A getter of another type returns your default with reason TYPE_MISMATCH.
getDetail — get the full result with the variant key
// Client SDKs (server SDKs: add the context as the second argument)
const result = flags.getDetail('checkout_flow', 'old_checkout');
// result includes {
// value: 'new_checkout',
// enabled: true,
// variant: 'variant_a',
// reason: 'VARIANT',
// version: 4
// }Flutter
final result = flags.getStringDetail('checkout_flow', 'old_checkout');
switch (result.variant) {
case 'control':
return OldCheckout();
case 'variant_a':
return NewCheckout();
case 'variant_b':
return MinimalCheckout();
default:
return OldCheckout();
}Use Cases#
Test multiple versions of a feature simultaneously to find the best performer
Return different config values (colors, copy, limits) per variant
Release a new experience to a small group while keeping the old one for most users
Serve different UI layouts or content based on user segments
Variant analytics are available in the flag detail page in Abrova Console. They count the evaluations the service makes for client SDKs. Server SDK evaluations run locally and are included only when the SDK runs with sendUsage: true.