Feature Flags as Product Tools
A flag is how a launch becomes small and reversible: who sees it, how many, and how to stop it — owned like a product decision, and removed once the decision is made.
The ask, the obvious response, and how it goes wrong
Every lesson starts where the work starts: someone asked for something, and the first response that comes to mind has a problem.
How do we make every launch small enough to watch and cheap enough to undo?
The PM, on Thursday: "The new one-page checkout goes live Monday. Marketing's spring email goes to the whole list at 9am, and it links straight to checkout."
Merge behind a flag while building — that is what flags are for — then flip it on for everyone Monday morning with the team watching dashboards. If something breaks, flip it back or redeploy the old version.
Flipping to 100% at 9am puts every customer from the biggest traffic spike of the month into the new checkout at once. A bug that affects one card type becomes a revenue incident in minutes.
- Flipping to 100% at 9am puts every customer from the biggest traffic spike of the month into the new checkout at once. A bug that affects one card type becomes a revenue incident in minutes.
- Conversion drops on Monday. Was it the new checkout, or the email bringing in less committed buyers? With everyone moved at the same moment the traffic changed, there is nothing to compare against (Reading an Experiment Honestly).
- "Flip it back" assumes someone knows which flag, has permission to touch it, and trusts that off still works. The old path has had no real traffic during a week of staff testing and nobody has checked it.
- The flag stays in the code for a year. Two later changes are added under it, one of them the wrong way round, and a config tidy-up turns it off in production.
What is really going on
- A flag separates deploy from release. Deploy is an engineering event: the code is in production. Release is a product event: a customer sees it. Once those are separate, the release can be as small, as gradual and as reversible as the product needs.
- Used as a product tool, a flag carries three decisions: who sees it (the cohort — staff, a customer segment, a country), how many (a share of that cohort, raised in steps), and how to stop (a kill switch, tested, owned by someone who will be awake).
- Cohorts make the launch readable. Keeping a stable share of customers on the old checkout means Monday's email traffic lands in both flows, and the comparison survives the change in traffic.
- A kill switch is only a kill switch if the off path is known to work. The old flow has to keep receiving some traffic, or be exercised by tests, until the new one is at 100% and has been stable long enough to trust.
- Every flag is temporary product state with a cost. Each one doubles the paths through the code it guards; two interacting flags make four. A flag with no owner and no expiry is tech debt with a toggle (Tech Debt Is a Product Decision).
Deploy is not release
Without flags, deploying the new checkout and giving it to customers are the same moment, and the only way to undo the second is to undo the first. With a flag, the code can sit in production for days, exercised by staff, before a single customer sees it — and the release becomes a series of small, separate decisions.
A flag config is a launch plan
When the flag carries the product decisions, its configuration reads like the launch plan: who owns it, who is in, how fast it grows, what stops it, and when it goes away. If you cannot fill those fields in, the launch is not planned yet — whatever tool the team uses.
1flag: checkout.one_page2owner: checkout-team3expires: 2026-10-31 # removal ticket opens on this date4default: off # what customers get if flags cannot be read5sticky_by: customer_id # same customer, same checkout, every request6 7kill_switch:8 owner: checkout on-call9 trigger: payment errors above weekly baseline for 15 minutes10 11cohorts:12 - name: staff13 match: { email_domain: store.example }14 percent: 10015 - name: support-known customers16 match: { segment: support_opt_in }17 percent: 10018 - name: everyone else19 percent: 10 # the other 90% is the comparison20 21ramp:22 - { percent: 10, hold_days: 3, gate: conversion and payment errors within guardrails }23 - { percent: 25, hold_days: 3, gate: same }24 - { percent: 50, hold_days: 7, gate: experiment read written up }25 - { percent: 100, gate: PM and engineering agree; old path scheduled for removal }Three fields do most of the work: default (what happens when the flag system fails), sticky_by (whether cohorts leak) and expires (whether the flag ever leaves).
Explaining Monday
The people who most need to understand the rollout are the ones who never see the config: marketing, support and whoever answers to finance for Monday's numbers. They need to know what customers will see, why not everyone sees it, and what happens if something goes wrong (Writing for Stakeholders).
The PM has asked for the new checkout to go live for everyone on Monday, the same morning as the spring email.
"The flag is ready. We will turn it on at 8:45 and monitor. If anything goes wrong we can roll back."
"Plan for Monday: from 8:45, one in ten customers gets the new checkout; everyone else keeps the current one, and all email links work for both. We raise it to a quarter on Thursday and half next week if conversion and payment errors hold. If payment errors go above normal for fifteen minutes, on-call switches everyone back — seconds, no redeploy. Support: the order view shows which checkout a customer used. Keeping most people on the old one during the email is what lets us say afterwards whether the new checkout helped."
The kill switch has to be tested
A kill switch that has never been used is a hope. Before the ramp starts, turn the flag off for a staff account in production and walk through a real order on the old path. Do it again before each large step if the old path has changed.
Keep the switch narrow. One flag per decision: the new checkout, the new address form and the new payment step should be separable, so that turning one off does not revert the others.
- Someone specific owns the switch during each ramp step, and knows they may use it without asking first.
- The trigger is a number and a duration, written down, not "if things look bad".
- The off path is exercised — by real traffic or by tests — for as long as the flag exists.
Every flag leaves
The flag was a tool for the launch. Once the launch decision is made — to 100% or back to zero — the flag has done its job, and every week it stays it costs a little: a path nobody tests, a condition every reader has to understand, a toggle someone can flip by accident.
checkout.one_page still exists at 100%. The old checkout code is still there, untested. A config migration drops unknown keys and quietly switches every customer back to a checkout that no longer matches the current payment API.
The flag expired two weeks after reaching 100%. The removal ticket deleted the flag, the old checkout and its tests. The decision and the rollout numbers live in the launch write-up.
A flag at 100% is not neutral. It keeps a second product alive in the code, and the day it is flipped by accident that second product is what customers get.
How to do it
Most important first.
- Write the rollout as a plan before launch: cohorts, shares, how long each step lasts, and which numbers have to hold before the next step (Guardrail Metrics).
- Start with people who will tell you: staff first, then customers support already has a relationship with, then a small random share, then larger ones.
- Keep customers on the old flow for as long as you need to read the result, and say in advance when that ends.
- Name the kill-switch owner and the condition that triggers it — "payment errors above the weekly baseline for fifteen minutes" — so turning it off is a procedure, not a debate (On-Call for Product Engineers).
- Give every flag an owner and an expiry date in its config, and treat removing it as part of the feature, not a follow-up (Done Means Someone Used It).
- Tell support and marketing who sees what. A support agent who cannot tell which checkout a customer is on cannot help them (The Support Loop).
How to explain the decision
The sentences, the order, and what to lead with — for someone who did not make the call.
- Lead with what customers experience on Monday: "On Monday, one in ten customers gets the new checkout. Everyone else gets the one they know. We raise it through the week if the numbers hold."
- Give marketing the part they care about: "The email still goes to everyone at 9am. The links work for both checkouts; nobody hits a dead end."
- Say what the held-back customers buy, in their terms: "Keeping most people on the old checkout during the email is how we will know whether any change in sales is the new checkout or the email crowd."
- Make the stop condition concrete: "If payment errors go above normal for fifteen minutes, I switch everyone back to the old checkout. It takes seconds and nobody has to redeploy."
- "Marketing wants everyone on the new checkout on email day — it is in the copy." Then the copy promises something we would be testing on the biggest traffic day of the month. I would rather change a sentence of copy than take that risk, and I am happy to help rewrite it.
- "Cleaning up flags is busywork." Every stale flag is a path nobody tests. The day someone flips it by accident, it is an incident; removing it now is an afternoon.
- "Ramping takes too long, we are confident." Then the ramp will be uneventful and cost a few days. Confidence is what every team had before every rollback I have seen.
What can go wrong
- Cohorts that leak: the flag is evaluated per request instead of per customer, so one customer sees the new checkout on the cart page and the old one at payment.
- The ramp becomes the launch: 5%, 25%, 100% in one afternoon because the dashboards looked fine for an hour. Rare problems — one card type, one country, one coupon — need days to show up, not hours.
- A kill switch that kills too much: one flag guards both the new checkout and an address-form fix, and turning off the checkout also reverts a fix support was relying on.
- Flag sprawl: dozens of stale flags, nobody sure which are live, every new engineer afraid to delete any.
- Flags as a way to avoid deciding: the feature sits at 50% for months because nobody wants to own the call to go to 100% or to remove it.
- "Flags are a deploy tool; product does not need to care." Who sees the feature, when, and how it is stopped are product decisions. Leaving them to whoever merges the code means nobody made them.
- "We have a flag, so the launch is safe." Only if the off path works, someone is watching the right number, and that someone knows they are allowed to turn it off.
- "A percentage rollout is an A/B test." A ramp shows whether something breaks. Showing whether it is better needs a stable comparison group, a metric chosen in advance and enough time (Reading an Experiment Honestly).
Knowing whether it worked
- Rollbacks are boring: someone turns off a flag, writes one line in the channel, and nobody calls it an incident.
- Launch-day comparisons exist: the team can say how the new checkout did against the old one on the same traffic.
- The number of live flags stays roughly flat over a quarter instead of climbing, because flags leave as fast as they arrive.
- Support tickets say which experience the customer was in, because support can see it.
- With a handful of flags, a config file and code review are enough. At 10x flags you need a list with owners and expiry dates that someone actually reviews, or the list becomes archaeology.
- At 10x traffic, a 1% cohort is a real sample within hours, and the early ramp can move faster. The long tail — rare card types, rare countries — still takes days regardless of traffic.
- At 10x team size, flags interact across teams. Two teams ramping checkout changes in the same week make each other's results unreadable unless someone owns the calendar.
- Two code paths to maintain and test for the life of the flag. The old checkout has to keep working while the new one ramps, and that is real engineering time.
- Slower to 100%. A week-long ramp means most customers wait a week for an improvement you believe in.
- Flags add a failure mode of their own: the flag service or config is now in the checkout path, and it needs a safe default for when it cannot be read.
Where this applies
Product advice is context-sensitive. These labels say what each claim is specific to, and where a different stage, team or product would differ.
- PRODUCT-SPECIFICServer-rendered web checkout can ramp minute by minute. Native mobile apps ship through app review, so the flag has to be in the binary before launch and the off path has to work in every version still installed; printed material and firmware cannot be flagged at all.
- GENERALSeparating deploy from release applies to any product deployed continuously to many users. Where changes are rare and reach a handful of internal users — a monthly finance batch job — a flag can cost more than it buys.
- TEAM-SPECIFICWith a platform team running a flag service, owners and expiry dates can be enforced by tooling. Without one, the same discipline lives in a config file and code review, and needs a named person to hold it.
Where the depth lives
This domain teaches the product-side judgement and hands the mechanism off.