feature-flags

Control feature releases and enable progressive rollout with feature flag systems.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "feature-flags" with this command: npx skills add bagelhole/devops-security-agent-skills/bagelhole-devops-security-agent-skills-feature-flags

Feature Flags

Control feature releases and enable progressive rollout with feature flag systems.

When to Use This Skill

Use this skill when:

  • Implementing gradual feature rollouts

  • Enabling trunk-based development

  • Running A/B tests and experiments

  • Managing feature lifecycles

  • Implementing kill switches for production

Prerequisites

  • Application code access

  • Feature flag service or self-hosted solution

  • Basic understanding of deployment patterns

Feature Flag Types

Type Purpose Example

Release Control feature visibility New checkout flow

Experiment A/B testing Button color test

Ops Runtime configuration Rate limiting

Permission User access control Premium features

Kill Switch Emergency disable Third-party integration

LaunchDarkly

SDK Setup (Node.js)

const LaunchDarkly = require('launchdarkly-node-server-sdk');

const client = LaunchDarkly.init(process.env.LAUNCHDARKLY_SDK_KEY);

await client.waitForInitialization();

// Evaluate flag const user = { key: 'user-123', email: 'user@example.com', custom: { plan: 'premium', company: 'acme' } };

const showNewFeature = await client.variation('new-checkout', user, false);

if (showNewFeature) { // New feature code } else { // Existing code }

React SDK

import { withLDProvider, useFlags, useLDClient } from 'launchdarkly-react-client-sdk';

// Provider setup export default withLDProvider({ clientSideID: 'your-client-side-id', user: { key: 'user-123', email: 'user@example.com' } })(App);

// Using flags in component function FeatureComponent() { const { newCheckout, experimentVariant } = useFlags(); const ldClient = useLDClient();

// Track events const handleClick = () => { ldClient.track('checkout-started'); };

if (newCheckout) { return <NewCheckout onClick={handleClick} />; } return <OldCheckout onClick={handleClick} />; }

Targeting Rules

LaunchDarkly targeting configuration

flag: new-checkout targeting:

Individual users

targets: - variation: true values: ['user-123', 'user-456']

Rules

rules: # Beta users - variation: true clauses: - attribute: email op: endsWith values: ['@company.com']

# Premium plan
- variation: true
  clauses:
    - attribute: plan
      op: in
      values: ['premium', 'enterprise']

# Percentage rollout
- variation: true
  rollout:
    variations:
      - variation: true
        weight: 20000  # 20%
      - variation: false
        weight: 80000  # 80%

Default

fallthrough: variation: false

Unleash

Server Setup

docker-compose.yml

version: '3.8'

services: unleash: image: unleashorg/unleash-server:latest ports: - "4242:4242" environment: - DATABASE_URL=postgres://postgres:password@db/unleash - DATABASE_SSL=false depends_on: - db

db: image: postgres:15 environment: - POSTGRES_PASSWORD=password - POSTGRES_DB=unleash volumes: - postgres-data:/var/lib/postgresql/data

volumes: postgres-data:

SDK Setup (Node.js)

const { initialize } = require('unleash-client');

const unleash = initialize({ url: 'http://localhost:4242/api', appName: 'my-app', customHeaders: { Authorization: 'your-api-token' } });

