Skip to main content
Exclusion groups run one step before each member’s own bucketing. The math is small and completely deterministic.

The 10,000-slot space

Every group has an internal bucketing space of 10,000 slots, numbered 0 through 9999. That is the whole space; there is no larger number, no scaling factor. When you add a member to a group, you give it a share, a non-negative integer of slots it owns. Members can have:
  • Equal shares. Five members at 2,000 slots each = 10,000 total, full coverage.
  • Uneven shares. One test at 5,000, four at 1,250 each = 10,000 total.
  • Partial coverage. Two tests at 4,000 each = 8,000 owned, 2,000 unallocated.
Unallocated space is the visitors who see none of the group’s experiments. That is a valid choice and often a deliberate one. see holdouts.

The assignment step

On page load, before per-experiment bucketing runs, the group does this:
  • groupSalt, the group’s immutable UUID, generated on creation and never changed.
  • userId, the visitor’s stable id, from the __abtestly_uid cookie (see bucketing for how that is minted).
  • mod 10000, coerces the hash to a slot number.
Then: which member owns slot? If the slot falls inside a member’s share range, the visitor is eligible for that one member only. Every other member of the group is silently skipped for this visitor. If the slot is in the unallocated range, the visitor sees none of the group’s experiments.

Slot ranges, worked out

Take a group with three members: A owns 3,000, B owns 4,000, C owns 1,000. Total = 8,000 owned; 2,000 unallocated. The order members are listed in the group affects which slot range each one gets, but not the split proportions. Reordering members re-dices every visitor because the ranges shift.

Determinism and stickiness

The hash is deterministic. Given the same groupSalt and userId:
  • The visitor gets the same slot on page one and page 500.
  • The same visitor on their next visit still hashes to the same slot.
  • Two different visitors get statistically independent slots (assuming userIds are unique, which they are, UUIDv7).
That is the “sticky membership” property. A visitor bucketed to member A on their first visit stays on member A forever, unless the group’s membership changes.

Membership change → generation bump

Every time you add, remove, or change the share of a member, the group’s generation counter increments. Generation is part of the assignment: murmur3(groupSalt + '/' + userId + '/' + generation). Bumping the generation re-dices every visitor into potentially different slots. Why re-dice? Because if you added a new member and the new member took slots away from an existing member, sticky membership would lock existing visitors into ranges that no longer belong to their member. Re-dicing fixes that at the cost of “some visitors switch which test they see”. The generation shows up on every exposure event (as gn) so the results page can tell you if group membership changed mid-experiment. The activity log also records generation bumps.

Why exclusion runs before per-experiment bucketing

If it ran after, a visitor could be bucketed into experiment A, then into experiment B, and only then the exclusion check would kick one of them out. The “kick” would happen after A’s or B’s own bucketing hash was computed, leaving one experiment’s results with a mysterious “some visitors were bucketed but not exposed” cell. Running exclusion first cleanly separates the two decisions:
  1. Which member of the group is this visitor eligible for? (Group decides.)
  2. Does the visitor enter that member’s traffic allocation, and which variant? (Member decides.)
Every experiment’s stats stay clean.

Salt and code

If you want to re-derive a visitor’s group assignment in a Node script for an audit, the salt is inside the group’s config blob. Ask support for the salt; the hash is standard MurmurHash3 x86 32-bit, seed 0. Reference implementation:
Same inputs, same output, every time.

What does not shift a visitor’s group slot

  • Cache clear on their browser resets their userId, so they hash to a new slot, same as it would in any deterministic bucketing.
  • Publishing a new site config does not change the group assignment unless the change touched group membership.
  • Adding a new experiment outside the group has no effect at all.

Next

Long-term holdouts

Deliberately unallocate a percentage of traffic to measure long-tail effects.

Managing membership

Add, retire, and reorder members. What generation bumps do to running experiments.