Skip to content
10 min read

How One Boolean Can Save Your Entire Product

Imagine this. You ship a feature on a Friday afternoon. Everything looks good. You close your laptop, enjoy your weekend. Then Saturday morning, you get an alert. The feature broke a critical flow for a chunk of your users.

The fix is simple. Comment out one component, and the problem disappears. But the deployment pipeline? Not so simple. You push the fix through code review, CI/CD, staging, and production. Hours pass. Hours where real users are stuck. Hours you spend staring at a terminal, waiting for a deploy to finish, wondering if there is a better way.

Spoiler: there is. It is called a feature gate, and it completely changed how I think about shipping code.


Table of Contents


The Problem: Moving Fast at a Startup

If you have ever worked at a startup, you know the drill. Shipping features every week, sometimes every few days. The momentum matters. Every feature shipped is another shot at traction, another chance to prove the product is alive and evolving.

But here is the thing nobody talks about in the “move fast and break things” era: things actually break. A button stops working. A payment flow throws an error. A new feature conflicts with an older one. If you are shipping fast enough, it is not a matter of “if”, it is “when.”

Good engineers prepare for the worst, especially when real people depend on what you are building. You write tests, you do code reviews, you set up monitoring. But what happens when something slips through all of that and a broken feature hits production?


The Naive Fix

Let me walk you through what a typical “emergency fix” process looks like without feature gates.

Something breaks in production. Time to act. Here is what needs to happen:

StepActionEstimated Time
1Identify the broken feature10 min
2Comment out or revert the code2 min
3Create a pull request5 min
4Wait for code review15-60 min
5Merge to main2 min
6Wait for CI/CD pipeline10-30 min
7Deploy to staging5 min
8Verify in staging10 min
9Deploy to production10-30 min
Total~1-3 hours

One to three hours. To turn off one feature.

And that is the optimistic version. That assumes your reviewer is online, your CI pipeline does not flake, and staging does not surface a new issue.

The product cannot wait that long. Your users cannot wait. And you really, really do not want to be the person explaining to anyone why a critical flow has been broken for three hours while you “waited for the pipeline.”

There has to be a better way. Right?


What If You Could Just Turn It Off?

That question changed everything for me.

What if, instead of going through the entire deployment cycle to disable a broken feature, you could just… flip a switch? What if there was a toggle somewhere, a dashboard, an API, anything, that let you turn a feature on or off without touching a single line of code?

That is exactly what a feature gate is. Some people call it a feature flag. Same concept, different name.

The idea is embarrassingly simple. You wrap your feature behind a condition that checks a boolean value: true means the feature is visible, false means it is hidden. And that boolean lives somewhere outside your codebase, somewhere you can change it instantly without a deploy.

That is it. That is the whole concept. A kill switch for your features.

When I first learned about this, I genuinely asked myself: “Why was I not doing this from day one?”

And from that moment, it became my standard. Every new feature I ship goes behind a feature gate. Period.


How Feature Gates Work

At its core, a feature gate is a simple check. Before rendering a feature, your code asks: “Is this feature enabled?”

The answer comes from somewhere external, a service, a database, an API endpoint, and your app reacts accordingly. No redeploy. No code change. Just a value that flips from true to false (or the other way around).

Here is what that difference looks like in practice:

Without Feature GateWith Feature Gate
Disable a featureComment out code, PR, deployFlip a switch in a dashboard
Time to take effect1-3 hoursSeconds
Risk of new bugsYes (code changes can introduce bugs)Lower risk (no code changes needed)
Requires an engineerYesNot always (dashboard access needed)
Can be reversed instantlyNo (another deploy needed)Yes

The comparison speaks for itself. One approach is a whole process. The other is a toggle.


Architecture

Here is a simple overview of how feature gates fit into a typical web application:

