Module 2: Feature Flags
Flag types: temporary, experiment, operational, permanent
Description
The previous lessons used recommendations as the example flag, and by design it always looked like the same kind of flag: one that starts off, climbs in percentage over time, and eventually reaches 100%. But Mercado has, in its registry, flags that don't follow that pattern at all — lesson 5's kill switch never "climbs in percentage," and an A/B experiment flag should never reach 100% while the experiment is still running. This lesson classifies flags into four types, with different purposes and lifecycles, because treating them all the same — as if they were all going to disappear on their own in a few weeks — is exactly the mistake that opens the door to lesson 7.
Connection to the module. This lesson uses the type field that's been in the registry since lesson 3 (type: 'release' for recommendations, type: 'experiment' for checkoutVariantB), and finally gives it a full purpose. Lesson 7 is going to use this same classification to decide, with judgment, which flags are candidates for removal and which aren't.
An analogy: four kinds of tape, for four different jobs
A well-stocked toolbox doesn't have a single kind of tape for everything — it has painter's tape, meant to come off in a day with no residue; packing tape, to seal a box that's going to be opened weeks later; electrical tape, that stays installed indefinitely protecting a wire; and permanent floor marking tape, painted onto a warehouse floor to mark a lane, meant to last years. Using painter's tape to mark a warehouse lane peels off in a week and leaves the lane unmarked; using permanent floor tape to paint a wall that's getting repainted next month leaves an impossible mess to remove.
The four types of feature flag are, literally, that same logic applied to software: each one is designed to last a different amount of time, and using the wrong type for the job — treating a flag that should live forever as if it were temporary, or leaving a flag meant to last a week alive for months — produces the same kind of disaster as the wrong tape choice.
Worked example: the four types, with Mercado's real flags
Let's classify four flags from Mercado's registry, with a guide for what each type means:
function describeFlagType(type) {
const guide = {
release: 'temporary -- lives while the rollout is in progress; removed once it reaches 100%',
experiment: 'temporary -- lives while the A/B is running; removed once the experiment closes',
operational: 'permanent -- an operational kill switch, stays for the next incident',
permanent: 'permanent -- a long-term business decision (regional config, pricing plan)',
};
return guide[type];
}
const mercadoFlags = [
{ name: 'recommendations', type: 'release', enabled: true, rolloutPercent: 10 },
{ name: 'checkoutVariantB', type: 'experiment', enabled: true, rolloutPercent: 50 },
{ name: 'disableSellerPayouts', type: 'operational', enabled: false, rolloutPercent: 100 },
{ name: 'euPriceDisplay', type: 'permanent', enabled: true, rolloutPercent: 100 },
];
console.log('=== Active flag types at Mercado ===\n');
mercadoFlags.forEach((f) => {
console.log(f.name.padEnd(22) + 'type=' + f.type.padEnd(13) + '-> ' + describeFlagType(f.type));
});
What to expect. Running the file with Node, the output is exactly this:
=== Active flag types at Mercado ===
recommendations type=release -> temporary -- lives while the rollout is in progress; removed once it reaches 100%
checkoutVariantB type=experiment -> temporary -- lives while the A/B is running; removed once the experiment closes
disableSellerPayouts type=operational -> permanent -- an operational kill switch, stays for the next incident
euPriceDisplay type=permanent -> permanent -- a long-term business decision (regional config, pricing plan)
Notice the column that actually matters: not type, but the word that opens each description — temporary or permanent. All four flags use exactly the same technical mechanics (isEnabled(), the same hashUserId(), the same enabled check first); the only thing that distinguishes them is how long they're expected to live in the code, and that expectation is what determines whether an old flag is normal or a problem.
The four types, one by one
release — the type you used in lessons 2 through 5 with recommendations. Wraps a new feature while it climbs from 0% to 100%; once it reaches 100% and is confirmed to stay there, the flag has already fully served its purpose and the code should, eventually, stop needing the if (isEnabled(...)) condition — the feature simply becomes a normal part of the product. Expected lifespan: weeks, not months.
experiment — the type of checkoutVariantB. Wraps a variant being compared against a control, usually at 50% so each group's sample is comparable (the same kind of experiment this ecosystem's metrics guide analyzes with statistical rigor). It lives exactly as long as the experiment runs; when the z-test produces a result — variant wins or loses — the experiment flag gets retired, and if it won, it gets replaced by a release-type flag that carries out the winner's full rollout. Expected lifespan: days to weeks, tied to how long the experiment takes to gather significance.
operational — the kill switch type, disableSellerPayouts. Follows no rollout, has no "final 100%" to advance toward — it exists to shut something off in an emergency (here, seller payouts, if a fraud system detects something odd) and is expected to stay in the code indefinitely, ready for the next incident. Notice its state: enabled: false doesn't mean "abandoned" — it means "the emergency switch is in its normal resting position, ready to activate if needed." Expected lifespan: permanent, as long as the functionality it protects keeps existing.
permanent — the type of euPriceDisplay, which decides whether the price is shown with taxes included (European regulation) or without them. It's not an experiment or a rollout — it's a long-term business decision that varies by context (here, the buyer's region), and it's expected to exist as long as the business rule it represents exists. Unlike operational, it's usually active all the time for the segment it applies to, not waiting for an incident. Expected lifespan: years, or until the business rule itself changes.
Common mistakes
Treating an experiment flag as if it were release, and letting it climb to 100% without closing the experiment. What happens: the team, excited because checkoutVariantB seems to be doing well in early metrics, raises rolloutPercent past the 50% the experiment's design needed, before the metrics guide has calculated significance. Why it happens: raising the percentage feels like progress, and the discipline of "an experiment needs comparable groups until the end" is easy to lose sight of with good preliminary results. How to spot it: if an experiment flag's rolloutPercent changed before a closed statistical result exists, the experiment is already compromised — the sample stopped being comparable. How to fix it: an experiment flag keeps its rolloutPercent fixed (usually 50/50) until the experiment formally closes; only then does it get replaced by a release flag, which does make sense to climb toward 100%.
Confusing operational with a "forgotten flag nobody uses." What happens: someone reviewing the flag registry sees disableSellerPayouts with enabled: false for months, with no changes at all, and marks it as a removal candidate, assuming a flag that "hasn't done anything" for a while is debt. Why it happens: the intuition of "if it hasn't changed in months, it's abandoned" is correct for a release flag, but exactly wrong for an operational one — its value lies precisely in staying still, ready, not needing frequent changes. How to spot it: before marking any flag as a removal candidate, check its type — if it's operational, the right question isn't "how long since it changed?" but "does the functionality it protects still exist?" How to fix it: lesson 7 builds exactly this criterion: operational and permanent flags don't count as debt because of their age, unlike release and experiment.
Creating a flag without deciding its type from the start. What happens: someone adds a new flag to the registry without thinking yet about whether it's a temporary rollout, an experiment, an operational switch, or a permanent decision — and months later, nobody on the team can confidently say whether that flag should already have been removed or whether it's correctly serving its function. Why it happens: at the moment of creating the flag, the urgency is usually "make it work now," and classifying its lifecycle feels like an administrative step that can wait. How to spot it: if Mercado's registry has flags with no clear type, or with a type nobody can justify, the classification step got skipped. How to fix it: as in this lesson's example, deciding the type is part of creating the flag, not a later step — it determines, from day one, what lifespan to expect and who should review it, and how often.
Exercises
Exercise 1 — Classify a new flag. Mercado's pricing team wants to test, with 20% of buyers, a dynamic discount algorithm, comparing it against the current algorithm, for two weeks, before deciding whether to adopt it. What type fits, and what should happen to the flag at the end of those two weeks?
See solution
type: 'experiment' — it's comparing two algorithms with a fixed fraction of users, over a defined period, before making a decision based on the result. At the end of the two weeks, if the new algorithm wins, the experiment flag gets retired and replaced by a release flag that carries out the winner's full gradual rollout toward 100% (following the same pattern as recommendations); if it loses, the experiment flag simply gets retired, and the current algorithm stays as it was, with no new flag needed.
Exercise 2 — Find the wrong classification. A teammate classifies disableSellerPayouts (the seller payouts kill switch) as type: 'release', because "someday we won't need it anymore." What's wrong with that reasoning?
See solution
It confuses "not actively being used right now" (enabled: false, at rest) with "eventually finishes and gets retired" (release's definition). A release flag advances toward a final 100% and then disappears from the code because the feature it wrapped is now a normal part of the product. disableSellerPayouts doesn't advance toward any final state — its whole value lies in staying available indefinitely, ready for the next fraud or payment error incident. Classifying it as release would incorrectly put it on lesson 7's list of removal candidates, exactly the mistake this lesson's common mistakes section describes.
Exercise 3 — The four types, in your own words. Without looking at this lesson's table, write from memory a one-line sentence for each of the four types (release, experiment, operational, permanent), explaining how long each should live.
See solution
There's no single correct wording, but each sentence should capture the central idea: release — lives while a rollout is in progress, and disappears once it reaches 100% and is confirmed stable. experiment — lives while an A/B test gathers data, and gets retired as soon as the experiment closes with a result. operational — lives indefinitely, as an emergency mechanism ready for the next incident. permanent — lives indefinitely, representing a long-term business rule that doesn't depend on any experiment or rollout. The distinction that splits the four into two groups — temporary (release, experiment) versus permanent (operational, permanent) — is exactly what lesson 7 uses to decide what counts as debt and what doesn't.
Summary and next step
In this lesson you classified Mercado's flags into four types, by purpose and expected lifespan: release (temporary, toward a final 100%), experiment (temporary, tied to how long an A/B test runs), operational (permanent, a kill switch ready for the next incident), and permanent (permanent, a long-term business rule). You saw that the same technical mechanics — the same isEnabled() — serve all four, and that the only thing distinguishing them is how long they're expected to live in the code.
Before moving on you should be able to: classify a new flag into one of the four types, given its purpose; explain why operational and permanent aren't "forgotten flags" even though they don't change for months; and anticipate that only two of the four types — release and experiment — have a natural retirement date.
Lesson 7 closes the module's topical arc with the direct consequence of this classification: what happens when a release or experiment flag survives past its natural retirement date, and how that debt gets detected — and paid for — before it piles up uncontrolled.
Resources
- Pete Hodgson (with Martin Fowler), "Feature Toggles (aka Feature Flags)" — martinfowler.com/articles/feature-toggles.html. The "Categories of Toggle" section defines exactly this lesson's four types (Release, Experiment, Ops, and Permissioning Toggles) and their expected lifespan, the direct source for this classification.
- LaunchDarkly, "What Is a Kill Switch in Software Development?" — launchdarkly.com/blog/what-is-a-kill-switch-software-development. Describes kill switches as permanent safety mechanisms, distinct from temporary rollout flags — the foundation for this lesson's
operationaltype.