Chapter 106 · Feature Flags Go
Subchapter 106.2
references/best-practices.mdMarkdown8 KBView on GitHub
Ad blockers have the potential to disable your feature flags, which can lead to bad experiences, such as users seeing the wrong version of your app, or missing a new feature rollout.
To avoid this, deploy a reverse proxy, which enables you to make requests and send events to PostHog Cloud using your own domain.
This means that requests are less likely to be intercepted by tracking blockers, and your feature flags are more likely to work as intended. You’ll also capture more usage data.
PostHog offers a free managed reverse proxy (opens in a new tab), or you can run your own. See our reverse proxy docs (opens in a new tab) for more.
It should be easy to understand how feature flags affect your code. The more locations a flag is in, the more likely it is to cause problems. For example, a developer could remove the flag in one place but forget to remove it in another.
If you expect to use a feature flag in multiple places, it’s a good idea to wrap the flag in a single function or method. For example:
JavaScript
PostHog AI
function useBetaFeature() {
return posthog.isFeatureEnabled('beta-feature')
}PostHog evaluates flags based on the user’s distinct ID, having different IDs can cause the same user to receive different flag values across different sessions, devices, and platforms. By identifying (opens in a new tab) them, you can ensure consistent flag values.
The same applies to identifying groups (opens in a new tab) for group-level flags.
For flags targeting anonymous users, such as signup flows or landing page experiments, consider using device bucketing (opens in a new tab) instead. This evaluates the flag based on the device ID, ensuring a consistent experience on the device even after the user logs in.
Evaluating feature flags requires making a request to PostHog for each flag. However, you can improve performance by evaluating flags locally. Instead of making a request for each flag, PostHog will periodically request and store feature flag definitions locally, enabling you to evaluate flags without making additional requests.
Evaluate flags locally when possible, since this enables you to resolve flags faster and with fewer API calls. See our docs on local evaluation (opens in a new tab) for more details.
Since there is a delay between initializing PostHog and fetching feature flags, feature flags are not always available immediately. This makes them unusable if you want to do something like redirecting a user to a different page based on a feature flag.
To have your feature flags available immediately, you can initialize PostHog with precomputed values until it has had a chance to fetch them. This is called bootstrapping.
See our docs on bootstrapping (opens in a new tab) for more details on how to do this.
Good naming conventions for your flags makes them easier to understand and maintain. Below are tips for naming your flags:
Use descriptive names. For example, is_v2_billing_dashboard_enabled is much clearer than is_dashboard_enabled.
Use name “types”. This helps organize them and makes their purpose clear. Types might include experiments, releases, and permissions. For example, instead of new-billing, they would be new-billing-experiment or new-billing-release.
Name flags to reflect their return type. For example, is_premium_user for a boolean, enabled_integrations for an array, or selected_theme for a single string.
Use positive language for boolean flags. For example, is_premium_user instead of is_not_premium_user. This helps avoid double negatives when checking the flag value (e.g. if !is_not_premium_user is confusing).
When testing a change behind a feature flag, it is best to roll it out to a small group of users and increase that group over time. This is also known as a phased rollout (opens in a new tab). It enables you to identify any potential issues ahead of the full release.
For example, at PostHog we often roll out the flag to just the responsible developer. It then moves on to the internal team, then beta users, and finally into a full rollout. This enables us to test in production (opens in a new tab), get multiple rounds of feedback, identify issues, and polish the feature before the full release.
Leaving flags in your code for too long can confuse future developers and create technical debt, especially if it’s already rolled out and integrated. Be sure to remove stale flags once they are completely rolled out or no longer needed.
When you have many flags to clean up, use bulk delete (opens in a new tab) to select and delete multiple flags at once. Select flags using checkboxes (shift-click to select a range), or filter by name or status and use “select all matching” to select all flags that match your criteria. PostHog validates that flags aren’t used by experiments, early access features, or other dependent flags before deletion.
It’s possible that a feature flag will return an unexpected value (opens in a new tab). For example, if the flag is disabled or failed to load due to a network error.
In this case, its best to check that the feature flag returns a valid expected value before using it. If it isn’t, fallback to working code.
For sophisticated feature rollouts, consider using feature flag dependencies (opens in a new tab) where one flag’s activation depends on another flag’s state. This is useful for:
When using dependencies, keep the dependency chains simple and avoid circular dependencies.
We aim to be significantly cheaper than our competitors. To help you reduce your bill, we’ve created a dedicated guide (opens in a new tab) to estimating and reducing your feature flag costs.
For feature flags with flag persistence enabled and used across both your frontend and backend, you will need to do one of the following to ensure the evaluation result of the flag is consistent between both environments:
JavaScript
PostHog AI
// Frontend: Identify the user
posthog.identify('user123')
// Backend: Use the same distinct ID
const flagValue = await posthog.getFeatureFlag('my-flag', 'user123')getFeatureFlag call.JavaScript
PostHog AI
// Frontend: Get the anonymous ID (before identify is called)
const anonId = posthog.getAnonymousId()
// Backend: Pass the anonymous ID as a person property override
const flagValue = await posthog.getFeatureFlag(
'my-flag',
'user123', // identified distinct ID
{
personProperties: {
$anon_distinct_id: anonId
}
}
)Ask a question
HelpfulCould be better