Setting Up PostHog Feature Flags for Experiment Variants
This skill teaches you how to create and configure multivariate feature flags in PostHog so that users are deterministically assigned to control or test variants in an A/B experiment, with the right targeting rules and rollout percentages.
In PostHog, create a feature flag with a multivariate type, define your variant keys (typically 'control' and 'test'), set the rollout percentage for each variant, and add any property-based release conditions to target the right user segment. Then reference the flag key in your application code to render the correct experience per variant. PostHog persists variant assignment per distinct ID, so users see a consistent experience across sessions.
Outcome: You will have a working multivariate feature flag in PostHog that deterministically assigns each user to exactly one experiment variant, with targeting rules that restrict assignment to the correct user segment, ready for your application code to serve different experiences.
Prerequisites
- A PostHog project with the JS snippet or SDK installed and capturing events
- Basic understanding of feature flags (what they are, why they exist)
- Familiarity with your application codebase where variant logic will be implemented
- A defined experiment hypothesis with at least one success metric (see the sibling skill on designing experiment hypotheses)
Overview
Every A/B experiment depends on a single mechanism: the ability to split users into groups and serve each group a different experience. In PostHog, that mechanism is the feature flag. Before you can run any experiment, you need a multivariate feature flag that defines your variant keys, sets the traffic allocation for each variant, and optionally restricts which users are eligible. This skill covers the full setup process, from flag creation through release conditions, so you have a properly configured flag before you ever touch the Experiments tab or write conditional rendering code.
Within the PostHog Experiments Onboarding A/B Test Method, this skill sits right after designing your hypothesis and success metrics, and right before running the experiment itself. The flag is the bridge between your hypothesis ("if we show a simplified onboarding wizard, activation will increase") and the actual runtime behavior ("this user sees the wizard, that user sees the original flow"). Getting the flag configuration wrong, even subtly, can introduce bias, leak traffic between variants, or silently exclude the users you most care about.
The concrete artifact you produce is a live, enabled feature flag in your PostHog project with the following properties: a human-readable key that matches your experiment naming convention, two or more variant keys with explicit rollout percentages, release conditions that target the correct user segment, and a payload configuration (if needed) that passes variant-specific data to your frontend. You will also verify the flag with PostHog's feature flag debugger before any experiment traffic flows. A properly configured flag means your experiment results will be trustworthy, because every user is assigned exactly once and sees a consistent experience for the duration of the test.
How It Works
PostHog feature flags use a deterministic hashing algorithm to assign each user to a variant. When your application evaluates a flag for a given distinct ID, PostHog computes a hash of the flag key combined with the user's distinct ID. The hash produces a number between 0 and 1, and that number maps to one of the variant buckets you defined. Because the hash is deterministic, the same user always gets the same variant, no database lookup required after the initial assignment.
This hashing approach is what makes feature flags suitable for experiments. Unlike random assignment that could shift between page loads, deterministic hashing guarantees consistency. A user who lands on your site Monday morning and returns Thursday evening will see the same variant both times. This consistency is critical for measuring the true impact of a change, because if users bounced between experiences, your results would measure a blended effect that understates the real difference between variants.
The rollout percentage defines how the 0-to-1 number line is divided among variants. A 50/50 split means the first half of the number line maps to control and the second half maps to test. A 33/33/34 split (for three variants) divides the line into thirds. The percentages do not need to be equal. You might allocate 90% to control and 10% to test if you want to limit exposure to a risky change. Just know that unequal splits require more traffic to reach statistical significance.
Release conditions add a filter layer before the hash is computed. If a user does not match the release conditions, the flag returns false (or a default value), and that user is never assigned to any variant. This is how you restrict an experiment to new users, to a specific geography, or to users on a particular plan. Release conditions evaluate against PostHog person properties and group properties, so your tracking must set those properties before the flag is evaluated.
PostHog also supports flag payloads, which are JSON objects attached to each variant. Instead of just knowing "this user is in variant test," your code can receive a payload like {"wizard_steps": 3, "cta_text": "Get started"}. Payloads keep your experiment configuration centralized in PostHog rather than scattered across conditional branches in your codebase. This makes it easier to run multivariate tests where each variant tweaks several parameters simultaneously.
Understanding this mechanism matters because it reveals the failure modes. If you change the flag key mid-experiment, every user gets re-hashed and many will switch variants, destroying your data. If your release conditions are too broad, users outside your target segment dilute the results. If you set rollout to 100% on a single variant during the test, the experiment is over. The flag is not just a switch. It is the experimental apparatus, and its integrity determines whether your results mean anything. The PostHog Experiments Onboarding A/B Test Method depends on this integrity at every downstream step, from metric collection to statistical analysis.
Step-by-Step
Step 1: Define Your Flag Key and Variant Keys
Before you open PostHog, decide on your naming convention. The flag key is a machine-readable string that will appear in your code, your experiment configuration, and your analytics. Use a pattern like
experiment-[feature]-[date], for exampleexperiment-onboarding-wizard-2024-06. This key must be unique across your project.Next, decide your variant keys. For a simple A/B test, use
controlandtest. For A/B/N tests with multiple treatments, use descriptive keys likecontrol,short-wizard, andlong-wizard. Write down each variant key and a one-sentence description of what the user will experience in that variant.This document becomes your experiment's source of truth and should be shared with anyone who touches the codebase or reviews results.
Tip: Avoid generic flag keys like `test-1` or `new-feature`. When you have 30 flags in your project six months from now, descriptive keys save hours of debugging. Include a date or sprint identifier so you can quickly identify stale flags during cleanup.
Step 2: Create the Feature Flag in PostHog
Navigate to the Feature Flags section in your PostHog project dashboard. Click "New feature flag" and enter your flag key exactly as you defined it in step 1. Capitalization and hyphens matter because your code will reference this string. " Boolean flags only support on/off, which technically works for two-variant experiments, but multivariate flags give you named variant keys that are more readable in code and more extensible if you add variants later.
Add each variant key you defined, then set the description field to match your one-sentence variant descriptions. Save the flag but do not enable it yet.
Tip: PostHog also lets you create flags automatically when you create an experiment in the Experiments tab. If you prefer that workflow, the experiment creator will generate the flag for you. However, creating the flag manually first gives you more control over release conditions and payloads, which is worth doing for complex experiments.
Step 3: Set Rollout Percentages for Each Variant
With your multivariate flag open, set the rollout percentage for each variant. For a standard A/B test, set both control and test to 50%. PostHog's interface shows a visual bar that confirms the percentages sum to 100%. If you are running a riskier change, such as a new payment flow, consider starting with a 90/10 split where 90% of users see the control and only 10% see the test.
This limits blast radius but requires significantly more traffic to reach significance, so plan your experiment timeline accordingly. For A/B/N tests with three or more variants, divide traffic as evenly as possible. A three-variant test at 33/33/34 is fine. Avoid allocating less than 10% to any single variant, because the small sample size makes that variant's results unreliable.
Tip: If your experiment's minimum detectable effect (MDE) requires 5,000 users per variant, a 50/50 split needs 10,000 total users. Changing to 80/20 means you need 25,000 users to get 5,000 in the smaller bucket. Always calculate sample size before deciding on an unequal split.
Step 4: Configure Release Conditions to Target the Right Segment
Release conditions determine which users are eligible for the experiment. Click "Add release condition" to define property-based filters. For an onboarding experiment targeting new users, you might filter on a person property like
signed_up_aftergreater than your experiment start date, oraccount_age_daysless than 7. You can combine multiple conditions with AND logic, for exampleplan equals free AND country equals US.Each condition evaluates against PostHog person properties, so confirm that your tracking code sets these properties before the feature flag is evaluated on the client side. If the property does not exist for a user, the condition will not match, and that user will be excluded from the experiment. Test a few known user profiles in PostHog's person view to confirm their properties match your release conditions before proceeding.
Tip: A common pitfall is setting release conditions based on properties that arrive asynchronously. If your flag evaluates on page load but the `plan` property is set by a server-side event that fires 2 seconds later, the flag will return the default value (no variant) for those 2 seconds. Use PostHog's `posthog.onFeatureFlags` callback to wait for flag evaluation to complete before rendering.
Step 5: Add Payloads for Variant-Specific Configuration (Optional)
If your variants differ by more than just showing or hiding a component, attach JSON payloads to each variant. Open the payload editor for each variant key and enter a JSON object. For example, the control variant might have
{"wizard_steps": 5, "show_video": true}while the test variant has{"wizard_steps": 3, "show_video": false}. Your application code retrieves the payload along with the variant assignment, so you can pass these values directly to your component props or configuration.This approach keeps variant configuration centralized in PostHog instead of buried in conditional branches across your codebase. Payloads are especially useful for multivariate tests where each variant tweaks several parameters, because the alternative is a growing chain of if-else blocks. Keep payloads small and flat. Nested objects work but are harder to update and debug.
Tip: Payloads are returned as strings by some SDKs, so remember to parse them with JSON.parse() in JavaScript or the equivalent in your language. Test that your parsing handles edge cases like empty payloads or malformed JSON gracefully.
Step 6: Implement the Flag in Your Application Code
In your application, use the PostHog SDK to evaluate the flag and render the appropriate experience. ,
'control'or'test') orundefinedif the user does not match release conditions. Use a switch statement or if-else block to render the correct component for each variant. getFeatureFlagPayload('experiment-onboarding-wizard-2024-06')` to retrieve the JSON payload for the assigned variant.For server-side evaluation (Python, Node, Ruby, Go), use the corresponding SDK method and pass the user's distinct ID explicitly. Wrap your variant rendering in a loading state or fallback that handles the brief period before flags are evaluated, so users never see a flash of the wrong variant.
Tip: In React, create a reusable hook like `useExperimentVariant(flagKey)` that returns `{ variant, payload, isLoading }`. This pattern prevents scattered flag evaluation calls and makes it trivial to add new experiments. It also centralizes the loading state so you can show a skeleton screen instead of the default experience during evaluation.
Step 7: Verify Flag Assignment with PostHog's Feature Flag Debugger
Before enabling the flag for real traffic, verify that it works correctly. PostHog offers a feature flag debugger in the toolbar (if you have the PostHog toolbar enabled on your site) and in the person detail view. Navigate to the persons section, find a test user (or use your own distinct ID), and check which variant they are assigned to. Confirm that the variant matches what you expect based on the rollout percentage and release conditions.
Also verify using multiple distinct IDs to ensure that different users get different variants. If every test user gets the same variant, your release conditions may be too restrictive, or your rollout percentages may be misconfigured. Check the flag evaluation log for any errors. Additionally, test that a user who does not match your release conditions gets
undefinedorfalseand sees the default experience, not one of the variants.Tip: Create 3-5 test persons in PostHog with different property combinations: one that matches all release conditions, one that matches none, one that matches partially. This matrix ensures your conditions work correctly at the boundaries, not just in the happy path.
Step 8: Enable the Flag and Confirm Live Traffic
Once verification passes, enable the flag by toggling it on in the PostHog dashboard. Monitor the Events tab and filter for the
$feature_flag_calledevent, which PostHog automatically emits when your code evaluates the flag. Within the first few minutes (depending on your traffic), you should see this event appearing with the flag key and variant properties. Check the distribution of variants across events.With a 50/50 split and enough events (at least 50-100), you should see a roughly equal distribution. If one variant dominates, revisit your release conditions and rollout percentages. ,
onboarding_completed,first_action_taken) are firing and include the feature flag property so PostHog can attribute them to variants in the experiment results.Tip: If you see zero `$feature_flag_called` events after enabling the flag, the most common cause is a deployment gap. Your application code referencing the flag may not be deployed yet, or the PostHog snippet may be cached. Clear caches and verify that the code path referencing the flag is reachable by real users.
Step 9: Connect the Flag to a PostHog Experiment
With the flag live and traffic flowing, go to the Experiments section in PostHog and create a new experiment. When prompted for a feature flag, select your existing flag from the dropdown rather than creating a new one. PostHog will automatically pull in the variant keys and rollout percentages. Define your goal metric (the event that represents success) and any secondary metrics.
Set the experiment's minimum sample size or duration based on your power analysis. Launch the experiment. PostHog will now track variant assignment alongside your goal events and compute statistical significance using either Bayesian or frequentist methods, depending on your project settings. From this point, the experiment is running and you should not modify the flag configuration until the experiment concludes.
Any changes to rollout percentages, release conditions, or variant keys during the experiment will invalidate your results.
Tip: If you accidentally created the flag through the Experiments tab earlier and also created one manually, you may have duplicate flags. Delete the unused one immediately. Two flags with similar keys is a recipe for confusion, especially if a developer references the wrong key in code.
Examples
Example: Two-Variant Onboarding Wizard Test for a B2B SaaS Product
A B2B SaaS tool with 2,000 new sign-ups per month wants to test whether a shorter 3-step onboarding wizard improves activation (defined as completing the first integration) compared to the existing 6-step wizard. The team has 4 weeks to run the experiment. Only free-tier users who signed up after the experiment start date should be included.
The team creates a flag with key experiment-onboarding-short-wizard-2024-07 and two variant keys: control (6-step wizard) and test (3-step wizard). Rollout is set to 50/50. Release conditions filter on plan equals free AND created_at greater than 2024-07-01. They attach payloads: control gets {"steps": 6} and test gets {"steps": 3}, which their React wizard component reads to render the correct number of steps.
Before enabling, they test with 5 distinct IDs, confirming 3 land in control and 2 in test. They verify that a user with plan: pro is excluded. After enabling, they check $feature_flag_called events and see a 48/52 split after the first 200 events, which is within normal variance. They connect the flag to a PostHog experiment with integration_completed as the goal metric.
Over 4 weeks, roughly 1,000 users enter each variant. The experiment reaches significance showing the 3-step wizard increases activation by 12%. They proceed to the shipping and cleanup phase.
Example: Three-Variant Pricing Page Test for an E-Commerce Platform
An e-commerce platform with high traffic (50,000 visitors per day to the pricing page) wants to test three pricing presentations: the current table layout (control), a card layout with feature highlights, and a comparison slider. The team wants results within one week. All visitors to the pricing page are eligible.
The team creates a flag with key experiment-pricing-layout-2024-08 and three variants: control, cards, and slider, set to 33/33/34 rollout. No release conditions are needed because all pricing page visitors are eligible, but they add a condition page_visited equals pricing as a safety measure to prevent the flag from being accidentally evaluated on other pages. They do not use payloads because each variant renders a completely different React component. In their pricing page component, they evaluate the flag and render <PricingTable />, <PricingCards />, or <PricingSlider /> based on the returned variant key.
They handle the undefined case by defaulting to the table layout (control). 1% across roughly 2,000 flag evaluations. They connect the flag to an experiment with plan_selected as the primary metric and annual_plan_selected as a secondary metric. Within 5 days, they have over 200,000 users across variants and reach significance.
Example: Mobile App Onboarding Test with Server-Side Flag Evaluation
A mobile fitness app with 500 daily new installs wants to test a gamified onboarding flow versus the standard tutorial walkthrough. The app uses PostHog's Python SDK on the backend to evaluate flags, because client-side evaluation in mobile introduces latency on first launch. Only users on iOS 16+ should be included.
The team creates a flag with key experiment-gamified-onboarding-2024-09 with variants control (tutorial) and test (gamified). Rollout is 50/50. They add a release condition filtering on os_version >= 16 AND platform equals ios. platform})`.
The returned variant key is sent to the mobile app in the registration response payload, so the app knows which onboarding to render without making a separate flag evaluation request. The team verifies the setup by registering test accounts with different OS versions, confirming that iOS 15 users get no variant (default experience, which is the standard tutorial) while iOS 16+ users are split between control and test. They create the experiment in PostHog with first_workout_completed as the goal metric. After 3 weeks and 10,500 eligible users, the experiment shows the gamified flow increases first-workout completion by 18%.
Example: Small Startup Testing a CTA Change with Limited Traffic
A 3-person startup with 300 weekly sign-ups wants to test whether changing the CTA button text on their landing page from 'Start Free Trial' to 'See It In Action' improves trial starts. They expect the test to take 6-8 weeks to reach significance given their low traffic volume.
The team creates a flag with key experiment-cta-text-2024-10 with variants control and test at a strict 50/50 split. Because they need every user to count, they set no release conditions, allowing all landing page visitors to be assigned. They use payloads to store the CTA text: control gets {"cta": "Start Free Trial"} and test gets {"cta": "See It In Action"}. Their landing page fetches the payload and renders the button text dynamically.
This approach means they can test additional CTA text variations in the future by adding new variants and payloads without code changes. onFeatureFlags()` to wait for the flag to load before rendering the button, showing a skeleton placeholder during the brief loading period. After enabling, they check the split weekly rather than daily because their low traffic makes daily variance noisy. At the end of week 2, they see a 49/51 split across 600 users, which is fine.
They connect the flag to an experiment with trial_started as the goal metric. The experiment runs for 7 weeks before reaching significance, showing the 'See It In Action' copy improves trial starts by 8%.
Best Practices
Use a consistent naming convention for all experiment flag keys across your team. A pattern like
experiment-[area]-[change]-[YYYY-MM](e.g.,experiment-onboarding-cta-2024-06) makes it possible to search, filter, and audit flags months later. Without a convention, flag proliferation quickly becomes unmanageable and stale flags accumulate in production code.Always test your feature flag with multiple distinct IDs before enabling it for production traffic. A single test only confirms one code path. You need to verify that different users land in different variants, that users outside your release conditions are excluded, and that edge cases like anonymous users or users with missing properties behave as expected. Skipping this step means discovering assignment bugs after polluted data has already flowed into your experiment.
Set release conditions based on properties that are available synchronously at flag evaluation time. If a property is set by an asynchronous event (a server-side API call, a webhook, a delayed identification call), the flag may evaluate before the property exists, causing the user to be excluded or assigned incorrectly. Audit the timing of property assignment relative to flag evaluation in your application's initialization flow.
Document the mapping between each variant key and the user-visible experience in a shared location (experiment brief, ticket, or README). When a developer sees
variant === 'test'in code, they should be able to look up exactly what 'test' means without reading through rendering logic. This documentation also helps experiment reviewers interpret results correctly, because "test" tells you nothing about what changed.Keep the total number of active experiment flags under 10 at any given time. Each active flag adds a conditional branch to your codebase, increases the surface area for interactions between experiments, and makes debugging harder. If two experiments affect the same user flow, their flags can interact in unexpected ways, producing results that are valid for neither experiment. Prioritize experiments and run them sequentially on the same surface rather than concurrently.
Never modify a flag's rollout percentages or release conditions while an experiment is running. Changing the allocation mid-experiment introduces a temporal confound: users assigned before the change are a different population than users assigned after. PostHog cannot distinguish between these groups in its analysis, so your results will reflect a blended effect that misrepresents both the pre-change and post-change experiences.
Use PostHog's
$feature_flag_calledauto-capture event as a health check during the first hours after launch. Filter events by flag key and check that the variant distribution matches your configured percentages within a reasonable margin. A 50/50 split should show roughly 45-55% per variant in the first few hundred events. Significant deviation (e.g., 70/30) indicates a configuration or targeting problem that should be investigated immediately before more data is collected.Plan for flag cleanup at experiment creation time, not after. Add a calendar reminder or ticket for the expected experiment end date. When the experiment concludes, remove the flag evaluation from your code, delete the flag in PostHog, and deploy. Orphaned flags are technical debt. The sibling skill on shipping winning variants and cleaning up flags covers this process in detail.
Common Mistakes
Using a boolean flag instead of a multivariate flag for the experiment
Correction
Boolean flags return true or false, which technically works for a two-variant test, but they do not give you named variant keys. When you later connect the flag to an experiment, PostHog expects multivariate variant keys like 'control' and 'test' to label results. A boolean flag forces PostHog to map true/false to variant labels, which creates confusion in the results UI and makes A/B/N expansion impossible without creating a new flag. Always select 'Multivariate' as the flag type for experiments, even with only two variants.
Changing the flag key or variant keys after the experiment has started collecting data
Correction
Renaming a flag key forces PostHog to re-hash every user's assignment, because the hash input changes. Users who were in 'control' may now land in 'test,' and vice versa. This cross-contamination makes all previously collected data unreliable. The symptom is a sudden shift in variant distribution visible in the $feature_flag_called events.
If you realize a naming mistake after launch, it is better to stop the experiment, discard the data, create a new flag with the correct name, and restart. The cost of a few lost days of data collection is far lower than the cost of shipping a decision based on contaminated results.
Setting release conditions that are too broad, including users outside the target experiment segment
Correction
If your experiment targets new users but your release conditions allow all users (or you forget to set conditions entirely), existing users will be assigned variants and their behavior will dilute your results. Existing users have different baseline behaviors than new users, so including them adds noise and can mask a real effect. The telltale sign is seeing unexpectedly high control-group conversion rates, because existing users already know the product. Before enabling the flag, query PostHog's persons list with your release condition filters to confirm the resulting population matches your intended experiment segment.
Evaluating the flag before PostHog has loaded or before user properties are available
Correction
In single-page applications, developers sometimes call posthog.getFeatureFlag() in the component's initial render, before the PostHog library has finished loading or before the identify call has set user properties. The result is undefined, and the user sees the fallback experience instead of being assigned a variant. This creates a hidden exclusion: a subset of users (typically those with slower connections) never enter the experiment, biasing results toward users with faster devices. Use PostHog's posthog.onFeatureFlags() callback to wait for flags to be ready, and implement a loading state in your UI that prevents rendering until the variant is known.
Running multiple experiments on the same user flow simultaneously without isolation
Correction
If experiment A changes the onboarding headline and experiment B changes the onboarding CTA button, and both flags are active, a user could see the new headline with the old button, the old headline with the new button, or any combination. This interaction makes it impossible to attribute changes in your goal metric to either experiment independently. The fix is to either run experiments sequentially on the same surface, or use PostHog's mutual exclusion feature (experiment groups) to ensure a user is only enrolled in one experiment at a time on a given flow. Check your active flags before launching a new experiment on the same page or component.
Forgetting to send the feature flag property with goal events, making attribution impossible
Correction
PostHog can automatically attach active feature flag information to events if you enable send_feature_flags in your SDK configuration. If this is not enabled, or if you are using a custom event tracking setup, your goal events (like onboarding_completed) may arrive without any feature flag metadata. When you view experiment results, PostHog cannot determine which variant the converting user was in, so the event is excluded from analysis. Verify that your SDK config includes feature flag auto-capture, and spot-check a few goal events in PostHog's event explorer to confirm that the $feature/your-flag-key property is present.
Other Skills in This Method
Running A/B Tests in the PostHog Experiments Tab
Step-by-step walkthrough of creating, launching, and monitoring an A/B test using PostHog's Experiments UI, including variant allocation and goal setup.
Comparing PostHog Experiments with Eppo, LaunchDarkly, and Other Platforms
How to evaluate PostHog's experimentation capabilities against dedicated tools like Eppo, Statsig, and LaunchDarkly based on analysis methods, integrations, and pricing.
Shipping the Winning Variant and Cleaning Up Feature Flags
How to roll out the winning experiment variant to 100% of users, remove the losing variant's code, and archive feature flags to keep your codebase clean after an experiment concludes.
Designing Experiment Hypotheses and Success Metrics for Onboarding
How to formulate a clear hypothesis, choose primary and secondary conversion metrics, and define what winning looks like before launching an onboarding A/B test.
Segmenting New User Cohorts for Onboarding Experiments
How to target experiments specifically to new users or sign-up cohorts using PostHog's person properties and cohort filters to avoid contaminating results with existing users.
Interpreting Bayesian and Frequentist Results in PostHog
How to read PostHog's experiment results dashboard, understand credible intervals vs p-values, and decide when an experiment has reached statistical significance.
Integrating PostHog A/B Tests with Webflow and Marketing Pages
How to implement PostHog experiments on no-code or marketing landing pages using the JavaScript snippet, Webflow custom code, and anti-flicker techniques.
Frequently Asked Questions
How do I set up PostHog experiment feature flags if my application uses server-side rendering?
) before rendering the page. Pass the user's distinct ID and any relevant person properties to the `get_feature_flag` method. Inject the variant key into the rendered HTML or pass it as a prop to your client-side framework. This approach eliminates the flicker that occurs when client-side flag evaluation loads after the initial render. The tradeoff is that you must have the user's distinct ID available at render time, which typically means the user must be authenticated or you must use a persistent cookie-based ID.
Can I use the same feature flag for multiple experiments at different times?
Technically you can reuse a flag key, but it is strongly discouraged. PostHog's deterministic hashing means the same flag key and distinct ID always produce the same variant. If a user was in the 'control' variant of your first experiment, they will be in 'control' again if you reuse the flag key for a second experiment. This creates a population bias because you are testing the same user split, not a fresh random assignment. Create a new flag with a new key for each experiment, even if the variants are similar.
How long should I wait after enabling a flag before connecting it to an experiment?
You should connect the flag to an experiment within the first few hours of enabling it, ideally before or immediately after. Any traffic that flows through the flag before the experiment is created will not be tracked in the experiment's results, because PostHog starts counting from the experiment's start timestamp. If you want to verify the flag works before starting the official experiment, enable the flag with a very small rollout (e.g., 5%) for a brief verification period, then increase to your target rollout and create the experiment simultaneously.
Should I set up PostHog experiment feature flags before or after designing my success metrics?
Design your success metrics first. The flag configuration depends on knowing your target segment (which determines release conditions), your required sample size (which influences rollout percentages and timeline), and your goal events (which must be firing correctly before the flag sends traffic). The sibling skill on [designing experiment hypotheses and metrics](/skills/designing-onboarding-experiment-hypotheses-and-metrics) should be completed before this one. Configuring a flag without defined metrics often leads to missing release conditions or incorrect targeting that must be fixed mid-experiment.
Why does my PostHog feature flag return undefined for some users even though it is enabled?
The most common causes are: the user does not match your release conditions (check their person properties in PostHog against your conditions), the PostHog SDK has not finished loading when you call `getFeatureFlag()` (use the `onFeatureFlags` callback), the user has not been identified yet (anonymous users may not have the person properties your conditions require), or the distinct ID being used in code does not match the distinct ID in PostHog (common when switching between anonymous and identified users). Check each cause systematically using PostHog's feature flag debugger on the person detail page.
How do I handle PostHog experiment feature flags in a monorepo or microservices architecture?
Centralize flag key definitions in a shared constants file or configuration service that all services can import. Each service that needs to evaluate the flag should have its own PostHog SDK instance initialized with the same project API key. Server-side services should use the server-side SDK and pass the user's distinct ID explicitly. Avoid evaluating the same flag in multiple services for the same user request, because this can cause race conditions if one service caches a different result. Instead, evaluate once at the entry point (API gateway or main application server) and pass the variant downstream via request headers or context objects.
What happens if a user clears their cookies or switches devices during the experiment?
If the user's distinct ID changes (because cookies are cleared and they get a new anonymous ID), PostHog treats them as a new user and may assign them a different variant. This is a known limitation of client-side identity. To mitigate this, encourage or require authentication early in the flow you are testing, so the distinct ID is tied to a stable account rather than a cookie. PostHog's `identify` call merges anonymous and authenticated IDs, but the merge only works if both IDs have been seen in the same session. Cross-device consistency requires authenticated distinct IDs.