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-side | Server-side | Edge (ShiftZero) | |
|---|---|---|---|
| Flicker | Yes, or anti-flicker blanking | None | None |
| Page weight | 25–80 KB blocking SDK | None | Optional 1.6 KB async tag |
| React hydration | Mismatch-prone | Safe | Safe — hydration data patched with the HTML |
| App code changes | None | Every experiment | None |
| Who can author | Anyone (visual editor) | Engineers only | Anyone (visual editor) |
| Works on any stack | Yes | One codebase at a time | Yes — operates on the HTML response |
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.