Skip to the chapter
←ReactGGroundwork⚛React
React/Advanced/A22
55 / 57 written chapters
/

The cover
  1. B1Setup & JSX
  2. B2Thinking in React
  3. B3Components
  4. B4Props
  5. B5useState
  6. B6Events & conditionals
  7. B7Lists & keys
  8. B8Forms
  9. B9useEffect
  10. B10Lifting state & styling
  11. B11Guided project
  12. ★Cheat page
  1. I1useRef
  2. I2Context
  3. I3useReducer
  4. I4Custom hooks
  5. I5Effects in depth
  6. I6Routing
  7. I7State architecture
  8. I8Memoisation
  9. I9Boundaries & portals
  10. I10Data fetching
  11. I11Forms at scale
  12. I12State libraries
  13. I13Auth
  14. I14TanStack Query
  15. I15Testing
  16. I16TypeScript
  17. I17Animation
  18. I18E2E testing
  19. I19i18n
  20. I20File uploads
  21. I21Real-time UI
  22. I22shadcn/ui
  23. ★Cheat page
  1. A1Fiber
  2. A2Concurrent
  3. A3Suspense
  4. A4Server Components
  5. A5Server Actions
  6. A6use()
  7. A7Rendering strategies
  8. A8Effect timing
  9. A9External stores
  10. A10Refs in depth
  11. A11Component patterns
  12. A12Performance
  13. A13Accessibility
  14. A14Design systems
  15. A15Advanced TypeScript
  16. A16Architecture at scale
  17. A17Custom renderers
  18. A18Migrating & upgrading
  19. A19Production debugging
  20. A20Review & judgement
  21. A21State machines
  22. A22Feature flags
  23. A23Interview bank
  24. ★Cheat page

⚛A22 · Part 3 · Advanced

Feature flags

Deploying code and releasing it are different decisions, and a flag is what separates them.

5 min read6 sections2 exercises
55 of 57

In this chapter

  • What a flag is for
  • Evaluation needs context
  • Where the flag is evaluated
  • OpenFeature: a standard, not a vendor
  • The costs

and 1 more

about 5 min
06On this page
  1. What a flag is for
  2. Evaluation needs context
  3. Where the flag is evaluated
  4. OpenFeature: a standard, not a vendor
  5. The costs
  6. A rollout, step by step

What a flag is for

if (flags.newCheckout) return <NewCheckout />;
return <OldCheckout />;

The code for the new checkout is merged, built and deployed — but it is off. Turning it on is a configuration change, not a deploy, and turning it off again takes seconds instead of a rollback. That separation is the whole value: engineers merge to the main branch daily without waiting for a feature to be finished, and product decides when users see it.

KindLifetimeExample
Release flagdays to weeks, then deletedthe new checkout, rolled out to 10%, then 100%
Kill switchpermanentturn off the recommendations service when it is down
Experimentuntil the result is intwo pricing layouts, measured
Entitlementpermanentthe export feature, for the paid plan only

The kinds behave differently, and confusing them is the source of most flag pain. A release flag that lives for a year is a permanent fork in the code. A kill switch that gets removed as "clean-up" removes your emergency exit.

Evaluation needs context

const enabled = await client.getBooleanValue("new-checkout", false, {
  targetingKey: user.id,
  plan: user.plan,
  country: user.country,
});

A flag is not a boolean; it is a function of who is asking. The evaluation context — user id, plan, country, app version — feeds rules such as "on for staff", "on for Germany", or "on for 10% of users". The last argument, false, is the default returned if the flag service is unreachable or the flag is missing. Make it the old behaviour, always; a default that turns the new feature on means an outage in your flag provider ships an unfinished feature to everyone.

Percentage rollouts must be sticky
function bucket(flagKey, userId) {
  let h = 2166136261;                                    // FNV-1a hash
  const text = flagKey + ":" + userId;
  for (let i = 0; i < text.length; i++) {
    h ^= text.charCodeAt(i);
    h = Math.imul(h, 16777619);
  }
  return (h >>> 0) % 100;                                  // 0 to 99, the same every time
}

const enabled = bucket("new-checkout", user.id) < rolloutPercent;

