> ## Documentation Index
> Fetch the complete documentation index at: https://docs.abtestly.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Minimum detectable effect

> MDE is an input you declare, not a result you receive. The exact sample-size formula we run, a worked example to the visitor, and the sizes our own function returns across common baselines.

Minimum detectable effect is the smallest true relative lift a test is
built to catch. It is a declaration, made before launch, and it is the
single number with the most leverage over how long you will be waiting.
Set it small and the test is sensitive and slow. Set it large and the
test is quick and half blind to the effects you are realistically going
to produce.

## The formula we run

`requiredSampleSize` in `worker/src/lib/stats.ts`, returning visitors
**per variation**:

```
p1 = baselineRate
p2 = baselineRate * (1 + mde)
pAvg = (p1 + p2) / 2

zAlpha = PHI_INV(1 - alpha/2)      // 1.959963986 at alpha = 0.05
zBeta  = PHI_INV(power)            // 0.841621233 at power = 0.80

n = ceil(
      ( zAlpha * sqrt(2 * pAvg * (1 - pAvg))
      + zBeta  * sqrt(p1*(1-p1) + p2*(1-p2)) )^2
      / (p2 - p1)^2
    )
```

`PHI_INV` is `inverseNormalCdf`, a Beasley-Springer-Moro approximation
accurate to about 1e-9 across (0, 1).

Note the two variance terms. The `zAlpha` term uses the pooled variance
under the null, where both arms share `pAvg`. The `zBeta` term uses the
unpooled variance under the alternative, where the arms genuinely
differ. Formulas that use a single variance for both terms return a
different number, sometimes by a few percent, and the difference is not
a rounding artifact. Ours matches the test we actually run, which
pools.

The function returns `Infinity`, meaning "cannot be sized", when the
baseline rate is not strictly inside (0, 1), when the MDE is zero or
negative, or when the implied variant rate reaches 1.

## Worked example

A 4 % baseline, hunting a 10 % relative lift, at 95 % confidence and
80 % power.

```
p1   = 0.04
p2   = 0.04 * 1.10 = 0.044
pAvg = 0.042

zAlpha term = 1.959963986 * sqrt(2 * 0.042 * 0.958)
            = 1.959963986 * sqrt(0.080472)
            = 1.959963986 * 0.2836758  = 0.5559945

zBeta term  = 0.841621233 * sqrt(0.04*0.96 + 0.044*0.956)
            = 0.841621233 * sqrt(0.080464)
            = 0.841621233 * 0.2836618  = 0.2387358

numerator   = (0.5559945 + 0.2387358)^2 = 0.6315962
denominator = (0.044 - 0.040)^2         = 0.000016

n = ceil(0.6315962 / 0.000016) = ceil(39474.76) = 39,475
```

39,475 visitors per variation, so 78,950 for a two-arm test. A 0.4
percentage point difference is a small thing to see through binomial
noise, and this is the price.

## What our own function returns

Every cell below is `requiredSampleSize(baseline, mde)` evaluated on the
shipped code at the default 95 % confidence and 80 % power. Visitors per
variation.

| Baseline | 5 % MDE | 10 % MDE | 15 % MDE | 20 % MDE |
| -------- | ------- | -------- | -------- | -------- |
| 1 %      | 637,010 | 163,095  | 74,193   | 42,693   |
| 2 %      | 315,206 | 80,682   | 36,693   | 21,109   |
| 3 %      | 207,938 | 53,211   | 24,193   | 13,914   |
| 5 %      | 122,124 | 31,234   | 14,193   | 8,158    |
| 10 %     | 57,763  | 14,751   | 6,693    | 3,841    |
| 20 %     | 25,583  | 6,510    | 2,943    | 1,683    |

Two things worth reading off that grid.

Lower baselines cost more for the same relative MDE. A rare event
carries more relative noise, so a 1 % checkout completion needs about
25 times the traffic of a 20 % newsletter signup to resolve the same
percentage lift.

The scaling is close to inverse square but not exactly it. Dividing the
5 % column by the 20 % column gives 14.9 at a 1 % baseline rising to
15.2 at a 20 % baseline, against the 16 a clean inverse square would
predict. The gap is real: `p2` moves with the MDE, so the variance
terms in the numerator move too, and they move in the direction that
slightly favours the larger effect. Anyone quoting a flat "quartering
the effect costs sixteen times the traffic" is quoting the limit, not
the formula.

## Choosing the number

