shiftzero

Server-side vs client-side A/B testing

The most consequential choice in experimentation tooling isn't which vendor — it's where the variant gets applied. This guide lays out the real trade-offs between client-side, server-side, and edge testing.

Client-side testing: easy install, visible cost

Client-side tools work by loading a JavaScript SDK that waits for the page, then mutates the DOM into the variant. The install is one snippet — which is why the approach dominated for a decade — but the mechanics have three structural problems:

  • Flicker. The browser paints the original, then the SDK swaps it. Users see the control flash into the variant — worst on slow connections, exactly where behaviour is most price-sensitive. “Anti-flicker snippets” fix it by hiding the whole page behind a blank screen, trading flicker for delay.
  • Core Web Vitals. 25–80 KB of blocking JavaScript, layout shift on the swap, delayed LCP. Performance teams routinely find their testing tool in the waterfall's critical path.
  • React hydration. On hydrated frameworks the server HTML must match what the client renders. A DOM mutated between paint and hydration triggers mismatches — warnings at best, a re-rendered flash or broken interactivity at worst.

Server-side testing: correct, but expensive to adopt

Server-side testing moves assignment and rendering into your application: the server decides the variant and renders it directly. No flicker, no SDK weight, no hydration conflict — the statistically and architecturally right answer. The cost is where it lives: your application code. Every experiment is a code change wired through templates or components, deployed by engineers, cleaned up by engineers. Experimentation slows to the speed of your release cycle, and non-engineers can’t author tests at all.

Edge testing: server-side correctness, client-side ergonomics

The edge approach runs at the CDN layer, in front of your origin. Assignment happens on the request; the variant is applied to the HTML as it streams through; framework hydration data is patched in the same pass. The browser receives a page that was simply always the variant.

Client-sideServer-sideEdge (ShiftZero)
FlickerYes, or anti-flicker blankingNoneNone
Page weight25–80 KB blocking SDKNoneOptional 1.6 KB async tag
React hydrationMismatch-proneSafeSafe — hydration data patched with the HTML
App code changesNoneEvery experimentNone
Who can authorAnyone (visual editor)Engineers onlyAnyone (visual editor)
Works on any stackYesOne codebase at a timeYes — operates on the HTML response
The columns aren’t really “three tools” — they’re a decade of trade-off collapsing. Client-side tools optimised for adoption and paid in correctness; server-side paid in velocity. The edge is where the trade stops being necessary.

Questions to ask any vendor

  • Show me a page reload on a live experiment — can I catch the original?
  • What exactly does your snippet weigh, and does it block rendering?
  • What happens to a hydrated React component your editor modifies?
  • Are your live results valid if I check them every day? Under which statistical regime?
  • Where does my visitors' data physically live?

ShiftZero’s answers are on the homepage — including a live embedded demo you can reload yourself — and in the statistics docs.