Skip to content

Experimentation SDK Architecture

intermediate

The problem: experiment evaluation must be fast

Section titled “The problem: experiment evaluation must be fast”

Every experiment evaluation adds latency. If a server-side SDK takes 200ms to decide which variation to show, your API response is 200ms slower. If a client-side snippet blocks rendering while it downloads configuration, visitors see a flash of unstyled content. The architecture of an experimentation system determines whether experiments are invisible to users or noticeably degrade their experience.

Optimizely solves this with a local-evaluation architecture. Both the server-side SDKs and the client-side snippet download a configuration file once, then evaluate all experiment decisions locally without network round-trips.

At the center of every Optimizely experiment is the datafile — a JSON document that contains your complete experiment configuration. It includes every active experiment, its variations, traffic allocation percentages, audience conditions, and feature flag states.

The datafile contains:

  • Experiments — variation definitions, traffic allocation, and status
  • Feature flags — flag keys, variables, and rollout rules
  • Audiences — targeting conditions (attributes, values, operators)
  • Events — metric definitions used for tracking conversions
  • Attributes — user attribute keys available for targeting

Because the datafile is a static JSON document, SDKs evaluate decisions locally using deterministic logic. No network call is needed at decision time. This is what makes experiment evaluation sub-millisecond on the server and near-instant in the browser.

Server-side SDK architecture (Feature Experimentation)

Section titled “Server-side SDK architecture (Feature Experimentation)”

Server-side SDKs follow a consistent lifecycle across all platforms:

  1. Initialize — The SDK downloads the datafile from Optimizely’s CDN and builds an in-memory representation of your experiment configuration.
  2. Evaluate — When your code calls decide() or isFeatureEnabled(), the SDK evaluates the decision locally using the in-memory datafile. No network call occurs.

Deprecation note: isFeatureEnabled() is deprecated in SDK v4+ and will be removed in a future major version. Use decide() (via the OptimizelyUserContext) instead. The decide() method returns a single OptimizelyDecision object that includes the enabled state, variation key, and feature variables in one call, replacing the need for separate isFeatureEnabled() and getFeatureVariable*() calls.

  1. Track — When a conversion happens, the SDK queues an event and dispatches it asynchronously to Optimizely’s event endpoint.
  2. Update — The SDK periodically polls for datafile changes or listens for webhook notifications.
Your Application
└── Optimizely SDK (in-process)
├── Datafile (in-memory JSON config)
├── Decision Engine (local evaluation)
├── Event Dispatcher (async HTTP)
└── Notification Center (lifecycle hooks)

When you change an experiment in the Optimizely app, the datafile is updated on the CDN. Your SDK needs to pick up that change. Two strategies are available:

  • Polling — The SDK periodically fetches the latest datafile. Default intervals vary by SDK (typically 5 minutes). You configure the interval at initialization. Polling is simple and works everywhere.
  • Webhooks — Optimizely sends a webhook notification to your server when the datafile changes. Your server then tells the SDK to re-fetch. Webhooks give near-instant updates but require infrastructure to receive them.

For most applications, polling with a short interval (30-60 seconds) balances freshness and simplicity.

Client-side snippet architecture (Web Experimentation)

Section titled “Client-side snippet architecture (Web Experimentation)”

The Web Experimentation snippet follows a different lifecycle optimized for browser environments:

  1. Load — The snippet loads synchronously in the <head> to execute before the page renders. This prevents flicker — visitors never see the original content before the variation is applied.
  2. Activate — The snippet evaluates all active experiments for the current page, determines which variations apply, and executes the variation code (DOM modifications, CSS changes, or custom JavaScript).
  3. Track — The snippet listens for conversion events (clicks, pageviews, custom events) and sends them to Optimizely’s analytics endpoint.
  4. Persist — The snippet stores bucketing decisions in browser cookies so returning visitors see the same variation.
Browser
└── Optimizely Snippet (synchronous in <head>)
├── Snippet Config (bundled experiment definitions)
├── Activation Engine (DOM mutation, CSS injection)
├── Event Tracker (beacon/XHR)
└── Cookie Storage (bucketing persistence)

The snippet bundles experiment configuration directly into the JavaScript file. When you publish changes in the Optimizely app, the snippet file is rebuilt and deployed to the CDN. There is no separate datafile.

AspectFeature Experimentation (SDK)Web Experimentation (Snippet)
Runs onYour server, mobile app, or OTT deviceVisitor’s browser
Config deliverySeparate datafile (JSON via CDN)Bundled into the snippet JS file
Decision latencySub-millisecond (in-memory)Near-instant (in-page)
Flicker riskNone (server-side rendering)Managed via synchronous loading
Update mechanismPolling or webhooksCDN snippet rebuild
Supported changesAny code logicDOM, CSS, and JavaScript
Offline capableYes (cached datafile)No (requires page load)

Choose server-side SDKs when you need to experiment on backend logic, support multiple platforms from a single decision source, or eliminate any risk of visual flicker.

Choose the client-side snippet when marketers need to launch visual tests without code deploys, or when the experiment only involves front-end content changes like headlines, images, or page layout.

Many teams use both. The SDK handles feature flags and backend experiments. The snippet handles marketing-driven visual tests. They share the same Optimizely project and analytics, so results appear in one place.