Replace Sitecore A/B Testing with Optimizely Web Experimentation

Loading...·7 min read

Sitecore sites arrive at this question from an unusual direction. Most platforms never had testing built in, so adding Optimizely is an addition. Sitecore XP had testing built in — page tests, component tests, personalisation rules, all wired into xDB and the marketing taxonomy — and teams moving to XM Cloud or simply off xDB find themselves with a testing practice, a set of goals, and no engine underneath them.

That makes this a migration rather than an installation, and migrations fail on the parts nobody wrote down. This guide covers what maps from Sitecore testing onto Optimizely Web Experimentation and what does not, where the snippet goes in a Sitecore layout, how to turn Sitecore goals into Optimizely custom events, and how to reconcile two systems that will never quite agree. It has a close cousin in the Google Optimize migration guide, which covers the same shape of move from a different starting point.

What Sitecore Testing Did and What Replaces It

Sitecore's testing was expressed in content terms: a page version, a component variant, a rule on a rendering. Optimizely's is expressed in browser terms: a change to the DOM, applied to a targeted audience. Some things translate cleanly and some do not.

Sitecore XP capability

Optimizely Web Experimentation equivalent

Page-level A/B test on versions

Experiment with variations that change the DOM, or a redirect experiment

Component A/B/n test on a rendering

Experiment scoped by a selector for that component

Personalisation rule on a rendering

Audience condition plus a variation

Goal triggered on a page

Custom event pushed on that page

Engagement value points

No equivalent; use a revenue tag or a numeric value tag

Outcome recorded against a contact

No equivalent in the browser; keep it in Sitecore

Pattern cards and profile scores

No equivalent; audience conditions are evaluated per-session

The two rows with no equivalent are the ones to raise early with whoever owns the marketing taxonomy. Engagement value in particular is often the number a Sitecore marketing team has been reporting for years, and it does not survive the move — Optimizely can carry a number per event, but it has no concept of a cumulative visit score.

How Optimizely Web Experimentation Sits on a Sitecore Site

The snippet goes in the layout and runs in the browser, before paint. Sitecore's own tracking continues to run alongside it and continues to record goals server-side. Neither system knows about the other until you connect them, and the connection is one-directional: your page code tells Optimizely that a Sitecore goal was triggered.

flowchart TD
    A[Request for a Sitecore page] --> B[Layout renders, snippet in head]
    B --> C[Optimizely decides the variation before paint]
    C --> D[Variation applied to the rendered markup]
    D --> E[Visitor triggers a Sitecore goal]
    E --> F[Sitecore records the goal server-side in xConnect]
    E --> G[Page code pushes a matching Optimizely custom event]
    G --> H[Conversion attributed to the variation]
    F --> I[Sitecore reporting, unchanged]

Note that the goal is recorded twice, in two systems, from one visitor action. That is deliberate and it is the reason the reconciliation section below exists.

Prerequisites

  • The Optimizely Web Experimentation snippet URL for your project.

  • Access to the layout — the MVC layout .cshtml, or the head component in a headless XM Cloud front end.

  • An inventory of the goals you currently report on. Not all of them; the ones a decision has ever been made from.

  • Custom events created in Optimizely for each goal you intend to mirror, with API names noted. An event pushed under a name that does not exist in Optimizely is discarded without an error.

  • Agreement on where a test's content lives. A DOM variation is invisible to Sitecore authors, which is a real change to how content is governed.

Step 1: Add the Snippet to the Layout

Put the script first in <head> in the layout, above Sitecore's own tracking and above any bundled site JavaScript.

<head>
    <script src="https://cdn.optimizely.com/js/YOUR_PROJECT_ID.js"></script>
    @Html.Sitecore().VisitorIdentification()
</head>

Do not add async or defer. Sitecore pages are frequently heavy, and a deferred decision on a heavy page is the textbook case for visible flicker — the flicker guide covers what the visitor experiences and why the synchronous request is the cheaper trade.

