Skip to content

Show a variant only to some visitors

A condition says where a variant may show: only on mobile, never to search traffic, only to one campaign’s visitors. This guide declares conditions in code and with the command, adds dimensions and audiences of your own, and checks that each variant can still reach visitors.

A condition holds once every deployment of your site runs routingcat 1.1.0 or later. Until then, the variant it applies to is held back and shown to no visitor: see Before every deployment upgrades.

A rule or an expectation

Decide which you mean first:

  • A rule must hold: the 50%-off hero only for the campaign whose ad promises 50% off; the sticky bar never on desktop, where it breaks; a claim never shown to search traffic. A rule is a condition.
  • An expectation is a belief, such as “this headline is for mobile visitors”. As a condition, it would stop RoutingCat from ever learning whether it is true, so declare it as an expectation: routing starts from your belief, and the results correct it.

Declare a condition

In routingcat.config.json, a variant’s entry under variants takes only, where it may show, and never, where it may not, each a condition over dimensions and their levels, as "home/hero:sticky": { "only": { "device": "mobile" } }. A slot’s entry under slots takes only too, where the slot varies at all.

The dimensions of one object must all hold, a list of levels holds for any of them, and any, all and not combine conditions, at most 4 deep. Config and declarations gives every form.

How a condition behaves:

  • A variant shows where its only holds and its never doesn’t, within its slot’s only. A visitor it excludes is routed among the slot’s other variants, as if it were paused for them, in the random slice and a fixed share alike.
  • The default takes no condition, nor an expectation: it shows wherever no other variant may, and to the control, so plan refuses one. Outside a slot’s only, everyone sees its default.
  • The visit keeps its variants, since a condition is checked at the visitor’s first view of the page in the visit: one on time_of_day never switches a page mid-visit, and a condition you add reaches the visits that start after it.
  • The measured gain includes your conditions, since the control still sees the defaults. A variant’s predictions are over the visits it may show to, and compared with the default’s on those same visits.
  • On a slot with a fixed share, as a price test has, plan says that if its variants change a price, visitors see different prices by who they are: Routing settings says what RoutingCat recommends for prices.

npx routingcat plan refuses a dimension, a level or an audience your site doesn’t have, naming those it has, and prints each variant’s conditions under its line.

A production deploy makes them live within about 2 minutes.

Set a condition with the command

npx routingcat set changes a live variant’s conditions without a deploy, within about 2 minutes: --only and --never each take a condition, and none removes one. Run it with --dry-run first, as any act:

$ routingcat set home/hero:save-time --only device=mobile --never '{"any":[{"in":{"traffic_source":["search"]}},{"in":{"visit":["repeat"]}}]}' --expectation device=mobile
set · done · op_01m4zbfqr0fa49v710xk3sc2w2 · registry version 20
home/hero:save-time only: none → device=mobile · never: none → '{"any":[{"in":{"traffic_source":["search"]}},{"in":{"visit":["repeat"]}}]}' · expect: none → device=mobile
change ch_01m4zbfqr0fwqttp9y5atppz0f
publications pb_01m4zbfqr0fzbb7a7j23jr4e9q
scorecard ev_01kx68ntc0fgnbx7xqtn0wa7xx: support changed
! home/hero:save-time: its conditions hold once your deployments run routingcat 1.1.0 or later. Until then, RoutingCat holds it back. (D147)

The variant’s scorecards go on, each marked support changed: from now on they compare it on the visitors it may be shown to.

The command writes one dimension as device=mobile, or device=mobile,desktop for any of those levels, several dimensions in one quoted value, as 'device=mobile ad_network=meta', and any other condition as its JSON, in single quotes, as the command reference gives it.

A visitor already shown the variant keeps it for the rest of their visit, 7 days by default. Where it must stop at once, as for a variant broken on desktop, pause it as you set the condition, and resume it once the visits under way have ended: a variant resumed sooner returns to the visitors who had it.

A production deploy that changes the variant’s only or never in routingcat.config.json replaces the act, so write the same condition there: status and show list where they differ. A slot’s own only changes in the config alone.

The dimensions

npx routingcat dimensions lists the dimensions a condition can name, RoutingCat’s own first, then those you declare, with their levels:

$ routingcat dimensions
pantry · dimensions
device built in mobile, desktop, other
traffic_source built in direct, search, social, paid, email, referral, ai, other
visit built in first, repeat
time_of_day built in morning, lunch, afternoon, evening, night, unknown
day_type built in weekday, weekend
ad_network built in meta, google, microsoft, tiktok, linkedin, pinterest, other, none
campaign from utm_campaign spring, brand, other, none

What each of RoutingCat’s own levels means is in Config and declarations.

Meta adds its click identifier to unpaid clicks too, so a Meta visit counts as meta only from a link tagged paid: give your Meta ads’ links a source and a paid medium, as utm_source=facebook&utm_medium=paid_social.

A condition can name any level, other, unknown and none included, and excludes those it doesn’t name: mobile in only leaves tablets out, where desktop in never keeps them, but for an iPad whose browser presents itself as a Mac, which reads desktop.

