feature-flags

v2026.09.24

Feature flag management. LaunchDarkly, Unleash, Flagsmith, and custom implementations. Gradual rollouts, A/B testing, kill switches, and flag lifecycle management. USE WHEN: user mentions "feature flag", "feature toggle", "LaunchDarkly", "Unleash", "Flagsmith", "gradual rollout", "A/B test", "canary release", "kill switch" DO NOT USE FOR: CI/CD deployment strategies - use `github-actions`; environment config - use environment variable patterns

GitHub
Install command
npx skhub add claude-dev-suite/feature-flags
Markdown
SKILL.md

Feature Flags

LaunchDarkly

import * as LaunchDarkly from '@launchdarkly/node-server-sdk';

const client = LaunchDarkly.init(process.env.LD_SDK_KEY!);
await client.waitForInitialization();

// Evaluate flag
const showNewUI = await client.variation('new-dashboard-ui', {
  key: user.id,
  email: user.email,
  custom: { plan: user.plan },
}, false); // default value

if (showNewUI) {
  renderNewDashboard();
} else {
  renderLegacyDashboard();
}

React SDK

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

function Dashboard() {
  const { newDashboardUi, maxUploadSize } = useFlags();

  return newDashboardUi ? <NewDashboard maxUpload={maxUploadSize} /> : <LegacyDashboard />;
}

// Wrap app
export default withLDProvider({
  clientSideID: process.env.NEXT_PUBLIC_LD_CLIENT_ID!,
  context: { kind: 'user', key: user.id, email: user.email },
})(App);

Unleash (self-hosted)

import { initialize } from 'unleash-client';

const unleash = initialize({
  url: 'https://unleash.example.com/api',
  appName: 'my-app',
  customHeaders: { Authorization: process.env.UNLEASH_API_KEY! },
});

if (unleash.isEnabled('new-checkout', { userId: user.id })) {
  // New checkout flow
}

Custom Implementation (simple)

interface FeatureFlags {
  [key: string]: {
    enabled: boolean;
    rolloutPercentage?: number;
    allowedUsers?: string[];
  };
}

class FlagService {
  constructor(private flags: FeatureFlags) {}

  isEnabled(flag: string, userId?: string): boolean {
    const config = this.flags[flag];
    if (!config?.enabled) return false;
    if (config.allowedUsers?.includes(userId!)) return true;
    if (config.rolloutPercentage != null && userId) {
      const hash = simpleHash(userId + flag) % 100;
      return hash < config.rolloutPercentage;
    }
    return config.enabled;
  }
}

Flag Types

TypePurposeExample
BooleanOn/off togglenew-ui-enabled
Percentage rolloutGradual release10% → 50% → 100%
User targetingSpecific usersBeta testers, internal
MultivariateA/B/C testingcheckout-variant: "A"
Kill switchEmergency disablepayments-enabled

Anti-Patterns

Anti-PatternFix
Flags that live foreverSet expiration, clean up after full rollout
Flag checks deep in codeCheck at entry points, pass result down
No default valuesAlways provide sensible defaults
Testing only flagged-on pathTest both paths in CI
Flags in tight loopsCache flag evaluation result

Production Checklist

  • Default values for all flags (graceful degradation)
  • Flag naming convention (feature-name-action)
  • Cleanup process for fully-rolled-out flags
  • Monitoring: flag evaluation metrics
  • Testing both flag states in CI
  • Audit log of flag changes
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

skills/best-practices/feature-flags

Default branch

main

Latest commit

9496306

Tree SHA

fe4e2f1