Docs · Targeting

How targeting works

A flag has a default variation that everyone gets unless a rule says otherwise. Rules are ordered: the first one that matches wins. Within a rule, every condition must match (AND). An optional rollout % further narrows the match to a deterministic per-user bucket.

Evaluation order

for rule in flag.rules:                          # top to bottom
    if every condition matches the user:         # AND
        if rule.rollout is set:
            bucket = hash(user.id, flag.key) % 100
            if bucket >= rule.rollout.percent:
                continue                         # rolled out of this rule
        return rule.serveVariation               # first match wins
return flag.defaultVariation                     # nothing matched

Worked example: name@example.com always on, 50% of everyone else

You want to ship a new checkout flow. Your tester is name@example.com — they should always see the new flow. Everyone else gets a 50% rollout so you can compare conversion.

Flag config

{
  "key": "new-checkout",
  "description": "Faster Stripe checkout flow",
  "variations": [
    { "key": "on",  "value": true },
    { "key": "off", "value": false }
  ],
  "defaultVariation": "off",
  "rules": [
    {
      // Rule 1: targeted user — always on
      "conditions": [
        { "attribute": "email", "op": "equals", "value": "name@example.com" }
      ],
      "serveVariation": "on"
    },
    {
      // Rule 2: 50% of everyone else
      "rollout": { "percent": 50 },
      "serveVariation": "on"
    }
  ]
}

How it evaluates

For name@example.com: Rule 1’s condition matches → serve true. Rule 2 is never checked. For any other user: Rule 1 misses. Rule 2 has no conditions and a 50% rollout → ~half the users are bucketed in and get true; the rest fall through to the default (false). Bucketing is deterministic per user, so the same user always sees the same outcome until you change the rollout %.

UserRule 1 (email)Rule 2 (50% bucket)Result
name@example.com✓ matchtrue
alice@acme.iomissbucket 23 < 50true
bob@acme.iomissbucket 78 ≥ 50false
carol@acme.iomissbucket 23 < 50true

Try it

Sign up for the 15-day trial and create a flag with these two rules. Then call client.isEnabled('new-checkout', { id: user.id, attributes: { email: user.email } }) — your tester will land on true, everyone else on a stable 50/50 split.

import nightroll from '@nightroll/js';

const client = nightroll.init('clt_live_…');
await client.ready();

const user = {
  id: currentUser.id,
  attributes: { email: currentUser.email }
};

if (client.isEnabled('new-checkout', user)) {
  showNewCheckout();
} else {
  showOldCheckout();
}

Caveats

← Back to the dashboard