Plan and execute a disciplined feature-flag rollout for any app. Use when asked to "add a feature flag", "gradual rollout", "staged release", "kill switch", "dark launch", "flag cleanup", "canary release", "rollback plan", "safe feature release", or "deploy without switching on".
Scanned 9/11/2026
Install to Claude Code
npx -y skills add kensaurus/cursor-kenji --skill workflow-feature-flag --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Workflow Feature Flag?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/kensaurus-workflow-feature-flag)More formats (shields.io, HTML) on the badges page.
---
name: workflow-feature-flag
description: >
Plan and execute a disciplined feature-flag rollout for any app. Use when asked to "add
a feature flag", "gradual rollout", "staged release", "kill switch", "dark launch",
"flag cleanup", "canary release", "rollback plan", "safe feature release", or "deploy
without switching on".
license: MIT
---
# workflow-feature-flag — Gate → Rollout → Monitor → Promote or Rollback
**Degree of freedom: MIXED.** Who is in the cohort and when to promote
`[HIGH freedom]`; default-off, kill switch, and cleanup `[LOW freedom — run exactly]`.
## How to reason
1. **Observe** — existing flag infra, default, targeting, metrics
2. **Interpret** — deploy vs enable; who can be hurt if this is wrong
3. **Classify** — dark launch / percent rollout / kill / promote / cleanup
4. **Severity** — a default-on flag in prod is a failed gate
## Worked example
> **Observe:** new checkout step; no flag library; 100% of traffic would hit it on deploy.
> **Interpret:** ship dark, enable 5%, watch payment errors.
> **Classify:** add default-off flag + kill switch; do not delete the old path yet.
> **Monitor:** error rate > baseline → kill; stable → promote and remove the flag.
## Self-critique before reporting
- **Default off** — merge does not enable the feature
- **Kill exists** — one switch turns it off without a rollback deploy
- **Cleanup named** — flag removal is a tracked follow-through, not forgotten
- **Right owner** — npm package publish → `deploy-npm`; observe window → `workflow-ship-and-observe`
**Shipping code and switching it on are two separate acts.** A feature flag
lets you deploy on your schedule, enable for specific users or a percentage of
traffic, and kill it instantly if anything goes wrong — without a rollback
deploy. This skill gives that discipline to any feature.
---
## Phase 0: Detect existing flag infrastructure [LOW freedom — run exactly]
```
package.json → launchdarkly-node-server-sdk, @launchdarkly/js-client-sdk,
flagsmith, growthbook, unleash-client, posthog-js
.env.* → LD_SDK_KEY, FLAGSMITH_ENV_KEY, GROWTHBOOK_API_HOST,
POSTHOG_KEY (reference by name only)
src/lib/flags.* → custom feature-flag utility
src/config/flags.* → env-var-based flags
```
| Detected platform | Approach |
|-------------------|----------|
| LaunchDarkly | Use LD SDK client; flags managed in LD dashboard |
| PostHog | Use PostHog feature flags API; tied to analytics context |
| GrowthBook | Open-source, self-hosted or cloud; SDK + feature API |
| Flagsmith | Open-source or cloud; REST API + SDK |
| None / custom | Simple env-var or database-backed flag (implement below) |
---
## Phase 1: Design the flag [HIGH freedom]
Before writing any code, define the flag contract:
```
Flag name: [kebab-case, descriptive, present-tense: "new-checkout-flow"]
Description: [What this flag controls — plain English]
Type: Boolean / String variant / JSON payload
Targeting: [Who sees it first: internal users? 5% of traffic? specific org IDs?]
Rollout plan: 0% → internal only → 5% → 25% → 100%
Kill-switch: If Sentry error rate on [metric] exceeds [threshold], roll back to 0%
Cleanup date: [When to remove the flag — typically 2 weeks after 100% stable]
```
**Naming rules:**
- `kebab-case`, present-tense action: `new-checkout-flow`, not `checkoutV2` or `feature123`
- Include the domain: `billing-usage-dashboard`, `auth-passkey-login`
- No generic names like `beta`, `v2`, `new-feature` — they become permanent accidents
---
## Phase 2: Implement the flag gate [LOW freedom — run exactly]
### Option A: Simple env-var flag (no external service)
```typescript
// src/lib/flags.ts
export const FLAGS = {
newCheckoutFlow: process.env.NEXT_PUBLIC_FLAG_NEW_CHECKOUT_FLOW === 'true',
billingUsageDashboard: process.env.NEXT_PUBLIC_FLAG_BILLING_USAGE_DASHBOARD === 'true',
} as const;
export type FeatureFlag = keyof typeof FLAGS;
```
Usage:
```typescript
import { FLAGS } from '@/lib/flags';
if (FLAGS.newCheckoutFlow) {
return <NewCheckoutFlow />;
}
return <LegacyCheckoutFlow />;
```
For server-side percentage rollout without a vendor, use a hash of the user ID:
```typescript
function isInRollout(userId: string, flagName: string, percentage: number): boolean {
const hash = parseInt(
require('crypto').createHash('md5')
.update(`${userId}:${flagName}`)
.digest('hex').slice(0, 8),
16,
);
return (hash % 100) < percentage;
}
```
### Option B: PostHog flags (already in the stack)
```typescript
import { useFeatureFlagEnabled } from 'posthog-js/react';
function CheckoutPage() {
const useNewFlow = useFeatureFlagEnabled('new-checkout-flow');
if (useNewFlow) return <NewCheckoutFlow />;
return <LegacyCheckoutFlow />;
}
```
Server-side (Next.js Server Component):
```typescript
import { PostHog } from 'posthog-node';
const client = new PostHog(process.env.POSTHOG_KEY!);
const isEnabled = await client.isFeatureEnabled('new-checkout-flow', userId);
```
### Option C: LaunchDarkly
```typescript
import { useFlags } from 'launchdarkly-react-client-sdk';
const { newCheckoutFlow } = useFlags();
```
---
## Phase 3: Stage the rollout [LOW freedom — run exactly]
### Step 1: Internal only (0% of real users)
Set the flag enabled only for internal team accounts / test emails.
Verify the feature works end-to-end for the team.
Check for errors in Sentry immediately after enabling:
```json
sentry:search_issues
{
"organizationSlug": "<ORG>",
"query": "new issues in the last 1 hour",
"projectSlugOrId": "<PROJECT>",
"regionUrl": "<REGION_URL>",
"limit": 10
}
```
### Step 2: 5% rollout
Enable for 5% of real users (random cohort). Monitor for 24–48 hours.
Define the health gates before enabling:
- Error rate for the flagged feature path must stay < baseline + 0.5%
- P95 response time must stay < baseline + 200 ms
- No new Critical/High Sentry issues
Check Supabase logs for unexpected errors:
```json
supabase:get_logs
{
"project_id": "<PROJECT_ID>",
"service": "api"
}
```
### Step 3: 25% → 50% → 100%
Each step: enable, wait 24 h, check Sentry + Supabase + business metrics.
Only proceed when the health gates are green.
---
## Phase 4: Rollback procedure [LOW freedom — run exactly]
If any health gate fails at any stage:
1. Set the flag to 0% immediately (no deploy needed — this is the whole point)
2. Capture the Sentry issue IDs and root-cause analysis:
```json
sentry:analyze_issue_with_seer
{
"organizationSlug": "<ORG>",
"issueId": "<ISSUE_ID>",
"regionUrl": "<REGION_URL>"
}
```
3. Fix the root cause (do not re-enable until fixed and re-tested internally)
4. Document what happened in the flag's description
---
## Phase 5: Promote to 100% and clean up [LOW freedom — run exactly]
Once the feature is stable at 100% for at least 1 week:
1. Remove the flag check from the code — ship the new path as the default
2. Delete the fallback code (old path)
3. Delete the flag from the flag platform (LaunchDarkly / PostHog / etc.)
4. Remove the env var from all environments
5. Delete the flag from `src/lib/flags.ts` (for the env-var approach)
**Why cleanup matters:** Stale flags become permanent conditions. Code that says
`if (FLAGS.newCheckoutFlow)` six months later is a landmine nobody dares touch.
```
CLEANUP CHECKLIST:
- [ ] Flag gate removed from source code
- [ ] Old fallback code deleted
- [ ] Flag deleted from the flag platform
- [ ] Env var removed from .env* and deployment config
- [ ] No more references to the flag name in the codebase
```
---
## Phase 6: Rollout report [LOW freedom — do not skip]
```markdown
## Feature Flag Rollout — [flag-name] — [Date]
### Flag
- Name: [flag-name]
- Description: [what it controlled]
- Targeting: [who saw it, what percentage stages]
### Rollout timeline
| Date | Stage | Duration | Health status |
|------|-------|----------|---------------|
| ... | 0% → Internal | 2 days | ✅ Green |
| ... | Internal → 5% | 2 days | ✅ Green |
| ... | 5% → 100% | 3 days | ✅ Green |
### Health gate checks
- Sentry new issues at each stage: [count]
- P95 response time delta: [ms]
- Error rate delta: [%]
### Outcome
**Promoted to 100% / Rolled back** — [reason]
### Cleanup scheduled
[Date to remove the flag from code]
```
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!