Was this page helpful?

A/B Testing with Contentful and Optimizely

Overview

This tutorial gives an overview of an end-to-end experimentation setup with Contentful, Optimizely and the Optimizely App. The goals of the setup are:

  • Separation of concerns: Controlling content in Contentful and experiments in Optimizely
  • Speed: Server side selection of variations for fast delivery and to avoid "flash of content"
  • No development dependencies: Enable server side experimentation without pushing new code

The following areas will be covered:

  • Setup of the Optimizely app in Contentful
  • Selection of content variations from the Contentful and Optimizely APIs
  • Rendering of experiments in a React application

Optimizely

Optimizely is an experimentation platform that integrates with your content to produce powerful insights on all your apps powered by Contentful.

In the following tutorial you will walk through how to set up a simple application which allows for A/B testing using content from Contentful.

Requirements to get started

  • An Optimizely account with a "Full Stack" experiment.
  • The Optimizely app installed on your space. See the installation instructions.

How the Optimizely App changes the Contentful API response

The Optimizely app creates a new content type: the variation container. A variation container is a custom Contentful content type which consists of two or more values inside a content model that pertain to the same reference field. Let's look at the following example:

In our example without Optimizely, we have a content model with a reference field to hold "blocks". Notice the CTA is "Buy Now!":

Static content model

Fig 1.1

Using Optimizely, the same content model is transformed. Where we originally received the CTA of "Buy Now!", we now are receiving a variation container holding all the different possible CTAs:

Experimental content model

Fig 1.2

The variation container is simply a content type that nests the possible values for CTA.

Leveraging Optimizely to pick the right variation

We are going to create a pseudo-backend which proxies the Contentful API response and uses the Optimizely SDK to determine which variant to use. We will then reformat the response based on this decision.

This example will use Javascript; however, the SDK is offered in multiple languages on the Optimizely developer docs.

The Optimizely client allows us to determine which variant we should use based on an identifier for the current user. In this example, we are passing userId to determine which variant this user should see from the ctaVariants experiment.

import Optimizely from 'optimizely';
import datafile from './optimizelyDataFile';
import { getUser } from 'user';

const optimizelyClient = new Optimizely({ datafile });

const user = getUser();

const variation = optimizelyClient.activate('ctaVariants', user.userId);

// variation => 'cta_b'

ctaVariants contains two possible variants, a control group called cta_a and a test group called cta_b. In the example code above, Optimizely has determined this user should see variant cta_b.

Let's look at the variation container JSON response. Notice that the content type exposes a meta and variations property which lists all possible variations.

{
    "sys": {
    "space": { ... },
    "id": "41nZggHEplcBOsrPXLOEU",
    "type": "Entry",
    "createdAt": "2019-07-17T14:32:24.306Z",
    "updatedAt": "2019-07-17T14:32:24.306Z",
    "environment": { ... },
    "revision": 1,
    "contentType": {
        "sys": {
        "type": "Link",
        "linkType": "ContentType",
        "id": "variationContainer"
        }
    },
    "locale": "en-US"
    },
    "fields": {
    "experimentTitle": "CTA Variation Experiment",
    "experimentId": "15249150297",
    "meta": {
        "cta_a": "2hoYOxnZVV3Imvry1DhmtG",
        "cta_b": "5IlB5PCsca3zZnzlVd7WvI"
    },
    "variations": [
        {
        "sys": {
            "type": "Link",
            "linkType": "Entry",
            "id": "2hoYOxnZVV3Imvry1DhmtG"
        }
        },
        {
        "sys": {
            "type": "Link",
            "linkType": "Entry",
            "id": "5IlB5PCsca3zZnzlVd7WvI"
        }
        }
    ],
    "experimentKey": "cta-experiment"
    }
}
You must publish the content inside a variation container for it to be exposed in the API response.

cta_a and cta_b are variations of this experiment and they point to a Contentful content type which holds the actual content we want to show. Let's add to our backend code to get the variation:

import Optimizely from 'optimizely';
import datafile from './optimizelyDataFile';
import { getUser } from 'user';
import sdk from 'contentful-sdk';

const optimizelyClient = new Optimizely({ datafile });

const user = getUser();

// we are using the id of the variation container from the JSON sample above
const variationEntry = await sdk.getEntry('41nZggHEplcBOsrPXLOEU');

const variation = optimizelyClient.activate('ctaVariants', user.userId);

// variation => 'cta_b'

const ctaEntryId = variationEntry.meta[variation];

// ctaEntryId => '5IlB5PCsca3zZnzlVd7WvI'

We now have the entryId of the CTA content block we want to display! This means that the content we should show is the "20% off!" CTA from Fig 1.2.

Important notes about the example

In this example we used userId. Optimizely requires that you identify the current user so they can determine which experiment group to place them into. This ID should stick with the user and properly identify them throughout different sessions. If you use a random number as an ID, your A/B test will not work correctly and the results will be meaningless.

A datafile was used in the example above to create the Optimizely client. This is supplied by Optimizely and will be unique to your account. You can read more about the data file here.

Only published content on Contentful will be exposed in the variation container. Using a Contentful environment, you can test how a variation container works before promoting your experiment to production.

Conclusion

Concepts covered:

  • The definition of a variation container.
  • How a variation container is returned in the Contentful API response.
  • How to leverage Optimizely, using their SDK, to transform a variation container into the correct display field for your presentation layer.