Percentage Rollouts
Percentage rollouts let you release features to a subset of users and gradually increase the rollout as you gain confidence.
How It Works
- Create a flag with a percentage (e.g., 25%)
- Use
isEnabledForUser()with a user identifier - The SDK uses consistent hashing to determine if the user is in the rollout
- The same user always gets the same result for the same flag
Basic Usage
import { Featsync } from '@featsync/sdk';
const featsync = new Featsync({ apiKey: 'fs_your_api_key',});
// Get the current user's IDconst userId = getCurrentUserId();
// Check if this user is in the rolloutif (await featsync.isEnabledForUser('new-checkout', userId)) { showNewCheckout(); // ~25% of users see this} else { showOldCheckout(); // ~75% of users see this}Consistent Hashing
The SDK uses consistent hashing to ensure:
- Same user, same result - User “alice” will always get the same value for flag “new-checkout”
- Different flags, different buckets - User “alice” might be in rollout for “new-checkout” but not for “new-pricing”
- No flip-flopping - Users don’t see features appear and disappear randomly
// Alice will always get the same result for this flagconst aliceResult = await featsync.isEnabledForUser('new-checkout', 'alice');
// Bob might get a different resultconst bobResult = await featsync.isEnabledForUser('new-checkout', 'bob');
// Alice's result for a different flag is independentconst aliceOther = await featsync.isEnabledForUser('new-pricing', 'alice');User Identifiers
The user identifier should be:
- Unique - Different users should have different IDs
- Stable - The same user should always have the same ID
- Non-PII - Don’t use email addresses directly
Good Examples
// Database IDawait featsync.isEnabledForUser('feature', 'user_12345');
// UUIDawait featsync.isEnabledForUser('feature', 'a1b2c3d4-e5f6-...');
// Hashed emailimport { createHash } from 'crypto';const hashedEmail = createHash('sha256').update(email).digest('hex');await featsync.isEnabledForUser('feature', hashedEmail);Bad Examples
// Don't use raw emailsawait featsync.isEnabledForUser('feature', 'alice@example.com'); // Bad!
// Don't use session IDs (not stable)await featsync.isEnabledForUser('feature', sessionId); // Bad!Rollout Workflow
Here’s a typical workflow for rolling out a feature:
1. Start Small (10%)
// In the dashboard, set percentage to 10%// ~10% of users will see the featureif (await featsync.isEnabledForUser('new-feature', userId)) { showNewFeature();}2. Monitor Metrics
Watch your error rates, user feedback, and key metrics. If everything looks good, continue.
3. Increase Gradually
In the dashboard, increase the percentage:
- 10% → 25%
- 25% → 50%
- 50% → 100%
4. Full Rollout
Once at 100%, you can:
- Keep the flag for a quick kill switch
- Or remove the flag check from your code
Anonymous Users
For users without accounts, you can use any stable identifier:
// Use a cookie-based IDconst anonymousId = getOrCreateAnonymousId();await featsync.isEnabledForUser('feature', anonymousId);function getOrCreateAnonymousId() { let id = localStorage.getItem('anonymous_id'); if (!id) { id = crypto.randomUUID(); localStorage.setItem('anonymous_id', id); } return id;}Boolean Flags with isEnabledForUser
You can use isEnabledForUser() with boolean flags too. It behaves the same as isEnabled():
// For a boolean flag (100% or 0%)// Both return the same result:await featsync.isEnabled('my-flag');await featsync.isEnabledForUser('my-flag', userId);Getting Percentage Info
To see the rollout percentage in your code:
const flag = await featsync.getFlag('gradual-feature');
if (flag.percentage !== null) { console.log(`Rollout at ${flag.percentage}%`);}Testing Percentage Rollouts
To test your rollout logic, try different user IDs:
// See which test users are in the rolloutfor (const testUserId of ['user_1', 'user_2', 'user_3', 'user_4', 'user_5']) { const enabled = await featsync.isEnabledForUser('gradual-feature', testUserId); console.log(`${testUserId}: ${enabled ? 'IN' : 'OUT'}`);}Next Steps
- Integrate with React using the
useFlagForUserhook - Learn about environments for staging tests
- View the API reference for all methods