Skip to main content

Feature Flags

Manage feature flags to control feature availability and enable gradual rollouts.

Overview

Feature flags (also called feature toggles) allow administrators to:

  • Enable/disable features without code deployment
  • Roll out features gradually to user segments
  • Test features with specific users
  • Quick rollback if issues arise
  • A/B test new functionality

Current Implementation Status

Note: BookWish does not currently have a formal feature flag system implemented. This guide outlines best practices and a recommended approach for when the system is implemented.

Feature Flag Concepts

Types of Feature Flags

Release Flags

  • Control new feature rollouts
  • Long-lived (weeks to months)
  • Removed after full rollout
  • Example: New recommendation algorithm

Experiment Flags

  • A/B testing and experiments
  • Medium-lived (days to weeks)
  • Track metrics and compare variants
  • Example: New UI layout test

Ops Flags

  • Control operational aspects
  • Long-lived (permanent)
  • System health and performance
  • Example: Enable/disable caching

Permission Flags

  • Control access to premium/admin features
  • Permanent or long-lived
  • Based on user tier/permissions
  • Example: Premium-only features

Flag Targeting

User-Based:

  • Specific user IDs
  • User tier (free, premium, bookstore, admin)
  • User attributes (location, signup date)

Percentage-Based:

  • Gradual rollout (10%, 25%, 50%, 100%)
  • Canary deployments
  • Progressive enhancement

Context-Based:

  • Environment (dev, staging, production)
  • Platform (web, mobile, iOS, Android)
  • Geographic location

Database Schema

CREATE TABLE feature_flags (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) UNIQUE NOT NULL,
description TEXT,
enabled BOOLEAN DEFAULT false,
rollout_percentage INTEGER DEFAULT 0, -- 0-100
enabled_tiers TEXT[], -- ['premium', 'admin']
enabled_user_ids TEXT[], -- Specific users
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_feature_flags_enabled ON feature_flags(enabled);
CREATE INDEX idx_feature_flags_name ON feature_flags(name);

Backend Implementation

// Feature flag service
class FeatureFlagService {
async isEnabled(
flagName: string,
context: {
userId?: string;
userTier?: UserTier;
randomValue?: number; // 0-100 for percentage rollout
}
): Promise<boolean> {
const flag = await this.getFlag(flagName);

if (!flag || !flag.enabled) {
return false;
}

// Check user tier
if (flag.enabledTiers?.length > 0) {
if (!context.userTier || !flag.enabledTiers.includes(context.userTier)) {
return false;
}
}

// Check specific user IDs
if (flag.enabledUserIds?.length > 0) {
if (!context.userId || !flag.enabledUserIds.includes(context.userId)) {
return false;
}
}

// Check percentage rollout
if (flag.rolloutPercentage < 100) {
const randomValue = context.randomValue ?? this.getUserHash(context.userId);
return randomValue < flag.rolloutPercentage;
}

return true;
}

private getUserHash(userId?: string): number {
// Consistent hash for user (0-100)
if (!userId) return Math.random() * 100;
// Use consistent hashing algorithm
return (hashCode(userId) % 100 + 100) % 100;
}
}

Frontend Implementation

// React hook for feature flags
function useFeatureFlag(flagName: string): boolean {
const [enabled, setEnabled] = useState(false);
const user = useUser();

useEffect(() => {
async function checkFlag() {
const result = await api.get(`/api/feature-flags/${flagName}`, {
userId: user?.id,
userTier: user?.tier
});
setEnabled(result.enabled);
}
checkFlag();
}, [flagName, user]);

return enabled;
}

// Usage in components
function NewFeatureComponent() {
const newAlgorithmEnabled = useFeatureFlag('new-recommendation-algorithm');

if (newAlgorithmEnabled) {
return <NewRecommendations />;
}
return <OldRecommendations />;
}

Managing Feature Flags

Creating Feature Flags

Create New Flag:

INSERT INTO feature_flags (name, description, enabled, rollout_percentage)
VALUES (
'new-search-ui',
'New search interface with filters and facets',
false,
0
);

Flag Naming Conventions:

  • Use kebab-case: new-search-ui
  • Be descriptive: enhanced-recommendation-algorithm
  • Include context: mobile-app-dark-mode

Enabling Feature Flags

Enable for All Users:

UPDATE feature_flags
SET enabled = true,
rollout_percentage = 100
WHERE name = 'feature-name';

Enable for Specific Tiers:

UPDATE feature_flags
SET enabled = true,
enabled_tiers = ARRAY['premium', 'admin']
WHERE name = 'premium-feature';

Enable for Specific Users:

UPDATE feature_flags
SET enabled = true,
enabled_user_ids = ARRAY['user-id-1', 'user-id-2']
WHERE name = 'beta-feature';

Gradual Rollout:

-- Start with 10% of users
UPDATE feature_flags
SET enabled = true,
rollout_percentage = 10
WHERE name = 'new-feature';

-- Increase to 25%
UPDATE feature_flags
SET rollout_percentage = 25
WHERE name = 'new-feature';

-- Full rollout
UPDATE feature_flags
SET rollout_percentage = 100
WHERE name = 'new-feature';

Disabling Feature Flags

Quick Disable (Rollback):

UPDATE feature_flags
SET enabled = false
WHERE name = 'problematic-feature';

Reduce Rollout:

UPDATE feature_flags
SET rollout_percentage = 10 -- Back to 10%
WHERE name = 'buggy-feature';

Removing Feature Flags

After Full Rollout:

  1. Feature fully deployed and stable
  2. Remove flag checks from code
  3. Deploy code changes
  4. Delete flag from database