Work backwards from the decision, not from the calendar. The question
is what lift would be large enough to justify shipping and maintaining
this change. If a 3 % lift on this page would be worth having, 3 % is
your MDE and the traffic bill is what it is. If only a 15 % lift would
change anything, powering for 3 % buys sensitivity you will never spend.

The common failure runs the other way. A team wants the test finished
this month, so the MDE goes up until the sample size looks comfortable,
and the test launches underpowered for the effect it is actually likely
to produce. It then returns inconclusive on a change that genuinely
helped, and the team records that as evidence the change did nothing.
An underpowered test converts a guess into a confident-looking null.
If the honest MDE demands traffic you do not have, the available moves
are to test further up the funnel, to test a bolder change, or to
accept a longer run. Raising the MDE on paper is not one of them.
Before you commit to any of them, the public
[A/B test calculator](https://abtestly.com/ab-test-calculator) will
price the choice from your own baseline. It runs the same
`requiredSampleSize` function this page describes.

## Where MDE lives in ABTestly

The experiment editor carries a **Minimum detectable effect** field
inside the traffic and split section. It accepts 0.5 % to 50 % in 0.5 %
steps and defaults to 10 %. The value is stored in relative basis points
on the experiment (`mde_bps`, so 10 % is 1000).

It feeds two things and blocks nothing.

The projected runway under the verdict on the results page divides the
remaining sample by recent traffic. See
[test duration](/methodology/test-duration) for that calculation.

The collection-health banner uses the same requirement. When the
projection exceeds 8 weeks the experiment reads "This experiment may
take a while to reach a confident result", prints the MDE as a
percentage, and links back to the field so you can reconsider it.

<Warning>
  Sizing always assumes 95 % confidence and 80 % power. Both call sites
  that size a test (`estimateTimeToDecision` and the collection-health
  cron) invoke `requiredSampleSize` with its defaults. An experiment
  configured at a 99 % confidence level, or carrying a Bonferroni or
  Sidak correction across several variations, needs more traffic than the
  projection assumes, because the correction narrows the alpha the
  verdict is judged against but not the alpha the size was computed with.
  Read the runway as a floor in that case.
</Warning>

## What the MDE does not tell you

It is not a prediction of the lift you will get, and it is not a
threshold the result has to clear. A test powered for 10 % can return a
significant 4 % lift; it just will not do so reliably. It is also not a
promise. Power of 80 % means that if the true effect is exactly your
MDE, you catch it about four times in five, and miss it the fifth.

## Sources

The power calculation on this page is standard, and the guidance on
choosing an effect size rather than accepting one is not ours.

* [Kohavi, R., Tang, D. and Xu, Y. (2020), *Trustworthy Online Controlled Experiments: A Practical Guide to A/B Testing*, Cambridge University Press](https://www.cambridge.org/core/books/trustworthy-online-controlled-experiments/D97B26382EB0EB2DC2019A7A7B518F59). Chapter 17 covers statistical power and the practice of
  fixing the smallest effect worth detecting before a test starts.

Every link above was checked on 6 September 2026.

## Related

<CardGroup cols={2}>
  <Card title="Test duration" icon="calendar" href="/methodology/test-duration">
    Sample size divided by traffic, and the statuses the estimator can return.
  </Card>

  <Card title="The peeking problem" icon="eye" href="/methodology/peeking">
    Why the sample size only protects you if you wait for it.
  </Card>

  <Card title="Reading a result" icon="table" href="/results/reading-a-result">
    What the results page shows once the visitors arrive.
  </Card>

  <Card title="A/B test calculator" icon="calculator" href="https://abtestly.com/ab-test-calculator">
    A public port of `requiredSampleSize`, pinned to `worker/src/lib/stats.ts` by a generated vector suite.
  </Card>
</CardGroup>

***

## Already testing somewhere else

These pages assume you are deciding how to run a test. If you are already
running them in another tool, the quickest way to judge this one is to
[rebuild a single live experiment here](https://abtestly.com/switch-one-experiment)
instead of starting from an empty account.

<Card title="Send us one live experiment" icon="right-left" href="https://abtestly.com/switch-one-experiment" horizontal>
  If you already run experiments in Convert, VWO, Optimizely, AB Tasty or PostHog, send us one that is live today and we rebuild it in ABTestly with you, free. Within two business days you get back three lists: what carries across as it is, what has to be re authored, and what we cannot reproduce. We never ask for a login to your current tool.
</Card>
