Skip to content

User Context and Bucketing

advanced

The problem: consistent assignment across sessions

Section titled “The problem: consistent assignment across sessions”

When a visitor enters an experiment, they must see the same variation every time they return. If a user sees variation A on Monday and variation B on Tuesday, the experiment data is corrupted — you cannot attribute conversions to the correct experience. Consistency is not optional; it is a prerequisite for valid experiment results.

At the same time, traffic must be split evenly across variations. If 60% of users accidentally end up in variation A and only 40% in variation B, your statistical power drops and results take longer to reach significance.

Optimizely solves both problems with a deterministic bucketing algorithm that produces consistent, uniformly distributed assignments without storing per-user state on the server.

Optimizely uses MurmurHash3 to assign users to variations. The process works like this:

  1. Combine the user’s identifier and the experiment ID into a single string (the bucketing key).
  2. Hash that string with MurmurHash3, producing a 32-bit integer.
  3. Map the integer to a value between 0 and 10,000 (representing 0.00% to 100.00% of traffic).
  4. Compare that value against the experiment’s traffic allocation ranges to determine which variation the user receives.

Because MurmurHash3 is deterministic, the same input always produces the same output. A user with ID user-123 in experiment exp-456 will always hash to the same bucket number, and therefore always see the same variation. No database lookup is needed.

In Feature Experimentation, you explicitly build a user context:

const userContext = optimizelyClient.createUserContext('user-123', {
plan_tier: 'premium',
country: 'US',
lifetime_value: 450
});
const decision = userContext.decide('checkout_redesign');

The user ID is the bucketing key. It must be a stable identifier that persists across sessions. Good choices include database user IDs, authenticated account IDs, or device IDs. Avoid values that change between visits (session IDs, random values) unless you intentionally want independent bucketing per session.

Attributes are key-value pairs that describe the user. They serve two purposes:

  • Audience targeting — Attributes are evaluated against audience conditions to determine whether a user qualifies for an experiment.
  • Contextual signals — Attributes can be used by CMAB for personalized variation assignment.

Attributes are not used in the bucketing hash. They filter who enters the experiment, but they do not affect which variation the user sees.

Web Experimentation manages user identity automatically through cookies:

  • optimizelyEndUserId — A persistent cookie that identifies the visitor across sessions. This value is the bucketing key.
  • Audience conditions — Evaluated from browser state: URL, referrer, cookies, query parameters, device type, geographic location, and custom attributes.

Custom attributes can be set programmatically:

window.optimizely.push({
type: 'user',
attributes: {
plan_tier: 'premium',
loyalty_member: true
}
});

Traffic allocation controls what percentage of eligible users enter an experiment. It operates at two levels:

  1. Experiment-level allocation — What percentage of total traffic is included in the experiment. Setting this to 50% means half of eligible visitors are bucketed and half are excluded entirely.
  2. Variation-level allocation — How bucketed traffic is split among variations. In a two-variation test with even split, each variation gets 50% of the bucketed traffic.

You can change traffic allocation mid-experiment. When you increase allocation, new users in the expanded range are bucketed. Users already bucketed are not affected — their hash value has not changed, and their bucket number still falls in the same variation’s range.

When you run multiple experiments simultaneously, a single user might be bucketed into several experiments at once. If those experiments modify the same page or feature, their changes can conflict and produce unreliable data.

Mutual exclusion groups solve this by partitioning traffic so each user can be in at most one experiment within the group. If experiments A, B, and C are in a mutual exclusion group with equal allocation, each experiment gets one-third of traffic, and no user is in more than one.

Mutual exclusion groups are available on Advanced and Ultimate tiers.

During development and QA, you need to see specific variations without relying on the bucketing algorithm. Forced decisions let you override bucketing for a specific user:

// Force user-123 into variation "redesigned_flow" for the checkout flag
userContext.setForcedDecision(
{ flagKey: 'checkout_redesign', ruleKey: 'experiment_rule' },
{ variationKey: 'redesigned_flow' }
);

Forced decisions override the bucketing algorithm entirely. They are meant for testing and debugging — never use them in production for real traffic.

In Web Experimentation, you can force variations using URL parameters (optimizely_x) or the Optimizely browser extension.

Standard bucketing is deterministic but stateless. If you change an experiment’s traffic allocation or audience conditions, some users may shift to a different variation because the allocation ranges moved.

Sticky bucketing solves this by persisting a user’s variation assignment and honoring it even when the experiment configuration changes. Once a user is assigned to variation A, they stay in variation A regardless of subsequent allocation changes.

Use sticky bucketing when:

  • You plan to ramp traffic allocation gradually (1% to 10% to 50% to 100%).
  • Your experiment involves a multi-step user journey where switching variations mid-journey would be confusing.
  • You need to guarantee that no user ever sees more than one variation.

Avoid sticky bucketing when you intentionally want to re-randomize users, such as short-duration promotional tests where consistency across visits is less important.

When a user sees an unexpected variation, investigate in this order:

  1. User ID stability — Is the user ID the same across sessions? Different IDs produce different hashes.
  2. Audience qualification — Does the user meet the audience conditions? A user outside the audience is excluded before bucketing.
  3. Traffic allocation — Is the user in the allocated percentage? A 50% allocation excludes half of all users.
  4. Forced decisions — Are any overrides active for this user or in the environment?
  5. Mutual exclusion — Is the experiment in a group that has already assigned this user to a different experiment?