Rolling out to 10% by calling Math.random() would put the same user in and out of the feature on every page load. Hashing the flag key and user id gives each user a fixed position from 0 to 99, so a user is either in the ten or not, and raising the rollout to 25% only adds people. Including the flag key in the hash means each flag picks a different ten percent, instead of the same unlucky users testing everything.

Where the flag is evaluated

// Flags SDK (Vercel), server-side
import { flag } from "flags/next";

export const newCheckout = flag({
  key: "new-checkout",
  decide() { return false; },
});

export default async function Page() {
  const enabled = await newCheckout();
  return enabled ? <NewCheckout /> : <OldCheckout />;
}

Evaluate on the server where you can. A client-side flag has to be fetched after the page loads, so users first see the old UI and then watch it change — a layout shift, and a flicker that ruins any experiment measuring the first impression. The Flags SDK evaluates on the server only for this reason, and offers a precompute pattern so flagged pages can still be statically generated instead of falling back to per-request rendering.

Caching is the catch. A page that varies by flag is a page that varies by user, and a CDN happily serves one user's variant to everyone unless the variant is part of the cache key. Flags and caching have to be designed together.

OpenFeature: a standard, not a vendor

import { OpenFeature, OpenFeatureProvider, useBooleanFlagValue } from "@openfeature/react-sdk";

OpenFeature.setProvider(myProvider);          // LaunchDarkly, Unleash, Flagsmith, in-memory…

<OpenFeatureProvider><App /></OpenFeatureProvider>

function Checkout() {
  const enabled = useBooleanFlagValue("new-checkout", false);
  return enabled ? <NewCheckout /> : <OldCheckout />;
}

OpenFeature is a vendor-neutral API for evaluating flags. Your components call useBooleanFlagValue; a provider plugs in whichever service you use, so changing vendors means changing one line, not every call site. The hooks re-render when the evaluation context or the flag configuration changes, and can suspend until the provider is ready. The Flags SDK has an OpenFeature adapter, so the two combine: the framework integration and precompute from one, provider freedom from the other.

The costs

  • Flag debt. Every flag doubles the paths through the code and the states a bug can hide in. Give each release flag an owner and a removal date when you create it, and treat "delete the flag" as part of finishing the feature.
  • Combinatorial testing. Ten independent flags is a thousand combinations. Test the default state and the flag you are changing, and keep long-lived flags few.
  • Client-visible means public. A flag sent to the browser can be read in the network tab, including the name of a feature you have not announced. Put nothing sensitive in flag names or payloads.
  • A flag is not authorisation. Hiding the export button for free users is UX; the export endpoint must still check the plan. The same rule as route guards.
  • Experiments need more than a flag. You also need consistent assignment, logging that a user actually saw the variant, and enough traffic to conclude anything. A flag on its own is only the switch.

A rollout, step by step

  1. Merge the feature behind a flag that is off. Deploy freely.
  2. Turn it on for the team, then a small percentage of real users.
  3. Watch error rate and the relevant business metric between each step; increase to 25%, 50%, 100%.
  4. If a metric moves the wrong way, turn the flag off. No deploy, no rollback.
  5. When it has been at 100% for a while, delete the flag and the old code.
Interview answer, one sentence

"A flag separates deploy from release: code ships dark and is turned on by configuration, gradually and reversibly, using a sticky hash of the user id so people stay in or out; I evaluate it on the server to avoid flicker, default to the old behaviour if the service is down, never use a flag as authorisation, and delete each release flag once it is at a hundred percent."

02Practice this chapter

Opens in the editor: write it, run it, and check it against real tests.

  • 8 testsA sticky percentage rolloutadvanced
  • 6 testsRender by flag, fall back safelyadvanced
All 54 for this level →
A22

Finished reading?

0 of 57 chapters read in this series

→Next stop

  1. A21Previous · A21State machines and XState
  2. A22you are here
  3. Next · A23React interview bankForty-six questions split by level, and twenty output drills.20 min read

01On this page

6 sections · 0%

  1. 01What a flag is for
  2. 02Evaluation needs context
  3. 03Where the flag is evaluated
  4. 04OpenFeature: a standard, not a vendor
  5. 05The costs
  6. 06A rollout, step by step

[] chapters · t top