┌─────────────────┐         ┌───────────────────────┐
│                 │         │                       │
│   Your React    │────────►│  Feature Gate Service │
│   Application   │  check  │                       │
│                 │◄────────│  ┌─────────────────┐  │
│  if (flag) {    │  true/  │  │ new_checkout    │  │
│    <Feature />  │  false  │  │   = true        │  │
│  }              │         │  │ dark_mode       │  │
│                 │         │  │   = false       │  │
│                 │         │  └─────────────────┘  │
└─────────────────┘         └───────────────────────┘
                                      ▲
                                      │ toggle
                            ┌─────────┴─────────────┐
                            │                       │
                            │     Dashboard         │
                            │                       │
                            │  [✓] new_checkout     │
                            │  [ ] dark_mode        │
                            │                       │
                            │  (You flip the switch │
                            │   right here)         │
                            └───────────────────────┘

The flow is straightforward:

  1. Your app loads and asks the feature gate service: “Which features are enabled?”
  2. The service responds with flags: new_checkout = true, dark_mode = false
  3. Your app renders features based on those values
  4. When you need to disable something, you open the dashboard and flip the switch
  5. Next time the app checks, it gets the updated value
  6. The feature disappears. No deploy, no PR, no waiting.

And here is the timeline comparison that really drives it home:

Without feature gate:
  Bug found → Comment code → PR → Review → CI/CD → Deploy → Fixed
  |                                                          |
  └───────────────── 1-3 hours ─────────────────────────────┘

With feature gate:
  Bug found → Open dashboard → Flip switch → Fixed
  |                                           |
  └──────────── 10 seconds ──────────────────┘

Implementation: Using a Third-Party Service

Now, how do you actually implement this?

The easiest way is to use a service that already handles it for you. There are plenty of options out there: Statsig, LaunchDarkly, Unleash, Split, Flagsmith, and more.

Personally, I use Statsig. My company was already using it on another project that I was not directly involved in, so when it came time to pick a service for my side, it was a no-brainer to just go with what we already had. It has a feature gate feature (yes, that is literally what they call it), a clean dashboard, and solid SDKs. But the concept is the same across all of them.

Here is what it looks like in a React app using Statsig:

import { StatsigProvider, useGateValue } from '@statsig/react-bindings';

function App() {
  return (
    <StatsigProvider sdkKey="client-your-sdk-key" user={{ userID: 'user-123' }}>
      <CheckoutPage />
    </StatsigProvider>
  );
}

function CheckoutPage() {
  const showNewPayment = useGateValue('new_payment_flow');

  return (
    <div>
      <h1>Checkout</h1>
      {showNewPayment ? <NewPaymentFlow /> : <LegacyPaymentFlow />}
    </div>
  );
}

That is it. One hook call, one conditional render. When new_payment_flow is enabled in the Statsig dashboard, users see NewPaymentFlow. When it is disabled, they see LegacyPaymentFlow. Toggle it from the dashboard, and the change is live within seconds.

Third-party services also give you extras you did not know you needed: percentage rollouts (show the feature to 10% of users first), user targeting (enable for internal team only), and A/B testing built right in.

ProsCons
Ready to use, no infrastructure to buildMonthly cost at scale
Dashboard for non-engineers to manageExternal dependency
Advanced features (% rollout, targeting)SDK adds to your bundle size
Team collaboration built inLearning curve for the dashboard

Implementation: Build It Yourself

Do not want to use a third-party service? No problem. You can build your own.

The concept is the same: store boolean flags somewhere accessible, check them in your app, and provide a way to update them without deploying. Here is a minimal but functional approach in React.

Step 1: Create a Feature Flag Provider

// FeatureFlagProvider.jsx
import { createContext, useContext, useState, useEffect } from 'react';

const FeatureFlagContext = createContext({});

export function FeatureFlagProvider({ children }) {
  const [flags, setFlags] = useState({});

  useEffect(() => {
    // Fetch flags from your backend
    fetch('/api/feature-flags')
      .then((res) => res.json())
      .then((data) => setFlags(data));
  }, []);

  return <FeatureFlagContext.Provider value={flags}>{children}</FeatureFlagContext.Provider>;
}

export function useFeatureFlag(flagName) {
  const flags = useContext(FeatureFlagContext);
  return flags[flagName] ?? false;
}

Step 2: Use it in your components

import { FeatureFlagProvider, useFeatureFlag } from './FeatureFlagProvider';

function App() {
  return (
    <FeatureFlagProvider>
      <CheckoutPage />
    </FeatureFlagProvider>
  );
}

