Skip to content

Per-key randomness

import { rollout, bucket, hashPick, hashFloat, hashRandom } from "ransu/hash";

Sometimes you do not want a stream — you want the same answer for the same key, on every machine, forever, with nothing to coordinate.

rollout(userId, 0.1); // in the 10% rollout?
bucket(userId, 16); // a stable shard
hashPick(userId, ["control", "blue", "green"]); // a stable variant
hashFloat(userId); // a stable [0, 1)

A seeded generator gives you a sequence. Two processes that draw a different number of values before reaching the same user get different answers, and any restart or reorder shifts everyone. Deriving from the key sidesteps all of it: there is no position to keep in sync.

rollout(key, p) is hashFloat(key) < p, so raising p can only ever bring more keys in. Nobody gets taken back out when you widen an experiment — a property CI checks explicitly.

rollout("user-7", 0.1); // false
rollout("user-7", 0.5); // may become true, never the reverse

Without a salt, every feature flag would bucket users identically and the same unlucky 10% would get every experiment:

rollout(userId, 0.1, "new-checkout");
rollout(userId, 0.1, "new-search"); // an unrelated 10%

When one value is not enough — generating a consistent set of fixtures per user, for example:

const r = hashRandom(userId);
r.integer(1, 100);
r.pick(themes);