The dashboard
The dashboard at app.abtestly.com is where you build tests, pick goals, invite teammates, and read results. Every save is autosaved. Nothing goes live until you click Start or a scheduled start time arrives. Publishing writes a fresh config blob to Cloudflare KV, which the snippet reads on the visitor’s next request. The propagation window is on the order of sixty seconds across the edge. Republish always works; if the publish call fails on the network, an outbox retries it in the background until it succeeds so you never end up with a “saved but not live” experiment.The snippet
The snippet is a small tag you drop into<head>. It fetches your site’s
config from Cloudflare’s edge (cached, no origin round-trip), decides which
variant a visitor should see, applies your JavaScript and CSS, and fires an
exposure event.
The runtime is around 15 KB gzipped. It has no third-party dependencies. It
can wait for consent before firing, and it respects a
visitor opt-out URL that a person can pass to remove
themselves from every test on your site.
The snippet also handles single-page apps. When you turn on
SPA mode, a route change is treated like a new page load.
JavaScript is cleaned up, CSS is torn down, and the next route’s variants
are applied. That is one of the more subtle parts of the runtime; the
SPA lifecycle page walks through it in detail.
Ingest
Events go toapi.abtestly.com. There are only a handful of endpoints and
they are all simple:
Beacons use
text/plain on purpose so that sendBeacon works without a CORS
preflight. Payload shapes are documented on the
beacon protocol page for anyone building
their own integration.
Results
Every event ABTestly accepts is written to two places. The first is Cloudflare Analytics Engine, which is fast and cheap to query. It is what the dashboard reads for live-updating counters. Under very heavy write bursts, AE can sample its own data, that is fine for a live counter, not fine for a bill-of-materials. The second is the Exact Experiment Ledger. Events go through a queue, land in R2 as gzipped NDJSON, and get projected into a per-experiment Durable Object. The ledger is what your final numbers come from, no sampling, no approximation. If the ledger is a little behind while it catches up on a burst, the results page tells you that in plain English instead of quietly serving old numbers. The results page turns those events into a verdict using one of three statistics engines: frequentist, sequential, or Bayesian. You pick the engine per experiment. Verdict text is written in plain English, and it is honest about what it can and cannot say yet.Where each piece lives in the app
Sites & experiments
Every site you own, and every test attached to it.
Goals, locations, audiences
Reusable definitions your experiments attach to.
Settings
Per-site config: snippet, analytics adapters, SPA, global JS,
blocked IPs, opt-out URLs.
Team & access
Members, roles, and twenty per-site permission keys.