User Segments
Target by plan, country, age, or custom properties
Percentage Rollout
Gradual release from 1% to 100%
Consistent Bucketing
Users stay in same group across rollouts
Rule Priority
First matching rule wins
Percentage-Based Rollouts
Release features gradually to a percentage of users. Start with a small group and increase as confidence grows.
// Create a flag with 10% rollout
await platform.flags.create({
key: 'new_dashboard',
name: 'New Dashboard',
enabled: true,
rolloutPercentage: 10, // 10% of users
})
// Monitor metrics, then increase
await platform.flags.update('new_dashboard', {
rolloutPercentage: 25, // Increase to 25%
})
// Continue until full rollout
await platform.flags.update('new_dashboard', {
rolloutPercentage: 50, // Then 50%
})
await platform.flags.update('new_dashboard', {
rolloutPercentage: 100, // Full release
})How Bucketing Works
User Property Targeting
Target users based on their properties like plan, country, email domain, or any custom attribute you track.
// Check flag with user context
const isEnabled = await platform.flags.isEnabled('premium_feature', {
userId: user.id,
context: {
// Standard properties
email: user.email,
plan: user.subscription?.plan,
country: user.country,
// Custom properties
companySize: user.company?.size,
accountAge: daysSinceCreated(user.createdAt),
totalOrders: user.stats.orders,
},
})
// Create a flag targeting pro users
await platform.flags.create({
key: 'advanced_analytics',
name: 'Advanced Analytics',
targeting: {
rules: [
{
name: 'Pro Users',
conditions: [
{ field: 'plan', operator: 'in', value: ['pro', 'enterprise'] }
],
percentage: 100, // 100% of pro users
},
],
defaultPercentage: 0, // Others don't see it
},
})Available Operators
| Operator | Description | Example |
|---|---|---|
| equals | Exact match | plan equals 'pro' |
| notEquals | Does not equal | status notEquals 'banned' |
| in | Value in array | country in ['US', 'CA', 'GB'] |
| notIn | Value not in array | role notIn ['guest', 'trial'] |
| contains | String contains | email contains '@company' |
| startsWith | String prefix | userId startsWith 'test_' |
| endsWith | String suffix | email endsWith '@company.com' |
| matches | Regex match | email matches '^[a-z]+@beta\.' |
| gt / gte | Greater than | age gte 18 |
| lt / lte | Less than | daysActive lt 30 |
| exists | Property exists | subscription exists true |
| semver | Version comparison | appVersion semver '>=2.0.0' |
Segment-Based Targeting
Create reusable segments to target groups of users across multiple flags. Define once, use everywhere.
// Create reusable segments
await platform.segments.create({
key: 'beta_users',
name: 'Beta Program Users',
conditions: [
{ field: 'plan', operator: 'equals', value: 'beta' }
],
})
await platform.segments.create({
key: 'enterprise_accounts',
name: 'Enterprise Accounts',
conditions: [
{ field: 'plan', operator: 'equals', value: 'enterprise' },
{ field: 'companySize', operator: 'gte', value: 100 },
],
})
await platform.segments.create({
key: 'internal_team',
name: 'Internal Team',
conditions: [
{ field: 'email', operator: 'endsWith', value: '@sylphx.dev' }
],
})Segment Best Practices
Consistent Bucketing
Users are assigned to consistent buckets based on a hash of their ID. This ensures stable experiences even as you adjust rollout percentages.
Deterministic Assignment
Same user + same flag = same bucket, every time. No database lookups needed.
Additive Rollout
Increasing from 10% to 20% adds 10% of users without removing the original 10%.
Experiment Isolation
Different flags use different hash seeds, so experiment assignments are independent.
// How bucketing works internally
function getBucket(userId: string, flagKey: string): number {
// Create a deterministic hash from userId + flagKey
const hash = murmurhash3(`${flagKey}:${userId}`)
// Convert to 0-100 range
return hash % 100
}
// User "user_123" with flag "new_feature"
const bucket = getBucket('user_123', 'new_feature') // e.g., 42
// At 10% rollout: bucket 42 > 10, user doesn't see feature
// At 50% rollout: bucket 42 < 50, user now sees feature
// At 25% rollout: bucket 42 > 25, user doesn't see feature
// (going backwards removes users from the feature)
// For predictable rollouts, only increase percentagesRollback Considerations
Combining Rules (AND/OR)
Build complex targeting logic by combining multiple conditions with AND/OR operators.
// All conditions must match (AND)
{
name: 'US Enterprise Users',
conditions: [
// Must match ALL of these
{ field: 'country', operator: 'equals', value: 'US' },
{ field: 'plan', operator: 'equals', value: 'enterprise' },
{ field: 'verified', operator: 'equals', value: true },
],
percentage: 100,
}
// Only users who are:
// - In the US AND
// - On enterprise plan AND
// - Have verified emailPriority and Rule Ordering
Rules are evaluated in order from top to bottom. The first matching rule determines the outcome, so order matters.
// Order matters! Put specific rules first
targeting: {
rules: [
// Most specific first: blocked users never see features
{
name: 'Blocked Users',
conditions: [{ field: 'status', operator: 'equals', value: 'blocked' }],
percentage: 0, // Always disabled
},
// Internal team always gets features
{
name: 'Internal Team',
conditions: [{ field: 'email', operator: 'endsWith', value: '@company.com' }],
percentage: 100,
},
// Enterprise gets priority access
{
name: 'Enterprise',
conditions: [{ field: 'plan', operator: 'equals', value: 'enterprise' }],
percentage: 100,
},
// Pro users get gradual rollout
{
name: 'Pro Users',
conditions: [{ field: 'plan', operator: 'equals', value: 'pro' }],
percentage: 50,
},
// Free users get slower rollout
{
name: 'Free Users',
conditions: [{ field: 'plan', operator: 'equals', value: 'free' }],
percentage: 10,
},
],
// Catch-all for any unmatched users
defaultPercentage: 0,
}Rule Ordering Best Practices
- Put deny rules (blocked users) at the top
- Put internal/testing rules next for easy overrides
- Order by priority: enterprise, pro, free
- Use default percentage as a catch-all
- Keep rules to 5-7 maximum for maintainability
API Reference
| Method | Description |
|---|---|
| segments.create(config) | Create a reusable user segment |
| segments.update(key, config) | Update segment conditions |
| segments.delete(key) | Delete a segment |
| segments.evaluate(key, ctx) | Check if user matches segment |
| flags.targeting.addRule() | Add a rule to flag targeting |
| flags.targeting.reorder() | Change rule priority order |