Declare your own dimensions

Your own dimensions group your campaigns, ads and sources into levels. A site declares at most 8, each keyed in lowercase letters, digits and _, starting with a letter, and none of RoutingCat’s own keys.

routingcat.config.json takes dimensions, each read from one field of the visit’s arrival, with its levels, as "campaign": { "from": "utm_campaign", "levels": { "spring": "spring_*", "brand": ["brand", "brand_search"] } }.

  • The fields: the six UTM tags, utm_source to utm_id; the referrer’s host, referrer; the landing path, landing; the click identifiers’ names, click_id; and the visitor’s country, country.
  • A level takes one value or a list, matched whatever their case. A value with * matches any run of characters, so a naming convention such as spring_* needs one value. A click identifier is named exactly, as gclid.
  • The first level that matches wins, in the order you declare them. A visit whose field matches none reads other, and one without the field none.
  • A map replaces levels for long lists, such as ad ids from your ads manager: map names a CSV file of value,level lines, with no header, in your config’s folder, which RoutingCat receives with your declarations.
  • Limits: at most 62 levels and 50,000 values a dimension, and a size for your site’s dimensions together, which plan checks, naming the dimension past it; Limits and defaults gives each. A pattern keeps a dimension small where a list of every value would not.
  • What RoutingCat keeps of a field: a tag’s first 64 characters, and the referrer’s host’s or the landing path’s first 100, so plan refuses a longer value, which could never match. To target one campaign or ad whose name passes 64 characters, match the start of its name with a pattern, or tag it by its ID, for example utm_id={{campaign.id}}.

show --by and download --by break your results down by your dimensions too.

Name an audience

An audience is a condition you name once and reuse, by its key, wherever a condition goes.

routingcat.config.json takes audiences, as "meta_mobile": { "device": "mobile", "ad_network": "meta" }, which a variant then names as "only": "meta_mobile".

An audience names dimensions, never another audience, and a site has at most 32. npx routingcat audiences lists them, written as set takes them, which names one in a condition’s JSON, as --only '{"audience":"meta_mobile"}':

$ routingcat audiences
pantry · audiences
meta_mobile 'device=mobile ad_network=meta'
paid_or_mobile '{"any":[{"in":{"traffic_source":["paid"]}},{"in":{"device":["mobile"]}}]}'

Check that a variant can reach visitors

A condition can exclude everyone, through a typo in a campaign’s tag or a campaign that ended:

  • plan refuses a condition that no visitor could ever match, such as an only its own never cancels.
  • status warns when no visit of the last 28 days matches where a live variant may show. A condition for a campaign that hasn’t started matches once its visits arrive.
  • In --json, each variant’s scores carry its coverage: the share of the page’s visits where it may show.

A variant confined to one campaign outlives the campaign: retire it when the campaign ends, to free its place. status and show print each variant’s conditions under it, and a slot’s own on its line:

hero default plan-week · 4 live · 1 retired · only visit=first · personalize by ad_network
current learning 3.5% [3.0, 4.1] (stale) · −20% [−34, −5] (stale) vs default · signal 69 Strong · served 14% · 3,099 visits
save-time learning 3.8% [3.0, 4.5] (stale) · −14% [−32, +4] (stale) vs default · signal 59 Building · served 11% · 2,565 visits
only device=mobile · never '{"any":[{"in":{"traffic_source":["search"]}},{"in":{"visit":["repeat"]}}]}' · expect device=mobile · held back until your deployments run a routingcat that honors conditions

Expect a variant to do better for some visitors

An expectation is where you believe a variant does better than elsewhere. From its first visitors, routing shows it more there and less elsewhere, and the evidence corrects the belief, whichever way it goes: a right expectation saves some early exploring, and a wrong one is overturned as conversions come in.

A variant’s entry under variants takes expect, as "expect": { "device": "mobile" }, or an audience’s key.

An expectation is one place: dimensions with their levels, never any, all or not, which belong in a rule. It names dimensions its slot learns by: a slot’s personalization already covers RoutingCat’s own dimensions but ad_network, and its personalizeBy adds ad_network or one of yours, as Personalize by your own dimension shows. set --expectation sets it with the command, and none removes it.

Before every deployment upgrades

Conditions need routingcat 1.1.0 or later in the code that decides each visit. Until every deployment of your site that built, started or recorded visits in the last 72 hours runs it, previews included:

  • A variant with a condition is held back: kept, and shown to no visitor, so a rule never shows outside its condition. plan and the deploy name each one, status and show read held back until your deployments run a routingcat that honors conditions, and its slot routes among its other variants.
  • A slot’s only holds back each of its variants but the default, which everyone then sees.
  • Personalizing by a dimension waits, and the slot routes as before.

Once the last older deployment stops counting, what waited goes live with routing’s next update: within a day, or within about 2 minutes of your next deploy or act. Upgrade to 1.1 gives the steps.

Next

RoutingCat routes each visitor of your site to the variants that convert.