Feature Flags enable you to safely deploy and roll back new features. This means you can ship the code for new features and roll it out to your users in a managed way. If something goes wrong, you can roll back without having to re-deploy your application.
Feature Flags also help you control access to certain parts of your product, such as only showing paid features to users with an active subscription.
Looking to test changes to your product? Check out our Experimentation feature.
Learning resources
We have a number of comprehensive guides to using feature flags, including:
- How to do a canary release with feature flags in PostHog
- Tips for feature flag best practices with examples
Creating feature flags
In the PostHog app sidebar, go to 'Feature Flags' and click 'New feature flag'.
Think of a descriptive name and select how you want to roll out your feature.
Persisting feature flags across authentication steps
You have an option to persist flags across authentication steps.
Consider this case: An anonymous person comes to your website and you use a flag to show them a green call to action.
Without persisting feature flags, the flag value can change on login because their identity can change (from anonymous to identified). Once they login, the flag might evaluate differently and show a red call to action instead.
This usually is not a problem since experiments run either completely for anonymous users, or completely for logged in users.
However, with some businesses, like e-commerce, it's very common to browse things anonymously and login right before checking out. In cases like these you can preserve the feature flag values by checking this checkbox.
Note that there are some performance trade-offs here. Specifically,
- Enabling this slows down the feature flag response.
- It disables local evaluation of the feature flag.
- It disables bootstrapping this feature flag.
Implementing the feature flag
When you create a feature flag, we'll show you an example snippet. It will look something like this:
if (posthog.isFeatureEnabled('new-beta-feature')) {// run your activation code here}
What you do inside that if statement is up to you. You might change the CSS of a button, hide an entire section, or move elements around on the page.
Ensuring flags are loaded before usage
Every time a user loads a page we send a request in the background to an endpoint to get the feature flags that apply to that user. In the client, we store those flags as a cookie.
This means that for most page views the feature flags will be available immediately, except for the first time a user visits.
To combat that, there's a JavaScript callback you can use to wait for the flags to come in:
posthog.onFeatureFlags(function() {// feature flags are guaranteed to be available at this pointif (posthog.isFeatureEnabled('new-beta-feature')) {// do something}})
Bootstrapping flags
There is a delay between loading the library and feature flags becoming available to use. This can be detrimental if you want to do something like redirecting to a different page based on a feature flag.
To have your feature flags available immediately, you can bootstrap them with a distinct user ID and their values during initialization.
posthog.init('<ph_project_api_key>', {api_host: '<ph_instance_address>',bootstrap: {distinctID: 'your-anonymous-id',featureFlags: {'flag-1': true,'variant-flag': 'control','other-flag': false}}})
To get the flag values for bootstrapping, you can call getAllFlags()
in your server-side library, then pass the values to your frontend initialization. If you don't do this, your bootstrap values might be different than the values PostHog provides.
If the distinct user ID is an identified ID (the value you called posthog.identify()
with), you can also pass the isIdentifiedID
option. This ensures this ID is treated as an identified ID in the library. This is helpful as it warns you when you try to do something wrong with this ID, like calling identify again.
posthog.init('<ph_project_api_key>', {api_host: '<ph_instance_address>',bootstrap: {distinctID: 'your-identified-id',isIdentifiedID: true,featureFlags: {'flag-1': true,'variant-flag': 'control','other-flag': false}}})
Forcing feature flags to update
In our client-side JavaScript library, we store flags as a cookie to reduce the load on the server and improve the performance of your app. This prevents always needing to make an HTTP request, flag evaluation can simply refer to data stored locally in the browser. This is known as 'local evaluation.'
While this makes your app faster, it means if your user does something mid-session which causes the flag to turn on for them, this does not immediately update. As such, if you expect your app to have scenarios like this and you want flags to update mid-session, you can reload them yourself, by using the reloadFeatureFlags
function.
posthog.reloadFeatureFlags()
Calling this function forces PostHog to hit the endpoint for the updated information, and ensures changes are reflected mid-session.
Server-side local evaluation
If you're using our server-side libraries, you can use local evaluation to improve performance instead of making additional API requests. This requires:
- knowing and passing in all the person or group properties the flag relies on
- initializing the library with your personal API key (created in your account settings)
Local evaluation, in practice, looks like this:
await client.getFeatureFlag('beta-feature','distinct id',{personProperties: {'is_authorized': True}})# returns string or None
This works for getAllFlags
as well. It evaluates all flags locally if possible, and if not, falls back to making a decide
HTTP request.
await client.getAllFlags('distinct id',{groups: {},personProperties: {'is_authorized': True},groupProperties: {}})// returns dict of flag key and value pairs.
Using locally
To test feature flags locally, you can open your developer tools and override the feature flags. You will get a warning that you're manually overriding feature flags.
posthog.feature_flags.override(['feature-flag-1', 'feature-flag-2'])
This will persist until you call override again with the argument false
:
posthog.feature_flags.override(false)
To see the feature flags that are currently active for you, you can call:
posthog.feature_flags.getFlags()
Roll out the feature flag
There are three options for deciding who sees your new feature. You can roll out the feature to:
- A fixed percentage of users or groups
- A set of users or groups filtered based on their user properties, cohort (based on user properties), or group properties.
- A combination of the two
Roll out to a percentage of users or groups
By rolling out to a percentage of users or groups, you can gradually ramp up those who sees a new feature. To calculate this, we "hash" a combination of the key of the feature flag and the unique distinct ID of the user.
This way a user always falls in the same place between 0 and 100%, so they consistently see or do not see the feature controlled by the flag. As you move the slider towards 100%, more users start seeing your feature.
Hashing also means that the same user falls along different points of the line for each new feature. For example, a user may start seeing the feature at 5% for feature A, but only at 80% for feature B.
Filter by user or group properties
This works just like any other filter in PostHog. You can select any property and users that match those filters will see your new feature.
By combining properties and percentages, you can determine something like:
- Roll out this feature to 80% of users that have an email set
- Provide access to this feature to 25% of organizations where the
beta-tester
property istrue
. - Show this component to 10% of users whose
signed_up_at
date is after January 1st.
De-activating properties
If the feature has caused a problem, or you don't need the feature flag anymore, you can disable it instantly and completely. Doing so ensures no users will have the flag enabled.
Feature flag persistence
For feature flags that filter by user properties only, a given flag will always be on if a certain user meets all the specified property filters.
However, for flags using a rollout percentage mechanism (either by itself or in combination with user properties), the flag will persist for a given user as long as the rollout percentage and the flag key are not changed.
As a result, bear in mind that changing those values will result in flags being toggled on and off for certain users in a non-predictable way.
Multivariate feature flags
Where is this feature available?
Free / Open-source
Self-serve
Enterprise
Multivariate feature flags are only available when using PostHog >= 1.28 (if self-hosting) and posthog-js >= 1.13.
PostHog 1.28 introduced support for multivariate feature flags which can return string values according to a specified distribution.
Some examples for a 3-variant case would be 33/33/34%, 50/25/25%, or 70/20/10%. This is ideal for when you want to test multiple variants of the same interchangeable content, such as marketing taglines, colors, or page layouts.
Creating a feature flag with multiple variants
Create a multivariate feature flag just like you would a standard flag, and then change the "Served value" option to "a string value". You will then be prompted to enter a few keys with optional descriptions and set the distribution percentages for each.
Note that the rollout percentage of feature flag variants must add up to 100%. If you wish to exclude some users from your test, i.e. have some users receive no value at all, configure the release condition groups. While the release condition groups determine how many users will be bucketed into any of the given variants, the rollout percentage of each variant determines the portion of the overall release group that will be assigned to that particular variant.
Using multivariate feature flags in your code
With the latest version of our JS library, you can call:
if (posthog.getFeatureFlag('checkout-button-color') === 'black') {// do something}
getFeatureFlag
also returns true or false for standard (Boolean) feature flags, meaning that the following statements are equivalent:
posthog.isFeatureEnabled('new-beta-feature')posthog.getFeatureFlag('new-beta-feature') === true
getFlagVariants
Just as you can call getFlags()
to return an array of feature flags that are currently active, you can call:
posthog.feature_flags.getFlagVariants()
getFlagVariants
returns an object:
{"new-beta-feature": true,"checkout-button-color": "black"}
onFeatureFlags
onFeatureFlags(callback)
now passes the feature flag variants object as the second argument to callback
, which looks like this:
posthog.onFeatureFlags(function (flags, flagVariants) {// do something usefulconsole.log(flags) // ["new-beta-feature", "checkout-button-color"]console.log(flagVariants) // { "new-beta-feature": true, "checkout-button-color": "black" }})
Note that getFlags()
and the callback argument flags
will include the key names of all truthy feature flags, including active multivariate feature flags.
Querying data by multivariate feature flag values
With the latest version of our JS library, we send each feature flag's value as a separate property on every event. This means the values can be used in filters and breakdowns in Insights queries or wherever else you may choose to filter incoming events.
We send the event properties as $feature/your-feature-name
, for example $feature/checkout-button-color
. Standard (Boolean) flags are captured in this format as well.
For example, if you have a Trends graph of button click events and you'd like to narrow it down to clicks only when the checkout button is blue, apply a filter to your graph series such that $feature/checkout-button-color = blue
.
If you'd like to compare all variants for which we have data in one graph, apply a breakdown by $feature/checkout-button-color
.
Experimentation
Feature Flags and Experimentation are different features and work for different use cases. Check out our Experimentation manual for more details.
Further reading
Want to know more about what's possible with Feature Flags in PostHog? Try these tutorials:
- How to do a canary release with feature flags
- Running experiments on new users
- How to run Experiments without feature flags
Using a library other than JavaScript for your feature flag implementation? Check out these other libraries for more details.
Want more? Check our full list of PostHog tutorials.