On XM Cloud with a Next.js front end, the snippet belongs in the document head component rather than in a next/script tag, because every next/script strategy either defers execution or runs it after hydration.

Step 2: Map Sitecore Goals to Optimizely Custom Events

Sitecore goals are triggered server-side, most often by a controller action or a rule on a rendering. Optimizely cannot observe that. What you can do is push a matching custom event from the page that the goal's trigger produces — the thank-you view, the download handler's response page, the confirmation panel.

window['optimizely'] = window['optimizely'] || [];
window['optimizely'].push({
  type: 'event',
  eventName: 'brochure_downloaded',
  tags: { value: 1 },
  properties: {
    'Content Type': 'brochure',
    Source: 'product-page',
  },
});

Two Optimizely tag names are reserved. revenue must be an integer number of cents, so a value expressed in pounds has to be multiplied and rounded before it is sent — an unconverted 54.99 is discarded outright and the metric reports nothing. value is a plain number, and it is the closest thing Optimizely has to a Sitecore engagement value point.

Everything descriptive belongs in properties, and each property name has to exist in the Optimizely UI first: five predefined names plus ten custom slots, fifteen per event. A property sent under a name nobody created is dropped silently, exactly like a misspelled event name.

Where a goal is triggered without a page transition — a rule firing on an AJAX interaction, say — emit the Optimizely event from the same JavaScript that made the request, not from a DOMContentLoaded handler that will never run again.

Step 3: Reconcile the Two Sets of Numbers

Sitecore and Optimizely will report different totals for the same goal, permanently. This is not a defect and it should be explained once, in writing, to whoever will otherwise raise it every month.

Sitecore counts goals for every contact it tracks, including sessions that started before an experiment began, contacts on pages outside the experiment, and traffic Optimizely excludes as bot activity. Optimizely counts events only from visitors who were bucketed into a running experiment on a page carrying the snippet. The Optimizely number will be smaller, and the ratio between them should be roughly stable.

What matters is the stability, not the gap. A ratio that holds steady week to week means both systems are working. A ratio that drifts on one arm only is worth investigating, and the usual cause is a layout or a store view that never received the snippet — the Results page will typically surface it as a sample ratio warning first.

Before running the comparison at all, size the test properly on the sample size calculator. Sitecore estates often have lower traffic per template than the team expects, because the traffic is spread across many templates.

Gotchas

Sitecore personalisation and Optimizely targeting can both fire. If a rendering still carries a personalisation rule, some visitors receive different markup before Optimizely runs, and the variation's selector may not match for them. Retire the rule or scope the experiment to exclude those visitors.

Experience Editor loads the page too. Authors working in Experience Editor are real browser sessions and will bucket into experiments, and the editing chrome frequently breaks variation selectors. Exclude the editing host from targeting.

Preview and versioned content. A page with several versions renders whichever the workflow selects. A variation written against version 3's markup applies nothing on version 4, silently.

xConnect is not a browser API. There is no supported way to write the Optimizely variation onto the contact from page JavaScript. If you need the variation in Sitecore reporting, capture it in a form field or send it through your own endpoint to a server-side xConnect call.

Sitecore Engage batches its events. On XM Cloud front ends the Engage SDK queues browser events and sends them on its own schedule, so timings will not line up event-for-event with Optimizely's queue even when both are correct.

Verifying the Integration

Load a page and confirm in the console that window.optimizely is defined before Sitecore's visitor identification script has run. Then trigger one mirrored goal by hand and confirm two things independently: that the Optimizely queue received the push, and that Sitecore recorded the goal against the contact in the Experience Profile.

Then activate the experiment in draft and leave it for a day. Compare each arm's Optimizely conversion count against the Sitecore goal report for the same window and record the ratio somewhere durable. That recorded ratio is the artefact worth keeping: every future "the numbers do not match" conversation is answered by comparing today's ratio to it, rather than by re-litigating the architecture.