> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt # Feature Flags 101 – Feature Flags pocket guide Canonical URL: https://posthog.com/learn/feature-flags/pocket-guide/101 ## On this page - [Deploy is not release](#deploy-is-not-release) - [Three inputs, one stable answer](#three-inputs-one-stable-answer) - [Decide what happens before the answer arrives](#decide-what-happens-before-the-answer-arrives) - [Flag checks record exposure](#flag-checks-record-exposure) Chapter 01 # Feature Flags 101 – Feature Flags pocket guide A flag is a question your code asks PostHog: should this person get this feature? PostHog answers depending on the type of flag: - Boolean flags: `true` or `false` - Multivariate flags: a variant, such as `control` or `test` Any flag can also carry a JSON payload, which you read with `getFeatureFlagPayload()`. See [Payloads](https://posthog.com/learn/feature-flags/pocket-guide/payloads.md). You change the flag's configuration in PostHog, and the code stays the same. ## Deploy is not release Deployed code is expensive to revert. You can separate shipping that code from giving users access to it. A feature flag splits deployment and release. You deploy `new-checkout` turned off, and the new code sits in production doing nothing. Releasing it is a change in PostHog, and so is taking it back: you decide who gets it, how many, and for how long. The check itself is a few lines. On the server, it runs once at the top of the request, with the signed-in user's ID: ```javascript const flags = await posthog.evaluateFlags(user.id) if (flags.isEnabled(NEW_CHECKOUT_FLAG)) { return renderNewCheckout() } return renderCheckout() ``` Evaluate once, where the request comes in, and pass the result down. Each extra evaluation deeper in the stack is another chance for the identity or configuration to change and, when the server sends evaluations to PostHog, another network request. ## Three inputs, one stable answer PostHog evaluates a flag from three inputs: - **The flag's configuration**: its [release conditions](https://posthog.com/docs/feature-flags/creating-feature-flags.md#release-conditions), rollout percentage, and variants, which you set in PostHog. - **Identity**: the person's [distinct ID](https://posthog.com/docs/data/persons.md), which your code passes in. - **Properties**: facts about the person or their company, like their plan or country, that the release conditions filter on. For the rollout percentage, PostHog hashes the flag key and the distinct ID (or, for a group flag, the group key) into a number between 0 and 1. If that number is under the rollout percentage, the flag is enabled: | Input | Value | | --- | --- | | Hash of `new-checkout` and `user-42` | 0.19 | | Rollout percentage | 50% | | Result | `true`, because 0.19 is under 0.50 | The same inputs produce the same result on every check, so when a flag gives someone the "wrong" answer, look for what changed: the flag's configuration or one of the inputs. A common cause is the distinct ID: a browser that checks a flag before `identify()` runs is asking about an anonymous visitor, not your user. Once you know who the user is, call `identify()` before checking the flag. ## Decide what happens before the answer arrives In the browser, flags load asynchronously, and until they arrive `getFeatureFlag()` returns `undefined`, not `false`. Decide what your code does in that gap, and make it the behavior you already trust: ```javascript const showNewCheckout = posthog.getFeatureFlag(NEW_CHECKOUT_FLAG) ?? false ``` Whatever the flag returns, or if it returns nothing, your app must keep working. A broken flag must never take down production. You can also wait for flags with `posthog.onFeatureFlags()`, or send the server's answer along with the page so the gap never opens, which [the next page](https://posthog.com/learn/feature-flags/pocket-guide/server-and-client.md) covers. ## Flag checks record exposure When the SDK checks a flag, it captures a `$feature_flag_called` [event](https://posthog.com/docs/data/events.md) with the flag key and the value the person got, and the SDK drops repeats. That event is how PostHog knows who saw what: Experiments counts exposures from it, and it's one signal for telling a flag nobody checks anymore from one that's still in use, as [cleanup](https://posthog.com/learn/feature-flags/pocket-guide/cleanup.md) explains. See also: [Feature Flags](https://posthog.com/docs/feature-flags.md) · [Best practices](https://posthog.com/docs/feature-flags/best-practices.md) · [Adding flag code](https://posthog.com/docs/feature-flags/adding-feature-flag-code.md) · [How we made flags faster and more reliable](https://posthog.com/blog/how-we-improved-feature-flags-resiliency.md) · [Keep ad blockers from breaking flags](https://posthog.com/tutorials/flags-adblock-prevention.md)