Chapter 22 · Diagnosing Experiment Results
Subchapter 22.4
references/interpretation.mdMarkdown15 KBView on GitHub
How to read PostHog experiment results without falling into common interpretation pitfalls.
Watching results live and ending the experiment the moment it looks significant inflates false positives — you’re giving randomness more chances to look significant.
In Bayesian: PostHog applies a minimum-sample-size guard before analysis proceeds — a low
per-variant floor plus a proportion-validity rule of np > 5 and n(1-p) > 5 for
funnel/proportion metrics (legacy stats module: 100 exposures per variant via
FF_DISTRIBUTION_THRESHOLD). Early swings within that band are still noise — in the early days
of the experiment, significance can flip back and forth a lot.
Recommend:
Symptom: few hundred or fewer exposures per variant; the visible split looks badly off (a roughly 2-to-1 skew at a few dozen exposures is well within normal noise).
Mechanism: With low samples per variant (rule of thumb: under a thousand), the visible split can swing widely from the configured ratio — deterministic-hash variance is large at small samples. PostHog’s calculations account for this; the visible ratio is not a bug.
Funnel/proportion-specific validity gates. Beyond the per-variant exposure floor, funnel metrics also need the normal approximation to hold:
n * p ≥ 5 and n * (1 - p) ≥ 5, where p is the conversion rate.If a variant has very few converters (or, symmetrically, almost everyone converted), the test will refuse to report — not a bug. The fix is the same: more exposures, or accept that the result isn’t ready.
Recommend: wait. Run longer or increase rollout. Don’t read estimates before the running-time calculator threshold (≥1 day and ≥100 exposures).
A/A tests should almost never show significance. If the user reports their A/A test is showing a significant difference, work through:
multiple_variant_handling = "exclude" and the
A/A flag is producing $multiple users (from identity fragmentation, A3 in bias-and-skew.md),
the asymmetric exclusion can produce real differences between two arms that should be identical.ExperimentDataWarehouseNode with events_join_key: $group_<n>
on the exposure side and data_warehouse_join_key on a group-keyed metric table). The LEFT
JOIN duplicates each per-group row by the number of exposed users in that group, so a sum
metric over-counts proportional to per-group user count. If user counts are balanced but per-group
user counts aren’t, the sum can swing 5–30% even on a true A/A — and Bayesian reads that as
significant under the i.i.d. assumption. Detect: read the generated clickhouse_sql from
experiment-results-get, look for an exposures CTE joined per-user to a metric table where
the metric is group-aggregated upstream. Sanity check: re-aggregate the warehouse table by
org/group once (deduped) and compare to the per-user sum; a large gap confirms repeated-row
inflation.Recommend: if conditions 1–3 don’t explain the result, investigate instrumentation rather than assuming the methodology is wrong.
PostHog does not apply multiple-comparisons correction:
So with many metrics or many variants, the chance of some spurious significance grows. Concrete math at α=0.05 (the default): with 5 independent metrics, the chance of at least one false-positive is ~23%; with 10 metrics, ~40%. (Confidence level is configurable — see C6.)
Recommend:
mid-run-changes.md.PostHog defaults to Bayesian. Common misreads:
PostHog has Frequentist support (rolled out June 2025). Set in stats_config. Quick rules:
0.90,
0.95, 0.99 — set via default_experiment_confidence_level on the team or confidence_level
on the experiment’s stats_config. If a user reports a p-value of 0.07 as “significant”, they’re
likely on the 90% setting; check before debugging the math.A frequent source of confusion:
stats_config explicitly selects
Frequentist.Shipping an inconclusive result can be defensible when all of these hold:
Do not ship if the timeseries chart shows a sustained regression — point-in-time significance can flip, but a sustained downward trend on the timeseries is a stronger signal than a snapshot reading.
Recommend the user open the experiment’s timeseries view (per metric) — point-in-time significance can
flip, but a sustained trend is a stronger signal than a snapshot reading. The agent can also pull
this directly via experiment-timeseries-results.
For the qualitative part (replays / intuition), invoke the
posthog:analyzing-experiment-session-replays skill — it surfaces variant-level replay patterns and
is the right tool when the call is “primary metric is up, no guardrail regression, do we ship?”
PostHog can mark a metric as significant and send a notification well before the experiment has accumulated enough data for the result to be stable. The verdict can revert as the sample grows. Treat the notification as a prompt to review, not an instruction to ship.
Before acting on a significance notification, check all of:
np > 5 / n(1-p) > 5 for proportions (legacy stats module: 100 exposures per variant). That’s
a floor for analysis, not a sufficiency bar for shipping. Aim for the number the running-time
calculator produced when the experiment was set up.$multiple %. If A/B/skew (bias-and-skew.md) is in play, the
significance verdict is suspect regardless of how large the gap looks.When a previously-significant banner reverts to not-significant, that’s not a bug — it’s the same analysis updated with more exposures. Explain the difference between signal seen so far and result confirmed.
The End-experiment modal pre-fills the “Variant to keep” selector with the first non-control
variant (feature_flag_variants[1].key) every time it opens. There is no significance check,
no primary-metric direction check, and no guardrail check feeding that default. The “End
experiment” button is gated by selecting a conclusion (won / lost / inconclusive / stopped
early), not by touching the variant selector — so a user who picks a conclusion and clicks
through without re-examining the variant ships the position-default variant. The only way to end
without rewriting the flag is to manually clear the variant selector before clicking; the modal
does not prompt for this.
The modal also asks how to release the chosen variant, with two radio options:
The release-mode choice doesn’t read metrics either; the safer “experiment population” option is the default. If the user clicks through without re-examining, they get the safer behavior on release mode but still the position-default variant — those are independent risks.
Recommend: before clicking “End experiment”, do three things:
If any guardrail is trending negative, or the primary isn’t actually significant, the safe move is to keep control rather than ship the position-default. This matters most for sophisticated users who set guardrails for a reason — they are exactly the population the default will mislead.
A common case: conversion counts from the experiment page get pasted into an online A/B calculator, which returns a different verdict (“not significant” vs PostHog’s “significant”, or vice versa).
Two questions to ask before debugging stats:
stats_config and re-read (see C6).$multiple excluded, test accounts filtered, exposure-bounded date range, per-user aggregation
for trends, conversion-window applied for funnels. An online calculator gets none of that — if
the user typed in raw event counts they grabbed from SQL, the calculator and PostHog are
computing on different populations, and disagreement is expected.After confirming both methodology and inputs match, if the disagreement persists, treat it as a real anomaly worth investigating with the experiment URL.