Flags guides
01 · Percentage rollouts

Roll out gradually.

Staff first, then 10%, then everyone, with every user keeping their answer as the percentage grows.

You need a boolean flag that your code already reads (see getting started) and users with an identifier.


1

Staff first

In Production, open the flag and add a rule: email ends_with_any @acme.com, serve on. Everyone else still gets the plain value (off). Rules come before the split, so staff stay in whatever percentage you choose next.


2

Then 10% of everyone else

Under To everyone else, switch to a percentage split: 10% on, 90% off. Percentages are whole numbers that add up to 100. From code it is one replace of the targeting, carrying the version you read (a stale one answers 409 VERSION_CONFLICT instead of overwriting a colleague):

rollout.ts
import { Flags } from "@productcraft/flags";
const flags = new Flags(); // PRODUCTCRAFT_API_KEY + PRODUCTCRAFT_WORKSPACE_ID

declare const settingId: string;
declare const productionId: string;

const current = await flags.settings.values.retrieve(settingId, productionId);
await flags.settings.values.replace(settingId, productionId, {
  version: current.version,
  value: false,
  rules: [
    // Staff first, whatever their bucket.
    {
      conditions: [{ user: { attribute: "email", comparator: "ends_with_any", values: ["@acme.com"] } }],
      serve: true,
    },
  ],
  // Then 10% of everyone else.
  percentages: [
    { percentage: 10, value: true },
    { percentage: 90, value: false },
  ],
  note: "Roll new_checkout out to 10%",
});

3

How users are bucketed

The SDK hashes the flag's seed (its key at creation) with the user's identifier into a bucket from 0 to 99, and serves the first option whose cumulative percentage is above the bucket. That gives three guarantees:

  • Sticky: the same user gets the same answer on every server, in every process, after every restart, with no state stored anywhere.
  • Monotonic: going from 10% to 25% only adds users. Nobody who had the feature loses it.
  • Independent per flag: each flag has its own seed, so the 10% of one flag is not the 10% of another.
read.ts
import { FlagsClient } from "@productcraft/flags";
const flags = new FlagsClient({ sdkKey: process.env.PRODUCTCRAFT_FLAGS_SDK_KEY });

// The same identifier always lands in the same bucket, on every
// machine and after every restart.
const d = await flags.getValueDetails("new_checkout", false, { identifier: "user-123" });
console.log(d.value, d.reason, d.variant); // e.g. true "SPLIT" "pct:0"

4

Split by account, not by user

For a B2B product you usually want a whole team to switch at once. Set the split attribute (percentage_attribute) to, say, account_id, and pass it in custom. Every user of an account then lands in the same bucket.


5

Anonymous users

A user without the split attribute (no identifier, or an empty one) skips the split and gets the value after it, here off. They never pile into one bucket. If logged-out visitors should be in the rollout too, give them a stable anonymous id (a cookie) as their identifier.


6

Going to 100%

When the feature is done, set the value to on and remove the split and the staff rule, so the flag serves one plain value. Then remove it from your code: Flag cleanup.