Skip to content
Production Ready — Part 29 of 30

Feature Flags: Ship Code Without Turning It On

Written by claude-sonnet-4 · Edited by claude-sonnet-4
feature-flagsdeploymentvercel-edge-configposthogproductiongradual-rollout

The 3 AM Lesson Nobody Forgets

It was a Tuesday night deploy. A small team shipped a redesigned checkout flow to production -- tested thoroughly in staging, looking clean in every preview environment. Six hours later, at 3 AM, Sentry started screaming. The new checkout UI had a race condition that only appeared under real production load. Payments were failing silently for roughly 15% of users.

The fix was ready within forty minutes. But deploying it meant a full redeploy -- Vercel build queue, environment variable propagation, CDN cache purge, the works. Twenty more minutes of broken checkout while the deploy pipeline ran.

In the postmortem, someone asked the obvious question: "What if we could have just turned it off?" Not rolled back the whole release. Not re-queued a build. Just flipped a switch to send users to the old checkout while the fix shipped behind the scenes.

That is exactly what feature flags give you.

What Feature Flags Actually Are (From an Operations Perspective)

An architecture talk will tell you feature flags are about trunk-based development and continuous delivery. All true. But operationally, a feature flag is one thing: a kill switch with superpowers.

At its core, a flag is a conditional check in your code that controls what users see -- without requiring a new deployment to change the answer. The flag value lives somewhere that you can update instantly: an environment variable (requires a redeploy), a configuration store (instant), or a dedicated flag service (instant, plus targeting and analytics).

According to recent industry data, teams implementing feature flags see an 89% reduction in deployment-related incidents. That number makes sense when you understand what you actually get: the ability to separate "code is in production" from "users are seeing this feature."

The Knight Capital incident is the canonical disaster: in 2012, a bad deployment reactivated dormant trading code, executed 4 million trades in 45 minutes, and cost $440 million. A feature flag would not have prevented the bug -- but it would have given someone a kill switch instead of a full rollback race against time.

The Three Approaches, In Order of Complexity

Approach 1: Environment Variables (Simplest)

The simplest flag you can ship today is a boolean environment variable. It requires a redeploy to change, so it is not a true runtime toggle -- but it costs nothing, has zero dependencies, and is appropriate for features you want to control across environments.

// lib/flags.ts
export const flags = {
  newDashboard: process.env.FEATURE_NEW_DASHBOARD === 'true',
  betaCheckout: process.env.FEATURE_BETA_CHECKOUT === 'true',
};

// app/dashboard/page.tsx
import { flags } from '@/lib/flags';

export default function DashboardPage() {
  if (flags.newDashboard) {
    return <NewDashboard />;
  }
  return <OldDashboard />;
}

Set FEATURE_NEW_DASHBOARD=true in your Vercel project settings for production, leave it unset (or false) in preview environments. Simple, auditable, no third-party dependencies.

Use env-var flags when: you want to gate a feature per environment and can tolerate a redeploy to toggle it. Good for things like "enable this in staging but not prod" or "turn this on for the v2 launch."

Upgrade to a real tool when: you need to change a flag without redeploying, target specific users, or roll out gradually.

Approach 2: Vercel Edge Config (Dynamic, No Redeploy)

If you are already on Vercel, Edge Config is the best zero-friction upgrade. It is a globally distributed key-value store that reads at edge latency with no cold starts, and the free tier covers most small teams. Changes propagate in seconds -- no redeploy required.

npm install @vercel/edge-config

Create an Edge Config store in your Vercel dashboard, add a feature_new_dashboard key set to false, then connect it to your project.

// middleware.ts
import { NextResponse } from 'next/server';
import { get } from '@vercel/edge-config';

export async function middleware(request: Request) {
  const newDashboardEnabled = await get<boolean>('feature_new_dashboard');

  if (!newDashboardEnabled && request.nextUrl.pathname.startsWith('/dashboard/new')) {
    return NextResponse.redirect(new URL('/dashboard', request.url));
  }

  return NextResponse.next();
}

For server components, the pattern is even cleaner:

// app/dashboard/page.tsx
import { get } from '@vercel/edge-config';

export default async function DashboardPage() {
  const newDashboardEnabled = await get<boolean>('feature_new_dashboard');

  return newDashboardEnabled ? <NewDashboard /> : <OldDashboard />;
}

You can now flip feature_new_dashboard to true in the Vercel dashboard and users see the new feature within seconds. No build. No deploy. No 3 AM emergency queue.

In February 2026, PostHog and Vercel announced a native integration that syncs PostHog flags directly into Vercel's flags system, eliminating the manual wiring between the two tools entirely.

Approach 3: PostHog Feature Flags (Dynamic + Targeting + Analytics)

When you need more than a global on/off toggle -- when you want to target specific users, roll out to 10% of traffic, or measure whether the new feature actually improved conversion -- PostHog is the right tool for small teams. It has a generous free tier and works naturally with Next.js.

npm install posthog-js

Set up your provider once in the app shell:

// app/providers.tsx
'use client';
import posthog from 'posthog-js';
import { PostHogProvider } from 'posthog-js/react';
import { useEffect } from 'react';

export function PHProvider({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
      api_host: 'https://us.i.posthog.com',
    });
  }, []);

  return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
}

Then check flags anywhere in your client components:

// components/Dashboard.tsx
'use client';
import { useFeatureFlagEnabled } from 'posthog-js/react';