unleash.on('ready', () => { // Check feature const isEnabled = unleash.isEnabled('new-checkout');

// With context const context = { userId: 'user-123', properties: { plan: 'premium' } };

const isEnabledForUser = unleash.isEnabled('new-checkout', context);

// Get variant const variant = unleash.getVariant('experiment-flag', context); console.log(variant.name); // 'control' or 'treatment' });

Activation Strategies

Standard strategies

strategies:

  • name: default

    On/off for everyone

  • name: userWithId parameters: userIds: 'user-1,user-2,user-3'

  • name: gradualRolloutUserId parameters: percentage: 25 groupId: 'new-feature'

  • name: gradualRolloutRandom parameters: percentage: 50

  • name: flexibleRollout parameters: rollout: 30 stickiness: userId groupId: 'checkout-exp'

Custom Implementation

Database-Backed Flags

models.py

from django.db import models

class FeatureFlag(models.Model): name = models.CharField(max_length=100, unique=True) enabled = models.BooleanField(default=False) rollout_percentage = models.IntegerField(default=0) allowed_users = models.JSONField(default=list) rules = models.JSONField(default=dict) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True)

service.py

import hashlib

class FeatureFlagService: def init(self): self._cache = {}

def is_enabled(self, flag_name, user_id=None, context=None):
    flag = self._get_flag(flag_name)
    
    if not flag or not flag.enabled:
        return False
    
    # Check user allowlist
    if user_id and user_id in flag.allowed_users:
        return True
    
    # Check rules
    if context and self._evaluate_rules(flag.rules, context):
        return True
    
    # Check percentage rollout
    if flag.rollout_percentage > 0 and user_id:
        return self._is_in_rollout(flag_name, user_id, flag.rollout_percentage)
    
    return flag.rollout_percentage == 100

def _is_in_rollout(self, flag_name, user_id, percentage):
    hash_input = f"{flag_name}:{user_id}"
    hash_value = int(hashlib.md5(hash_input.encode()).hexdigest(), 16)
    return (hash_value % 100) &#x3C; percentage

def _evaluate_rules(self, rules, context):
    for rule in rules.get('rules', []):
        if self._evaluate_rule(rule, context):
            return True
    return False

Redis-Backed Flags

import redis import json

class RedisFeatureFlags: def init(self, redis_url): self.redis = redis.from_url(redis_url) self.prefix = 'feature_flag:'

def set_flag(self, name, config):
    key = f"{self.prefix}{name}"
    self.redis.set(key, json.dumps(config))

def is_enabled(self, name, user_id=None):
    key = f"{self.prefix}{name}"
    data = self.redis.get(key)
    
    if not data:
        return False
    
    config = json.loads(data)
    
    if not config.get('enabled', False):
        return False
    
    # User allowlist
    if user_id in config.get('users', []):
        return True
    
    # Percentage rollout
    percentage = config.get('percentage', 0)
    if percentage == 100:
        return True
    
    if percentage > 0 and user_id:
        return self._hash_user(name, user_id) &#x3C; percentage
    
    return False

def _hash_user(self, flag, user_id):
    import hashlib
    hash_input = f"{flag}:{user_id}"
    return int(hashlib.sha256(hash_input.encode()).hexdigest(), 16) % 100

Testing with Feature Flags

Unit Testing

// Jest mocking jest.mock('launchdarkly-node-server-sdk', () => ({ init: jest.fn(() => ({ waitForInitialization: jest.fn().mockResolvedValue(undefined), variation: jest.fn() })) }));

describe('Checkout', () => { it('shows new checkout when flag enabled', async () => { const ldClient = require('launchdarkly-node-server-sdk').init(); ldClient.variation.mockResolvedValue(true);

const result = await renderCheckout(user);
expect(result).toContain('NewCheckout');

});

it('shows old checkout when flag disabled', async () => { const ldClient = require('launchdarkly-node-server-sdk').init(); ldClient.variation.mockResolvedValue(false);

const result = await renderCheckout(user);
expect(result).toContain('OldCheckout');

}); });

Integration Testing

pytest fixtures

import pytest

@pytest.fixture def feature_flags(): """Provide controllable feature flags for testing.""" flags = {}

class TestFlags:
    def set(self, name, value):
        flags[name] = value
    
    def is_enabled(self, name, **kwargs):
        return flags.get(name, False)

return TestFlags()

def test_new_checkout(feature_flags): feature_flags.set('new-checkout', True)

response = client.get('/checkout')
assert 'new-checkout-form' in response.content

Monitoring and Analytics

Flag Usage Tracking

// Track flag evaluations const flagMetrics = { evaluations: new Map(),

track(flagName, variation, user) { const key = ${flagName}:${variation}; const count = this.evaluations.get(key) || 0; this.evaluations.set(key, count + 1);

// Send to analytics
analytics.track('feature_flag_evaluated', {
  flag: flagName,
  variation: variation,
  userId: user.key
});

} };

Stale Flag Detection

from datetime import datetime, timedelta

def detect_stale_flags(): """Find flags that haven't been evaluated recently.""" stale_threshold = timedelta(days=30) now = datetime.utcnow()

stale_flags = []
for flag in FeatureFlag.objects.all():
    if flag.last_evaluated:
        age = now - flag.last_evaluated
        if age > stale_threshold:
            stale_flags.append({
                'name': flag.name,
                'last_evaluated': flag.last_evaluated,
                'age_days': age.days
            })

return stale_flags

Common Issues

Issue: Inconsistent Flag Evaluation

Problem: Same user sees different variations Solution: Use consistent hashing, check caching strategy

Issue: Flag Debt Accumulation

Problem: Too many old flags in codebase Solution: Implement flag lifecycle, regular cleanup sprints

Issue: Performance Impact

Problem: Flag evaluation slowing requests Solution: Use local caching, batch evaluations

Best Practices

  • Use consistent naming conventions

  • Document flag purpose and owner

  • Set expiration dates for temporary flags

  • Implement flag lifecycle management

  • Use gradual rollouts (not 0→100)

  • Monitor flag evaluation metrics

  • Clean up old flags regularly

  • Test both variations in CI

Related Skills

  • blue-green-deploy - Deployment strategies

  • git-workflow - Trunk-based development

  • alerting-oncall - Monitoring rollouts

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Security

sops-encryption

No summary provided by upstream source.

Repository SourceNeeds Review
Security

linux-administration

No summary provided by upstream source.

Repository SourceNeeds Review
Security

linux-hardening

No summary provided by upstream source.

Repository SourceNeeds Review