DELETE FROM feature_flags
WHERE name = 'old-completed-flag';

Feature Flag Strategies

Gradual Rollout Strategy

Day 1: Internal Testing

  • Enable for admin users only
  • Test thoroughly
  • Fix critical issues

Day 3: Canary (5%)

  • Enable for 5% of users
  • Monitor metrics closely
  • Watch for errors/complaints

Day 7: Expanded (25%)

  • Increase to 25% if stable
  • Continue monitoring
  • Address feedback

Day 14: Majority (75%)

  • Increase to 75%
  • Most users have access
  • Fine-tune based on data

Day 21: Full Rollout (100%)

  • Enable for all users
  • Monitor for any issues
  • Plan flag removal

A/B Testing Strategy

Setup:

  1. Create two feature flags (variant A, variant B)
  2. Enable each for 50% of users (different users)
  3. Track metrics for both groups
  4. Compare results

Example:

-- Variant A: New algorithm
INSERT INTO feature_flags (name, enabled, rollout_percentage)
VALUES ('recommendation-algorithm-v2', true, 50);

-- Variant B: Old algorithm (everyone not in v2)
-- Implemented in code logic

Analysis:

  • Track conversion, engagement, retention
  • Statistical significance testing
  • Make data-driven decision
  • Roll out winner to 100%

Kill Switch Strategy

Emergency Disable:

For features that might cause issues:

  1. Deploy with flag disabled
  2. Enable gradually
  3. Keep flag permanently for quick disable
  4. Monitor closely

Use Cases:

  • Resource-intensive features
  • External API integrations
  • Experimental algorithms
  • High-risk changes

Feature Flag Best Practices

Flag Hygiene

Remove Old Flags:

  • Don't accumulate flags indefinitely
  • Set removal date when creating
  • Track flag age
  • Clean up quarterly

Document Flags:

  • Clear description
  • Owner/team responsible
  • Creation date and reason
  • Removal timeline

Testing

Test Both Paths:

  • Code with flag enabled
  • Code with flag disabled
  • Edge cases and transitions
  • Flag changes during user session

Monitor Closely:

  • Error rates
  • Performance metrics
  • User feedback
  • System health

Communication

Inform Team:

  • Announce new flags
  • Share rollout plan
  • Document flag purpose
  • Coordinate with stakeholders

User Communication:

  • Beta tester notifications
  • Feature announcements
  • Feedback channels
  • Clear expectations

Feature Flag Use Cases

BookWish Examples

New Recommendation Algorithm

INSERT INTO feature_flags (name, description, enabled, rollout_percentage)
VALUES (
'smart-recommendations',
'ML-powered book recommendations based on user behavior',
true,
25 -- Start with 25% rollout
);

Premium Feature Gate

INSERT INTO feature_flags (name, description, enabled, enabled_tiers)
VALUES (
'unlimited-wishlists',
'Remove wishlist limit for premium users',
true,
ARRAY['premium', 'admin']
);

Mobile App Dark Mode

INSERT INTO feature_flags (name, description, enabled)
VALUES (
'mobile-dark-mode',
'Dark mode theme for mobile app',
true
);

Store Custom Domains

INSERT INTO feature_flags (name, description, enabled, enabled_tiers)
VALUES (
'store-custom-domains',
'Allow stores to use custom domains',
true,
ARRAY['bookstore', 'admin']
);

Monitoring Feature Flags

Flag Usage Analytics

Track Flag Evaluations:

CREATE TABLE feature_flag_evaluations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
flag_name VARCHAR(255) NOT NULL,
user_id VARCHAR(255),
user_tier VARCHAR(50),
enabled BOOLEAN,
created_at TIMESTAMP DEFAULT NOW()
);

Query Flag Usage:

SELECT flag_name,
COUNT(*) as evaluations,
COUNT(CASE WHEN enabled THEN 1 END) as enabled_count,
COUNT(DISTINCT user_id) as unique_users
FROM feature_flag_evaluations
WHERE created_at >= NOW() - INTERVAL '7 days'
GROUP BY flag_name;

Flag Impact Metrics

Compare Metrics:

  • Conversion rates (enabled vs. disabled)
  • Engagement metrics
  • Error rates
  • Performance metrics
  • User satisfaction

Admin API Endpoints

Proposed Endpoints

List All Flags:

GET /api/admin/feature-flags
Authorization: Bearer <admin_token>

Get Flag Details:

GET /api/admin/feature-flags/:name
Authorization: Bearer <admin_token>

Create Flag:

POST /api/admin/feature-flags
Authorization: Bearer <admin_token>
Content-Type: application/json

{
"name": "new-feature",
"description": "Description of feature",
"enabled": false,
"rolloutPercentage": 0
}

Update Flag:

PUT /api/admin/feature-flags/:name
Authorization: Bearer <admin_token>
Content-Type: application/json

{
"enabled": true,
"rolloutPercentage": 50
}

Delete Flag:

DELETE /api/admin/feature-flags/:name
Authorization: Bearer <admin_token>

Third-Party Solutions

As an alternative to building a custom system, consider:

LaunchDarkly

  • Enterprise feature flag platform
  • Rich targeting and segmentation
  • Analytics and insights
  • SDK for all platforms

Split.io

  • Feature flags with experimentation
  • Real-time metrics
  • User segmentation
  • CI/CD integration

Flagsmith

  • Open-source option
  • Self-hosted or cloud
  • Multi-environment support
  • Simple interface

ConfigCat

  • Developer-friendly
  • Affordable pricing
  • Fast response times
  • Good documentation

Next Steps