export function Dashboard() {
  const newDashboardEnabled = useFeatureFlagEnabled('new-dashboard');

  if (newDashboardEnabled) {
    return <NewDashboard />;
  }
  return <OldDashboard />;
}

For server-side evaluation (avoiding the flicker on load), use the Node SDK:

// app/dashboard/page.tsx
import { PostHog } from 'posthog-node';

export default async function DashboardPage() {
  const client = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY!);
  const userId = await getCurrentUserId(); // your auth logic

  const newDashboardEnabled = await client.isFeatureEnabled(
    'new-dashboard',
    userId
  );

  return newDashboardEnabled ? <NewDashboard /> : <OldDashboard />;
}

In PostHog, you can set that flag to roll out to 10% of users, or only to users whose email matches @yourcompany.com, or to a specific list of beta testers. All without touching your code.

The Operational Playbook

Once flags are wired up, here is how you actually use them in day-to-day production operations:

The kill switch. This is the primary reason to invest in flags. Something breaks in production. You flip the flag off. Users are back on the working path in seconds. You fix the bug properly, flip the flag back on. No rollback, no emergency redeploy.

Gradual rollout. Start any significant feature at 0% or internal-only. Watch your error monitoring. Bump to 10%. Watch for 24 hours. Bump to 50%. Full rollout only after you have real production signal. This is how you catch the race condition before it affects most of your users.

Beta access. Target by email domain or user property. @yourcompany.com gets the feature for internal testing. Then open it to a waitlist. Then go broad. You control the sequence without code changes.

A/B testing. PostHog will split traffic between variants and track events against each group. You get actual data on whether the new checkout converts better before you commit to it permanently.

Flag Hygiene: Cleaning Up What You Ship

Flags accumulate. A codebase with twenty dormant flags is a maintenance hazard -- no one knows which ones are still relevant, removing the wrong one breaks something, and every flag branch is dead code that someone has to read past.

Set a rule: every flag gets a removal ticket created at the same time it is created. Once the feature is fully rolled out and stable (typically two to four weeks), the flag comes out. The code path that was behind the flag becomes the only code path. Delete the other branch.

This is flag debt, and it compounds exactly like technical debt. A flag you shipped in January that is still in your codebase in December is a liability, not a feature.

What Not to Put Behind a Flag

Feature flags control UI and application behavior. They are not the right tool for:

  • Database migrations. Schema changes are not reversible with a flag flip. Run migrations separately, ensure they are backward-compatible with both old and new code, then use a flag to control the application behavior that depends on the new schema.
  • Breaking API changes. If you remove or rename an API field behind a flag, you will break clients that are not flag-aware. Version your API instead.
  • Third-party webhook behavior. External services do not know about your flags. If a payment processor is calling your webhook, the flag state will not automatically propagate to that context.

The Workflow That Makes This Stick

The feature flag workflow that works in practice:

  1. Write the feature behind a flag, defaulting to false
  2. Merge to main -- the code ships to production, but no user sees it
  3. Enable the flag for internal users to test in production
  4. Watch monitoring for 24 hours
  5. Gradual rollout: 10% -> 50% -> 100%
  6. Remove the flag from code, clean up the old code path

This is how you make deployments boring. And boring deployments are the goal.


Action Items

  • Audit your last three releases: could any of them have benefited from a kill switch?
  • Add one environment variable flag to your current project -- even something small, just to establish the pattern
  • If you are on Vercel, create an Edge Config store and move that flag into it so you can toggle without a redeploy
  • If you need user targeting or gradual rollout, set up PostHog (free tier) and wire up one flag in a client component using useFeatureFlagEnabled
  • Write a flag removal ticket the next time you create a flag
  • Add a rule to your team: no significant feature ships to 100% without first going through at least a 10% rollout period

Ask The Guild

What is the most painful deployment you have experienced that a kill switch would have fixed -- and what did you actually do instead? Drop it in the thread. The best war stories tend to produce the best systems.

Copy A Prompt Next

Review and debug

If this article changed how you think about the problem, copy a prompt that turns that judgment into one safe, reviewable next step.

Matching public prompts

23

Keep the task scoped, copy the prompt, then inspect one reviewable diff before the agent continues.

Need the safest first move instead? Open the curated sample prompts before you browse the broader library.

Working With AI ToolsWorking With AI Tools

v0 by Vercel — UI Components From a Text Prompt

Generate production-ready UI components with v0 and integrate them into your projects.

Preview
"I want v0 to generate a React component for this screen:
[describe the UI, data fields, visual style, empty state, loading state, and mobile behavior]
The component must:
1. work in a Next.js + Tailwind project
2. be easy to wire to real data later
Production Ready

Use this production insight inside a full build sequence

Production articles show you what breaks in the real world. The right path turns that lesson into a sequence you can ship with instead of just nodding at.

Best Next Path

DevOps and Deployment

Guild Member · $29/mo

Connect the code to production: CI/CD, hosting, observability, DNS, and the runtime habits that keep launches boring.

25 lessonsIncluded with the full Guild Member library

Need the free route first?

Start with Start Here — Build Safely With AI if you want the workflow and vocabulary before you dive into the deeper path above.

T

About Tom Hundley

Tom Hundley writes for builders who need stronger technical judgment around AI-assisted software work. The Guild turns production experience into public articles, copy-paste prompts, and structured learning paths that help non-software developers supervise AI agents more safely.

Do this next

Leave this article with one concrete move. Copy the matching prompt, or start with the path that teaches the safest next skill in sequence.