Skip to main content

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

PropertyTypeRequiredDescription
idstringyesUnique group identifier
projectIdstringyesProject this group belongs to
namestringyesHuman-readable group name
descriptionstringnoWhy these flags are mutually exclusive
strategystringyesfirst_wins or priority_ordered
flagKeysarrayyesFlag keys in this group
prioritiesobjectnoPriority 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

ScenarioResult
Flag matches and is the winnerReturns the matched value (reason: TARGETING_MATCH or SPLIT)
Flag would match but another flag wonReturns default value (reason: MUTUAL_EXCLUSION)
Flag doesn't match (targeting miss)Returns default value (reason: DEFAULT)
Flag is disabledReturns 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.