function CheckoutPage() {
  const showNewPayment = useFeatureFlag('new_payment_flow');

  return (
    <div>
      <h1>Checkout</h1>
      {showNewPayment ? <NewPaymentFlow /> : <LegacyPaymentFlow />}
    </div>
  );
}

Step 3: The backend endpoint

// /api/feature-flags
const flags = {
  new_payment_flow: true,
  dark_mode: false,
  holiday_banner: true
};

export default function handler(req, res) {
  res.json(flags);
}

That is the whole thing. Change new_payment_flow from true to false in your backend (or database, or Redis, or even a JSON config file), and the feature disappears from your app. No frontend deploy needed.

Now, I am not going to pretend this covers everything a third-party service offers. You will not get percentage rollouts or user targeting out of the box. But for a small team, an early-stage product, or a personal project, this gets you 80% of the value with almost zero overhead.

The important thing is not which approach you choose. It is that you have a way to turn things off quickly when you need to.


Will These Flags Stay Forever?

This is the question that always comes up, and it is a fair one. If every feature gets wrapped in a flag, does that not make the code messy over time?

Honestly, it can. If you never clean up your flags, you end up with what some people call “flag debt,” a codebase full of conditionals for features that have been stable for months or even years.

To be transparent, I have not explored all the different types of feature flags out there yet. I am sure there are more advanced patterns and categorizations. But from my experience so far, here is how I think about it simply.

My approach: I always wrap new features in a flag when I ship them. Then I watch. If the feature runs smoothly for a few weeks, gets stable, and nobody has had to touch the gate, I remove it. The flag served its purpose. Time to clean up. But if the feature is still risky or we are not confident about it yet, the flag stays.

The key is to be intentional about it. Do not just leave flags around because you forgot. Make it part of your process to review and clean them up.


The Other Side

I always try to see everything from both sides. Feature gates are incredibly useful, but this approach also has its cons. It would not be fair to paint it as a perfect solution without talking about the tradeoffs.

Flag service downtime. If you are using a third-party service like Statsig or LaunchDarkly, what happens when that service goes down? If your app cannot reach the flag service, does every feature disappear? Does everything default to enabled? You need to think about fallback behavior. Most SDKs handle this with cached values or sensible defaults, but it is something you have to be aware of and configure properly.

Testing complexity. Every feature flag you add doubles the number of possible states your application can be in. Two flags means four combinations. Ten flags means 1,024 combinations. You cannot realistically test every combination, so you need to be strategic about which flag states you test together. This is a real cost that grows with every flag you add.

Security exposure. If you build your own feature flag system (like the DIY example earlier), be careful about how you expose your flags. A public /api/feature-flags endpoint that returns all your flag names and values means anyone can see what features you are working on, what you have not released yet, and what your internal project names are. Consider scoping what gets exposed or using authentication on your flag endpoints.

Flag debt is real. I mentioned this in the previous section, but it is worth repeating. If you do not actively clean up your flags, your codebase will slowly fill up with conditionals that nobody understands anymore. Schedule regular flag reviews. Treat it like any other technical debt.

None of these are reasons to avoid feature gates. They are reasons to use them thoughtfully.


The Peace of Mind

Now imagine that same scenario from the opening. A feature breaks on a Friday afternoon. But this time, you have a feature gate on it. You get the alert, open the dashboard, flip the switch. Done. Ten seconds. Users see the old flow, everything works, nobody panics.

Instead of spending hours in a deployment pipeline, you disable the feature in seconds and move on with your day.

That is the real value of feature gates. It is not just about the technical implementation. It is a mindset shift. You stop asking “what if this breaks?” and start asking “how quickly can I turn this off?” And that changes how confidently you ship.

Now, every feature I release goes behind a gate. It has become a non-negotiable part of my workflow. And I can tell you, shipping features hits different when you know you have a kill switch ready at all times.

You move faster, because you are not afraid. You sleep better, because you have direct control. And when something does go wrong (because it will), you are ready.

Flip the switch. Fix it later. Ship with confidence.


AI helped draft this post based on my experience and outline. If anything seems off, feel free to let me know.

© 2026 All rights reserved.