Ensure only one flag in a group is active for any given user to prevent experiment interference
Mutual Exclusion Groups
Mutual exclusion groups ensure that at most one flag in a group can return a non-default value for any given evaluation. This prevents experiments and features from interfering with each other — a user in experiment A won't also be in experiment B.
When to Use Mutual Exclusion
Use mutual exclusion groups when:
- Running concurrent experiments on the same page or flow — you want clean results without interaction effects
- Rolling out competing features — only one should be active per user
- A/B testing variants of the same experience — overlapping treatments would confuse results
Without mutual exclusion:
User sees Experiment A (new checkout) AND Experiment B (new pricing)
→ Can't attribute conversion change to either experiment
With mutual exclusion:
User sees EITHER Experiment A OR Experiment B (not both)
→ Clean attribution per experiment
How It Works
When a flag belongs to a mutual exclusion group, Flaggr evaluates all flags in the group and picks at most one winner:
1. Order flags by strategy (first_wins or priority_ordered)
2. Evaluate each flag against the evaluation context
3. First flag that matches (non-default, non-disabled) wins
4. All remaining flags return their default value with reason MUTUAL_EXCLUSION
Example
Group: checkout-experiments contains flags exp-short-signup, exp-one-click-buy, exp-guest-checkout.
For user-123:
exp-short-signup→ matches targeting rule → WINS (returns treatment value)exp-one-click-buy→ would match, but excluded → returns default value (reason:MUTUAL_EXCLUSION)exp-guest-checkout→ would match, but excluded → returns default value (reason:MUTUAL_EXCLUSION)
Strategies
first_wins
Flags are evaluated in the order they appear in the group's flagKeys array. The first flag that returns a non-default, non-disabled value wins.
{
"id": "grp-checkout",
"name": "Checkout Experiments",
"strategy": "first_wins",
"flagKeys": ["exp-short-signup", "exp-one-click-buy", "exp-guest-checkout"]
}Use first_wins when all flags have equal priority and you want simple, predictable behavior.
priority_ordered
Flags are sorted by explicit priority values (higher number = higher priority). The highest-priority flag that matches wins.
{
"id": "grp-checkout",
"name": "Checkout Experiments",
"strategy": "priority_ordered",
"flagKeys": ["exp-short-signup", "exp-one-click-buy", "exp-guest-checkout"],
"priorities": {
"exp-short-signup": 10,
"exp-one-click-buy": 20,
"exp-guest-checkout": 5
}
}In this example, exp-one-click-buy (priority 20) is evaluated first. If it matches, the other two flags return their default values.
Use priority_ordered when some experiments or features should take precedence over others.
Creating a Mutual Exclusion Group
curl -X POST /api/mutual-exclusion \
-H "Authorization: Bearer flg_your_token" \
-H "Content-Type: application/json" \
-d '{
"id": "grp-checkout",
"projectId": "proj-123",
"name": "Checkout Experiments",
"description": "Only one checkout experiment runs per user",
"strategy": "first_wins",
"flagKeys": ["exp-short-signup", "exp-one-click-buy", "exp-guest-checkout"]
}'Group Properties
| Property | Type | Required | Description |
|---|---|---|---|
id | string | yes | Unique group identifier |
projectId | string | yes | Project this group belongs to |
name | string | yes | Human-readable group name |
description | string | no | Why these flags are mutually exclusive |
strategy | string | yes | first_wins or priority_ordered |
flagKeys | array | yes | Flag keys in this group |
priorities | object | no | Priority map (required for priority_ordered) |
Updating a Group
Add a Flag
curl -X PATCH /api/mutual-exclusion/grp-checkout \
-H "Authorization: Bearer flg_your_token" \
-H "Content-Type: application/json" \
-d '{
"flagKeys": ["exp-short-signup", "exp-one-click-buy", "exp-guest-checkout", "exp-social-login"]
}'Change Strategy
curl -X PATCH /api/mutual-exclusion/grp-checkout \
-H "Authorization: Bearer flg_your_token" \
-H "Content-Type: application/json" \
-d '{
"strategy": "priority_ordered",
"priorities": {
"exp-short-signup": 10,
"exp-one-click-buy": 30,
"exp-guest-checkout": 5,
"exp-social-login": 20
}
}'Constraints
One Group Per Flag
A flag can only belong to one mutual exclusion group at a time. Attempting to add a flag that's already in another group returns an error:
{
"error": "Flag 'exp-short-signup' is already in mutual exclusion group 'grp-onboarding'"
}To move a flag between groups, first remove it from the existing group, then add it to the new one.
Evaluation Behavior
| Scenario | Result |
|---|---|
| Flag matches and is the winner | Returns the matched value (reason: TARGETING_MATCH or SPLIT) |
| Flag would match but another flag won | Returns default value (reason: MUTUAL_EXCLUSION) |
| Flag doesn't match (targeting miss) | Returns default value (reason: DEFAULT) |
| Flag is disabled | Returns default value (reason: DISABLED) |
Non-Matching Flags Don't Block
If no flag in the group matches for a given user, no flag wins. Each flag returns its own non-match reason (DEFAULT or DISABLED), not MUTUAL_EXCLUSION.
Evaluation Result
When evaluating through the mutual exclusion system, the response includes per-flag results:
{
"winnerFlagKey": "exp-one-click-buy",
"results": {
"exp-short-signup": {
"value": false,
"reason": "MUTUAL_EXCLUSION",
"excluded": true
},
"exp-one-click-buy": {
"value": true,
"reason": "TARGETING_MATCH",
"excluded": false
},
"exp-guest-checkout": {
"value": false,
"reason": "MUTUAL_EXCLUSION",
"excluded": true
}
}
}The excluded field makes it easy to distinguish between "didn't match" and "matched but was excluded by mutual exclusion."
Use Cases
Concurrent Experiments
Run multiple experiments on the same page without interference:
Group: "homepage-experiments"
├── exp-hero-banner-v2 (new hero layout)
├── exp-search-first (search bar at top)
└── exp-social-proof (customer testimonials)
Each user sees at most one experiment.
Feature Rollout Competition
When rolling out multiple features that affect the same workflow:
Group: "payment-features"
├── apple-pay-integration (priority: 30)
├── buy-now-pay-later (priority: 20)
└── crypto-payments (priority: 10)
Higher-priority features take precedence.
Regional Compliance
Different compliance features that shouldn't stack:
Group: "compliance-banners"
├── gdpr-consent-v2 (EU users)
├── ccpa-notice-v2 (California users)
└── lgpd-consent (Brazil users)
Only one compliance banner shows per user.
Related
- Experiments — A/B testing with variants and metrics
- Targeting Rules — Conditions and operators for targeting
- Advanced Evaluation — Consistent hashing for deterministic assignment
- Concepts — Core data model and evaluation flow