Skip to main content
A layer is a lane for your traffic. Inside a layer, a user is in at most one test at a time. Across layers, tests overlap freely and independently, so every layer sees your full population. That is the whole idea. Everything else on this page follows from one rule: a parameter belongs to exactly one layer.

What layers are for

Once you run more than a couple of tests at the same time, two things go wrong:
  • Tests collide. Two tests on the same page, on the same users, with the same goal. Nobody can say which one moved the number, and in the worst case the two variants together produce a broken page.
  • Tests wait. To avoid collisions, teams start scheduling tests one after another, and the checkout team ends up waiting for the search team to finish.
Layers give you both isolation and concurrency, per product area, decided once: Because your code reads parameters and never names an experiment, a layer can retire one test and start the next without a deploy. Web, mobile and backend all pick up the new policy on their next config refresh.

Which layer does a test belong in?

Ask one question per product area: may two tests here run on the same user at the same time? Two shapes to avoid:
  • One giant layer. If every parameter lands in the base layer, every change in the project waits on every other change.
  • One layer per experiment. You lose the collision guarantee on exactly the surfaces where it matters, and the layer list becomes noise.
The cost of sharing a layer, stated plainly: tests in the same layer share its users.

Setting up layers: one per surface

The recommended shape is one layer per surface. A surface is a product area such as PDP, Cart, Checkout or Onboarding. When you create a surface in the dashboard, choose Create a new layer and the surface gets its own lane, named after it. Parameters bound to that surface land in that layer.
1

Name your surfaces

Six is plenty to start. A surface is a product area with one owning team and one primary goal. For a shop: Sitewide, PDP, Cart, Shipping & Offers, Post-purchase.
2

Create each surface with its own layer

In Surfaces → New surface, keep Create a new layer selected. Pick the randomization unit as the surface’s default entity if it differs from your project default.
3

Put parameters in the layer of the surface that consumes them

Platform values nobody tests, such as kill switches and timeouts, stay in the base layer.
4

Split only when a layer is congested

When two teams keep waiting on each other in one layer, split it along the team boundary, between tests.
Every project has a base layer that’s created automatically and can’t be deleted. It holds stable parameters shared across the project. If you don’t set up surface layers, all parameters end up there and your tests will queue behind each other. A feature flag gets its own layer, so flags never queue behind each other or behind the base layer.
A backend service and the frontend that renders its output can share a layer only if both randomize on the same unit and the backend receives the same unit key. If different teams own them, give each its own layer with the same unit.

How assignment works

A user resolves once per layer. Each layer computes the user’s bucket independently:

Orthogonal bucketing

Because the layer ID is mixed into the hash, a user’s bucket in Layer A carries no information about their bucket in Layer B. The two assignments are statistically independent. This is what makes concurrent experimentation safe. bucketCount is a project-level setting (default 1000). A higher count gives finer-grained allocation ranges and smoother rollouts. User user_789 in the example above gets:
  • Checkout: hash("user_789" + "checkout") % 1000 = 342 → control (range 0–499)
  • Pricing: hash("user_789" + "pricing") % 1000 = 817 → high (range 666–999)
Measuring the effect of the colour change isn’t confounded by the pricing change.

Bucket ranges

Each layer has bucketCount buckets. The policies within a layer divide that space into non-overlapping allocation ranges:
For static A/B tests the ranges are fixed. For adaptive policies the optimizer adjusts the ranges over time, shifting traffic toward better-performing allocations. For rollouts the ranges expand in increments during forward ramping, and can shrink again on rollback or a manual percentage decrease. Advancing a change from canary to experiment to rollout reuses the same slice of the layer, so nobody is re-randomized mid-flight.

Eligible bucket ranges

A policy can optionally narrow itself to a sub-range of the layer (an eligible bucket range). This is how you fit several non-overlapping policies inside a single layer:
Users outside a policy’s eligible range skip it entirely.

Moving a parameter between layers

A parameter belongs to exactly one layer. You can move it in the dashboard, but every move re-randomizes that parameter’s users, because the bucket depends on the layer. Move parameters between tests, never while a policy overrides them.

Next steps

Surfaces

Product areas, and how each one gets its own layer.

Policies

How allocations and targeting work inside a layer.

Changes

The lifecycle from canary to rollout inside one layer.

A/B testing

Run your first A/B test.