xiaohongshu
xiaohongshu
feature-flag-system
Implement feature flags (toggles) for controlled feature rollouts, A/B testing, canary deployments, and kill switches. Use when deploying new features gradually, testing in production, or managing feature lifecycles.
Full skill instructions
Implement feature flags to decouple deployment from release, enable gradual rollouts, A/B testing, and provide emergency kill switches.
interface FlagConfig {
key: string;
enabled: boolean;
description: string;
rules?: FlagRule[];
variants?: FlagVariant[];
createdAt: Date;
updatedAt: Date;
}
interface FlagRule {
type: 'user' | 'percentage' | 'attribute' | 'datetime';
operator: 'in' | 'equals' | 'contains' | 'gt' | 'lt' | 'between';
attribute?: string;
values: any[];
}
interface FlagVariant {
key: string;
weight: number;
value: any;
}
interface EvaluationContext {
userId?: string;
email?: string;
attributes?: Record<string, any>;
timestamp?: number;
}
class FeatureFlagService {
private flags: Map<string, FlagConfig> = new Map();
constructor() {
this.loadFlags();
}
private loadFlags(): void {
// Load from database or config
this.flags.set('new-dashboard', {
key: 'new-dashboard',
enabled: true,
description: 'New dashboard UI',
rules: [
{
type: 'percentage',
operator: 'lt',
values: [25] // 25% rollout
}
],
createdAt: new Date(),
updatedAt: new Date()
});
this.flags.set('premium-features', {
key: 'premium-features',
enabled: true,
description: 'Premium features for paid users',
rules: [
{
type: 'attribute',
operator: 'equals',
attribute: 'plan',
values: ['premium', 'enterprise']
}
],
createdAt: new Date(),
updatedAt: new Date()
});
this.flags.set('beta-feature', {
key: 'beta-feature',
enabled: true,
description: 'Beta feature',
rules: [
{
type: 'user',
operator: 'in',
values: ['user1', 'user2', 'user3']
}
],
createdAt: new Date(),
updatedAt: new Date()
});
}
isEnabled(flagKey: string, context: EvaluationContext = {}): boolean {
const flag = this.flags.get(flagKey);
if (!flag) {
console.warn(`Flag not found: ${flagKey}`);
return false;
}
if (!flag.enabled) {
return false;
}
if (!flag.rules || flag.rules.length === 0) {
return true;
}
return this.evaluateRules(flag.rules, context);
}
getVariant(flagKey: string, context: EvaluationContext = {}): any {
const flag = this.flags.get(flagKey);
if (!flag || !this.isEnabled(flagKey, context)) {
return null;
}
if (!flag.variants || flag.variants.length === 0) {
return true;
}
return this.selectVariant(flag.variants, context);
}
private evaluateRules(rules: FlagRule[], context: EvaluationContext): boolean {
return rules.every(rule => this.evaluateRule(rule, context));
}
private evaluateRule(rule: FlagRule, context: EvaluationContext): boolean {
switch (rule.type) {
case 'user':
return this.evaluateUserRule(rule, context);
case 'percentage':
return this.evaluatePercentageRule(rule, context);
case 'attribute':
return this.evaluateAttributeRule(rule, context);
case 'datetime':
return this.evaluateDateTimeRule(rule, context);
default:
return false;
}
}
private evaluateUserRule(rule: FlagRule, context: EvaluationContext): boolean {
if (!context.userId) return false;
return rule.values.includes(context.userId);
}
private evaluatePercentageRule(rule: FlagRule, context: EvaluationContext): boolean {
const hash = this.hashContext(context);
const percentage = (hash % 100) + 1;
return percentage <= rule.values[0];
}
private evaluateAttributeRule(rule: FlagRule, context: EvaluationContext): boolean {
if (!rule.attribute || !context.attributes) return false;
const value = context.attributes[rule.attribute];
switch (rule.operator) {
case 'equals':
return rule.values.includes(value);
case 'contains':
return rule.values.some(v => String(value).includes(v));
case 'gt':
return value > rule.values[0];
case 'lt':
return value < rule.values[0];
default:
return false;
}
}
private evaluateDateTimeRule(rule: FlagRule, context: EvaluationContext): boolean {
const now = context.timestamp || Date.now();
if (rule.operator === 'between') {
return now >= rule.values[0] && now <= rule.values[1];
}
return false;
}
private selectVariant(variants: FlagVariant[], context: EvaluationContext): any {
const hash = this.hashContext(context);
const totalWeight = variants.reduce((sum, v) => sum + v.weight, 0);
const position = hash % totalWeight;
let cumulative = 0;
for (const variant of variants) {
cumulative += variant.weight;
if (position < cumulative) {
return variant.value;
}
}
return variants[0].value;
}
private hashContext(context: EvaluationContext): number {
const str = context.userId || context.email || 'anonymous';
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return Math.abs(hash);
}
async createFlag(config: Omit<FlagConfig, 'createdAt' | 'updatedAt'>): Promise<void> {
this.flags.set(config.key, {
...config,
createdAt: new Date(),
updatedAt: new Date()
});
}
async updateFlag(key: string, updates: Partial<FlagConfig>): Promise<void> {
const flag = this.flags.get(key);
if (!flag) {
throw new Error(`Flag not found: ${key}`);
}
this.flags.set(key, {
...flag,
...updates,
updatedAt: new Date()
});
}
async deleteFlag(key: string): Promise<void> {
this.flags.delete(key);
}
getAllFlags(): FlagConfig[] {
return Array.from(this.flags.values());
}
}
// Usage
const featureFlags = new FeatureFlagService();
// Simple boolean check
if (featureFlags.isEnabled('new-dashboard', { userId: 'user123' })) {
console.log('Show new dashboard');
}
// With user attributes
const hasPremiumFeatures = featureFlags.isEnabled('premium-features', {
userId: 'user123',
attributes: { plan: 'premium' }
});
// Get variant for A/B testing
const buttonColor = featureFlags.getVariant('button-color-test', {
userId: 'user123'
});
import { createContext, useContext, ReactNode } from 'react';
interface FeatureFlagContextType {
isEnabled: (key: string) => boolean;
getVariant: (key: string) => any;
}
const FeatureFlagContext = createContext<FeatureFlagContextType | null>(null);
export function FeatureFlagProvider({
children,
userId,
attributes
}: {
children: ReactNode;
userId?: string;
attributes?: Record<string, any>;
}) {
const flagService = new FeatureFlagService();
const context: FeatureFlagContextType = {
isEnabled: (key: string) => {
return flagService.isEnabled(key, { userId, attributes });
},
getVariant: (key: string) => {
return flagService.getVariant(key, { userId, attributes });
}
};
return (
<FeatureFlagContext.Provider value={context}>
{children}
</FeatureFlagContext.Provider>
);
}
export function useFeatureFlag(key: string): boolean {
const context = useContext(FeatureFlagContext);
if (!context) {
throw new Error('useFeatureFlag must be used within FeatureFlagProvider');
}
return context.isEnabled(key);
}
export function useFeatureVariant(key: string): any {
const context = useContext(FeatureFlagContext);
if (!context) {
throw new Error('useFeatureVariant must be used within FeatureFlagProvider');
}
return context.getVariant(key);
}
// Feature component wrapper
export function Feature({
flag,
fallback = null,
children
}: {
flag: string;
fallback?: ReactNode;
children: ReactNode;
}) {
const isEnabled = useFeatureFlag(flag);
return isEnabled ? <>{children}</> : <>{fallback}</>;
}
// Usage in components
function Dashboard() {
const hasNewDashboard = useFeatureFlag('new-dashboard');
const theme = useFeatureVariant('theme-experiment');
return (
<div>
{hasNewDashboard ? <NewDashboard /> : <OldDashboard />}
<Feature flag="premium-features" fallback={<UpgradePrompt />}>
<PremiumContent />
</Feature>
<div style={{ backgroundColor: theme?.backgroundColor }}>
Content with experiment theme
</div>
</div>
);
}
interface FlagEvaluationEvent {
flagKey: string;
userId?: string;
result: boolean;
variant?: any;
timestamp: number;
duration: number;
}
class FeatureFlagServiceWithAnalytics extends FeatureFlagService {
private analytics: Analytics;
constructor(analytics: Analytics) {
super();
this.analytics = analytics;
}
isEnabled(flagKey: string, context: EvaluationContext = {}): boolean {
const startTime = Date.now();
const result = super.isEnabled(flagKey, context);
const duration = Date.now() - startTime;
this.trackEvaluation({
flagKey,
userId: context.userId,
result,
timestamp: Date.now(),
duration
});
return result;
}
getVariant(flagKey: string, context: EvaluationContext = {}): any {
const startTime = Date.now();
const variant = super.getVariant(flagKey, context);
const duration = Date.now() - startTime;
this.trackEvaluation({
flagKey,
userId: context.userId,
result: variant !== null,
variant,
timestamp: Date.now(),
duration
});
return variant;
}
private trackEvaluation(event: FlagEvaluationEvent): void {
this.analytics.track('feature_flag_evaluated', {
flag_key: event.flagKey,
user_id: event.userId,
result: event.result,
variant: event.variant,
duration_ms: event.duration
});
}
async getAnalytics(flagKey: string, timeRange: { start: Date; end: Date }): Promise<{
evaluations: number;
uniqueUsers: number;
enabledRate: number;
variantDistribution: Record<string, number>;
}> {
return this.analytics.getFlagAnalytics(flagKey, timeRange);
}
}
from typing import Dict, Any, Optional
import hashlib
import json
class FeatureFlagClient:
def __init__(self, sdk_key: str, config: Optional[Dict] = None):
self.sdk_key = sdk_key
self.config = config or {}
self.flags: Dict[str, Dict] = {}
self.initialize()
def initialize(self):
"""Load flags from API or cache."""
# In production, fetch from API
self.flags = {
'new-feature': {
'enabled': True,
'rollout': {
'percentage': 50
}
},
'premium-feature': {
'enabled': True,
'targeting': {
'attribute': 'plan',
'values': ['premium', 'enterprise']
}
}
}
def variation(
self,
flag_key: str,
user: Dict[str, Any],
default: bool = False
) -> bool:
"""Evaluate flag for user."""
flag = self.flags.get(flag_key)
if not flag or not flag.get('enabled'):
return default
# Check targeting rules
if 'targeting' in flag:
if not self._evaluate_targeting(flag['targeting'], user):
return False
# Check percentage rollout
if 'rollout' in flag:
return self._evaluate_rollout(flag['rollout'], user, flag_key)
return True
def variation_detail(
self,
flag_key: str,
user: Dict[str, Any],
default: Any = None
) -> Dict[str, Any]:
"""Get flag variation with details."""
value = self.variation(flag_key, user, default)
return {
'value': value,
'variation_index': 0 if value else 1,
'reason': {
'kind': 'RULE_MATCH' if value else 'OFF'
}
}
def _evaluate_targeting(self, targeting: Dict, user: Dict) -> bool:
"""Evaluate targeting rules."""
attribute = targeting.get('attribute')
values = targeting.get('values', [])
user_value = user.get(attribute)
return user_value in values
def _evaluate_rollout(
self,
rollout: Dict,
user: Dict,
flag_key: str
) -> bool:
"""Evaluate percentage rollout."""
percentage = rollout.get('percentage', 0)
user_id = user.get('id', user.get('email', 'anonymous'))
# Consistent hashing for stable rollout
hash_value = self._hash_user(user_id, flag_key)
bucket = hash_value % 100
return bucket < percentage
def _hash_user(self, user_id: str, flag_key: str) -> int:
"""Hash user ID for consistent bucketing."""
combined = f"{flag_key}:{user_id}"
hash_bytes = hashlib.sha256(combined.encode()).digest()
return int.from_bytes(hash_bytes[:4], byteorder='big')
def track(self, event_name: str, user: Dict, data: Optional[Dict] = None):
"""Track custom event."""
# Send to analytics
pass
def identify(self, user: Dict):
"""Identify user."""
# Update user context
pass
def flush(self):
"""Flush events."""
pass
def close(self):
"""Close client."""
pass
# Usage
client = FeatureFlagClient(sdk_key='your-sdk-key')
user = {
'id': 'user-123',
'email': '[email protected]',
'plan': 'premium'
}
# Check if feature is enabled
if client.variation('new-feature', user):
print("New feature enabled!")
# Get detailed information
detail = client.variation_detail('premium-feature', user)
print(f"Value: {detail['value']}, Reason: {detail['reason']}")
# Track event
client.track('feature-used', user, {'feature': 'new-feature'})
interface FlagFormData {
key: string;
description: string;
enabled: boolean;
rolloutPercentage?: number;
targetUsers?: string[];
targetAttributes?: Record<string, any>;
}
function FeatureFlagDashboard() {
const [flags, setFlags] = useState<FlagConfig[]>([]);
const flagService = new FeatureFlagService();
useEffect(() => {
loadFlags();
}, []);
const loadFlags = async () => {
const allFlags = flagService.getAllFlags();
setFlags(allFlags);
};
const toggleFlag = async (key: string) => {
const flag = flags.find(f => f.key === key);
if (flag) {
await flagService.updateFlag(key, { enabled: !flag.enabled });
await loadFlags();
}
};
return (
<div className="dashboard">
<h1>Feature Flags</h1>
<table>
<thead>
<tr>
<th>Flag</th>
<th>Description</th>
<th>Status</th>
<th>Rollout</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{flags.map(flag => (
<tr key={flag.key}>
<td>{flag.key}</td>
<td>{flag.description}</td>
<td>
<Switch
checked={flag.enabled}
onChange={() => toggleFlag(flag.key)}
/>
</td>
<td>{getRolloutPercentage(flag)}%</td>
<td>
<button onClick={() => editFlag(flag)}>Edit</button>
<button onClick={() => deleteFlag(flag.key)}>Delete</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience