The two-step allocation
ABTestly follows the Amplitude-style two-step model: first decide whether the visitor enters the experiment, then decide which variant.Step 1: allocation
salt, the experiment’s immutable UUID (itsbucketingSalt). Every experiment gets its own salt at creation and it never changes.userId, the visitor’s stable id (UUIDv7 in localStorage + cookie).trafficAllocationBp, allocation in basis points (10000 = 100 %).
enter is false, the visitor is not in the test. The exposure event
does not fire. The visitor sees the original page.
Step 2: variant pick
hash2 wins.
The hash
MurmurHash3 32-bit, x86 variant, seed 0. Well-known, fast, uniformly distributed for short strings. The two different suffixes (/Exposure and /Bucket) mean the
allocation decision and the variant decision are independent.
increasing allocation to 60 % does not shuffle which visitors get
which variant among those already inside.
Why the salt is per-experiment
Two reasons:- A visitor that “always sees Variant A” would be wrong. If two experiments shared a salt, a visitor bucketed to variant 0 on test 1 would also be bucketed to variant 0 on test 2. That is a subtle correlation you almost never want. Per-experiment salts break that dependency.
- Cloning generates a new salt on purpose. So a visitor that saw Variant B in the original does not automatically see Variant B in the clone. Cleaner reads on both.
Where userId comes from
__abtestly_uid, a UUIDv7 written to both localStorage and a cookie
on first visit.
The cookie domain is resolved to eTLD+1 via a public-suffix probe, so
www.acme.com and blog.acme.com share a visitor id when both use
ABTestly on the same registrable domain.
Clearing either the cookie or localStorage produces a new id. Visitors
who clear cookies weekly are re-bucketed weekly; there is no way
around that without something like fingerprinting, which we do not do.
Reproducing a bucket in Node
Mutual exclusion groups
Bucketing inside an exclusion group is a step before per-experiment bucketing. First the visitor is assigned a slot in the group’s 10000-slot space; whichever member owns that slot is the one the visitor is eligible for. Then the standard two-step runs for that one member.What could shift a visitor’s bucket
- Cleared cookies/localStorage. New id, new bucket. This is visible-to-user and expected.
- Cloned experiment. New salt, so re-bucketed. This is expected and desirable.
- Reset data on the experiment. New epoch, but same salt, the bucket does not shift.
- Weights changed on the experiment. For a visitor already inside, the variant they get depends on the new weights. See Variations for why weight changes on running experiments are gated.