# Course: A/B Testing
https://www.sanity.io/learn/course/a-b-testing

Understand the what, why, and how of A/B testing within Sanity Studio. Enable data-driven decision-making to lead to an improved product

---

## Navigation

## Contents

1. [Introduction to A/B Testing](https://www.sanity.io/learn/course/a-b-testing/introduction-to-a-b-testing) · [markdown](https://www.sanity.io/learn/course/a-b-testing/introduction-to-a-b-testing.md)
2. [Field Level Experimentation](https://www.sanity.io/learn/course/a-b-testing/field-level-experimentation) · [markdown](https://www.sanity.io/learn/course/a-b-testing/field-level-experimentation.md)
3. [Implementing an A/B test](https://www.sanity.io/learn/course/a-b-testing/implementing-an-a-b-test) · [markdown](https://www.sanity.io/learn/course/a-b-testing/implementing-an-a-b-test.md)
4. [A/B Testing quiz](https://www.sanity.io/learn/course/a-b-testing/a-b-testing-quiz) · [markdown](https://www.sanity.io/learn/course/a-b-testing/a-b-testing-quiz.md)

---

## Lesson 1: Introduction to A/B Testing
https://www.sanity.io/learn/course/a-b-testing/introduction-to-a-b-testing

Why would we want to A/B test, and how do we plan for an A/B test

In this course you will learn:



- What is A/B Testing

- How to A/B Test

- How to setup Field level experiments using `@sanity/personalization-plugin`

- Adding an experiment to a field

- Connecting an external service to get running experiments

- Getting data for a running experiment

- Running an experiment on a page.


## About the author



Hi, I'm Jon, a Senior Solutions Architect at Sanity.



I work with our customers to enable them to get the full value out of the Sanity platform. As part of my role I have been developing the `@sanity/personalization-plugin` and working with our customers on how to implement it.



Prior to joining Sanity, I was a customer and worked on implementing Sanity into new and existing projects. I made sure we were making data driven decisions by implementing A/B testing of new features, and helped content editors to test their changes. 



## What is A/B Testing



We often want to make changes to our content, and often this is because we think this change will help our website, app, or other platform perform better. Now this could just be a hunch, but we need to check that we are actually improving the platform. 



A/B testing is a method that has been used to test out hypothesis for over 100 years (although it might not have had that name) from things like medicine to farming, to advertising. An A/B test is a basic kind of randomized controlled experiment, where you compare two versions of something to figure out which performs better. There are many ways to measure performance of a version, from increased conversion rate, decreased bounce rate, scroll depth, retention rate, average order value, customer satisfaction. What you choose will depend on what you are testing, but you are essentially seeing if users who have viewed the variant have improved against your chosen metric compared with those that have viewed the control. A/B testing should help us to better understand our customers, make more effective choices and increase conversions.



A/B tests can be as simple as the choice of wording on a button (“buy now” vs “add to cart”), or it could be two completely separate versions of a page (this is split testing which often gets lumped in with A/B testing). A/B tests don't just need to have two versions of content. A/B/N testing has N number of versions of content that would all be included in the same experiment. 



It is now common practice for companies to run A/B testing on their digital platforms, with companies like Amazon, Facebook, and Google each conducting more than 10,000 experiments per year.  



## How to A/B Test



We need to start with a clear goal of what we want to measure, and a hypothesis of how we think we can make a change to this goal.



In order to work out if our test is a success we need to be collecting data that we want to track, this could be conversion rates, user engagement, or bounce rate, among others.



Once we have that we can create the versions of content we want to test (usually a control and a variant).



We assign a user to a group and show each group a version of the content or a page, and then use statistical analysis of the data we have collected to determine which version performs better. 



Normally we would assign the user at random but we need to be aware of other factors that may influence the results of your test. For example you may be testing the wording of a button but that button may only appear of the desktop version of your website.  In this case you might only split users by device first and then assign their group. 



Now we know a bit about A/B Testing, lets configure fields for experimentation using the `@sanity/personalization-plugin` . 



---

## Lesson 2: Field Level Experimentation
https://www.sanity.io/learn/course/a-b-testing/field-level-experimentation

Sanity has created a plugin that allows A/B/N testing experiments to individual fields. You can set the experiments and their variations as config or use an async function to return the information.

## Prerequisites



You should be able to follow this course if you have completed the Day One with Sanity Studio course. It will also build on the same studio, schema, and front end.



> [!TIP]
> Go to the [Day one content operations](https://www.sanity.io/learn/course/day-one-with-sanity-studio) course



### Import example content to your dataset



To save you from having to fill out and publish a bunch of content to make this course interesting, we have prepared a dataset that you can import and work on.



Download `production.tar.gz` below and import it into your Sanity project by running the following command in your studio folder:



- [ ] **Download** the dataset export

- [ ] **Run **the following command in the terminal to Import** **the dataset into your project's `production` dataset


```sh
npx sanity@latest dataset import ~/path/to/production.tar.gz production
```

A successful import will give you a bunch of artists, venues, and events in the past and future between 2010–2030.



![Image](https://cdn.sanity.io/images/3do82whm/next/583efc78ea24be55801fbea2b64b975a8e1782ab-2144x1388.png)

## Configure Field Level Experiments



You could create your own schema types to support A/B Testing from external sources, but Sanity has created a plugin that gives an opinionated way of handling A/B testing inside of the Sanity Studio.



We are going to add a new field to run an experiment on the name of an event. Our hypothesis is that more information in the name of an event will encourage our users to read more about it.



First you need to install and add the plugin `@sanity/personalization-plugin` to your Sanity Studio configuration



- [ ] **Run **the following command in the terminal to install the [Personalization Plugin](https://github.com/sanity-io/sanity-plugin-personalization)


```sh
npm install @sanity/personalization-plugin
```

When configuring the plugin you will need to specify which fields types to run experiments on, and the details of the experiments and their variants that we are running. 



- [ ] **Update** `sanity.config.ts` to configures the plugins field types and experiments


```typescript:sanity.config.ts
// ...all other imports
import {fieldLevelExperiments} from '@sanity/personalization-plugin'

export default defineConfig({
  // ... all other config settings
  plugins: [
    // ...all other plugins
     fieldLevelExperiments({
      // field types that you want to be able to emperiment on
      fields: ['string'], 
      // hardcoded experiments and variants
      experiments: [
        {
          id: 'event-name',
          label: 'Event Name',
          variants: [
            {
              id: 'control',
              label: 'Control',
            },
            {
              id: 'variant',
              label: 'Variant',
            },
          ],
        },
      ],
     })
  ],
})
```

Adding a field will create a new type that we can use in our schema types. This type will be prefixed with `experiment`. In this example the new type to use in the schema will be `experimentString`. With this we can add the a new Name experimentation field to the schema of our event.



- [ ] **Update **`eventType.ts` to add a `newName` field to schema of event


```typescript:schemaTypes/eventType.ts

export const eventType = defineType({
  name: 'event',
  title: 'Event',
  icon: CalendarIcon,
  type: 'document',
  groups: [
    {name: 'details', title: 'Details'},
    {name: 'editorial', title: 'Editorial'},
  ],
  fields: [
    defineField({
      name: 'name',
      type: 'string',
      group: ['editorial', 'details'],
    }),
    defineField({
      name: 'newName',
      type: 'experimentString',
      group: ['editorial', 'details'],
    }),
    // ... and the rest
```

You're adding a new field using the new `experimentString` type to ensure you don't accidentally clear the existing value.



This will render the new experimentation field with a default value input.



![Sanity Studio showing new experimentation field](https://cdn.sanity.io/images/3do82whm/next/c1948ee024a5f99d719bf6352a0171e79b8242cc-3454x2164.png)

Let's add content for our new field in the Studio:



- Add a default value to be used when the experiment is not running or if the user is not included in the experiment.

- Select add experiment on the field. 

- Select which experiment you want to add the content for. 

- Add content for the control 

- [ ] **Add** content for the A/B test






![Image](https://cdn.sanity.io/images/3do82whm/next/f0abfb2b5488f2ff66846c9bed25024fb9f8737e-1080x622.gif)

## Complex Field Types



When specifying the field types in the plugin config you can use Sanity Studio primitives, your own custom defined types, types from other plugins, or you can defined a type in the plugin config. You can add validation/options in the plugin config or on the schema definition.



```typescript:sanity.config.ts
// ...all other imports
import {defineArrayMember, defineConfig, defineField} from 'sanity'
import {fieldLevelExperiments} from '@sanity/personalization-plugin'

export default defineConfig({
   // ...all other config
  plugins: [
    // ... all other plugins
    fieldLevelExperiments({
      fields: [
        // primitives
        'string',
        'image',
        'slug',

        // custom defined types
        'path',

        // internationalized Array plugin types
        'internationalizedArrayString',

        // types defined in plugin config
        defineField({
          name: 'featuredAuthor',
          type: 'reference',
          to: [{type: 'author'}],
          hidden: ({document}) => !document?.title,
          validation: (Rule) => Rule.required()
        }),
        defineField({
          name: 'customArray',
          title: 'Custom Array',
          type: 'array',
          of: [defineArrayMember({type: 'object', fields: [{name: 'string', type: 'string'}]})],
        }),
      ],
      // experiements removed for brevity
    }),
  ],

})
```

```typescript:schemaTypes/articleType.ts
import {defineField, defineType} from 'sanity'

export const path = defineType({
  name: 'path',
  type: 'string',
  validation: (Rule) =>
    Rule.required().custom((value: string | undefined, context) => {
      if (!value) return true
      if (!value.startsWith('/')) return 'Must start with "/"'
      return true
    }),
})

export const article = defineType({
  name: 'article',
  title: 'Article',
  type: 'document',
  fields: [
    defineField({
      name: 'title',
      title: 'Title',
      type: 'experimentString',
    }),
    defineField({
      name: 'path',
      title: 'Path',
      type: 'experimentPath',
    }),
    defineField({
      name: 'image',
      title: 'Image',
      type: 'experimentImage',
    }),
    defineField({
      name: 'image',
      title: 'Image',
      type: 'experimentImage',
      validation: (Rule) =>
        Rule.custom((value: any) => {
          if (!value.default) return 'Must have a default image'
          if (value.active && (!value.variants || value.variants?.length === 0)) {
            return 'Must have at least one variant'
          }
          return true
        }),
    }),
    defineField({
      name: 'featuredArtist',
      title: 'Featured Artist',
      type: 'experimentFeaturedArtist',
    }),
    defineField({
      name: 'array',
      type: 'experimentCustomArray',
    }),
  ],
  preview: {
    select: {
      title: 'title',
      media: 'image',
    },
    prepare({title, media}) {
      return {
        title: title.default ? title.default : undefined,
        subtitle: title.variants
          ? title.variants.map((variant: any) => variant.value).join(' | ')
          : undefined,
        media: media?.default ?? undefined,
      }
    },
  },
})

```

## Getting experiments



You might consider using an external service to help with your A/B testing as they help with traffic allocation of your experiment on the frontend, connect with analytics platforms to give you information about your experiments, and they often have other features such as feature flagging and personalization. 



When choosing a service you need to consider if the service meets your needs,  what other tools might you need, and what the cost of the service will be. 



You can connect `@sanity/personalization-plugin`** ** to an external service by using an async function for getting your experiments and variants. This function will be passed a Sanity Client so you can use information stored in the Content Lake if needed, as in the example below.



```typescript:sanity.config.ts
import { getExperiments } from './utils/experiments'

export default defineConfig({
  // ... all other config
  plugins: [
    // ... all other plugins
    fieldLevelExperiments({
      // .. field types
      experiments: (client: SanityClient) => getExperiments(client),
    }
  ],

})
```

```typescript:utils/experiments.ts
import {SanityClient} from 'sanity'
import {ExperimentType} from '@sanity/personalization-plugin'

export const getExperiments = async (client: SanityClient) => {
  // secret is stored in the content lake using @sanity/studio-secrets
  const query = `*[_id == 'secrets.namespace'][0].secrets.key`
  const secret = await client.fetch(query)

  if (!secret) {
    return []
  }
  // call to external api to fetch experiments
  const response = await fetch('https://example.api/experiments', {
    headers: {
      Authorization: `Bearer ${secret}`,
    },
  })
  const {experiments: externalExperiments} = await response.json()

  // map and transform to get experiments and their variations
  const experiments: ExperimentType[] = externalExperiments?.map(
    (experiment: externalExperiment) => {
      const experimentId = experiment.id
      const experimentLabel = experiment.name
      const variants = experiment.variations?.map((variant) => {
        return {
          id: variant.variationId,
          label: variant.name,
        }
      })
      return {
        id: experimentId,
        label: experimentLabel,
        variants,
      }
    },
  )
  return experiments
}

```

In the following lesson you will get the data for the experiment and add it to your Next.js application.



---

## Lesson 3: Implementing an A/B test
https://www.sanity.io/learn/course/a-b-testing/implementing-an-a-b-test

How to query for data and how to setup an A/B test on a front end

Depending on how content is rendered will influence how you fetch data to be used on the page.



If your page is statically generated, changing its content is not achievable at build time. In this instance, you might want to apply a routing-based approach with middleware. 



For server-side rendering, pass the values for which variant to use as parameters to your query. This might require middleware to set the user group and store it in a cookie. 



For client-side rendering you can add the parameters to the fetch—or you can fetch all variants and then filter the results client-side.



To ensure a consistent experience for a user it is a good idea to set a cookie to store the value of the user group they have been assigned. 



## Server-side filtering, in the query



For filtering on the query it may look something like this: 



```groq
*[
    _type == "event" &&
    slug.current == $slug
  ][0]{
  ...,
  "name": coalesce(
    newName.variants[
    experimentId == $experiment 
    && variantId == $variant
  ][0].value,
    newName.default,
    name
  ),
  "date": coalesce(date, now()),
  "doorsOpen": coalesce(doorsOpen, 0),
  headline->,
  venue->
}
```

This query uses the `coalesce` function to get the correct variant based on the experiment and the variant. If those are not present or do not match you'll get the default value, and then fallback to the existing `name` field incase the data has not been migrated across. 



You will need to pass along values for the `$experiment` and `$variant` query params. These should come from an external service or a cookie set on the user.



## Client-side filtering, in JavaScript



If you're unable to perform filtering server-side in the query, you may instead query for all variants, and then write a function like this which will look through all of the returned variants and return only one.



```typescript
const getExperimentContent = (field, experimentId, variantId) => {
  return (
    field.variants.find(
      (variant) =>
        variant.experimentId === experimentId && 
        variant.variantId === variantId
    )?.value || field.default
  );
};

```

You also need to consider occasions where A/B tests are not running, and some users might be excluded from an experiment, so we need to ensure that we get a fallback value if this is the case. 



## Implementing A/B Tests



> [!NOTE]
> The following details how to implement A/B tests in Next.js, but the same principles and implementation patterns could be repeated in any framework.



Let's try implementing the A/B test created in the Studio in the Next.js application. For this you'll use the hardcoded experiment that was initially added.



In order to get a working A/B test you'll need some way of assigning an ID and group to a user—using middleware—and retrieving the variant that user should see. 



> [!TIP]
> Learn more about [Next.js middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) in their documentation.



You'll also need a way to track if a user has viewed an experiment. This tracking would typically be done on an external A/B testing service like Google analytics, Segment, or others that could be linked up to a A/B testing service. 



To be able to analyze the experiment we will need to know if a user has been included in an experiment and which variant they saw. What we can do is get the `userID` and `userGroup` and use this in a client component to send an event when the user has viewed the page with an experiment. 



- [ ] **Create** functions for getting variants, userId and setting user group


```typescript:src/lib/experiments.ts
import { v4 } from "uuid";
import { cookies } from "next/headers";
import type { NextRequest, NextResponse } from "next/server";
import MurmurHash3 from 'imurmurhash'

type Experiment = Record<
  string,
  { label: string; variants: { id: string; label: string }[] }
>;

const EXPERIMENTS: Experiment = {
  "event-name": {
    label: "Event Name",
    variants: [
      {
        id: "control",
        label: "Control",
      },
      {
        id: "variant",
        label: "Variant",
      },
    ],
  },
};

const getTestCookie = async () => {
  const cookieStore = await cookies();
  return cookieStore.get("ab-test")?.value;
};

export const getUserGroup = async () => {
  const testCookie = await getTestCookie();
  return testCookie ? JSON.parse(testCookie)?.userGroup : "control";
};

// mocking a fetch to an external service for getting an experiment variant
export const getExperimentValue = async (experimentName: string) => {
  const userGroup = await getUserGroup();
  return {
    variant: EXPERIMENTS[experimentName].variants.find(
      (variant) => variant.id === userGroup
    ),
  };
};

export const setCookiesValue = (
  request: NextRequest,
  response: NextResponse
) => {
  if (!request.cookies.has("ab-test")) {
     // create a user ID - this could be a logged in user id if known
    const userId = v4();
    // Deterministic variant from hash (same userId → same variant)
    const userGroup = MurmurHash3(userId).result() % 2 ? "control" : "variant";
    // Setting cookies on the response using the `ResponseCookies` API
    response.cookies.set("ab-test", JSON.stringify({ userGroup, userId }));
  }

  return response;
};

// If use is part of any experiments, get the tracking call data
// This is passed into the <Tracking> client component
export const getDeferredTrackingData = async (): Promise<
  | {
      userGroup: string;
      userId: string;
    }
  | undefined
> => {
  const testCookie = await getTestCookie();
  const data = testCookie ? JSON.parse(testCookie) : undefined;
  return data;
};

```

- [ ] **Create** middleware to intercept the page request and storing the user group and user id as a cookie


```typescript:src/middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { setCookiesValue } from "./lib/experiments";

export function middleware(request: NextRequest) {
  let response = NextResponse.next();
  response = setCookiesValue(request, response);

  return response;
}

export const config = {
  matcher: [
    /*
     * Match all request paths except for the ones starting with:
     * - api (API routes)
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico, sitemap.xml, robots.txt (metadata files)
     */
    "/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
  ],
};

```

A new component is required to send back events when a user has been assigned an ID and a group and has partaken in an experiment.



The example code below only logs these events to the console. You would need to replace this with the integration of your choice for measuring experiments.



- [ ] **Create** client component for sending tracking data


```typescript:src/components/tracking.tsx
"use client";

import { useEffect } from "react";

// Helper component to track experiment views from server components
export function Tracking({
  userGroup,
  userId,
}: {
  userGroup: string;
  userId: string;
}) {
  useEffect(() => {
    // TODO: track with Google Analytics, Segment, etc.
    console.log("Viewed Experiment, send tracking", {
      userGroup: userGroup,
      userId: userId,
    });
  }, [userId, userGroup]);

  return null;
}
```

Update the route for a single event to query for the correct variant as well as include the `Tracking` component.



- [ ] **Update** the single event page route


```tsx:src/app/events/[slug]/page.tsx
import { getExperimentValue, getDeferredTrackingData } from "@/lib/experiments";
import { Tracking } from "@/components/tracking.tsx";
import { client } from "@/sanity/client";
import { sanityFetch } from "@/sanity/live";
import imageUrlBuilder from "@sanity/image-url";
import { SanityImageSource } from "@sanity/image-url/lib/types/types";
import { defineQuery, PortableText } from "next-sanity";
import Image from "next/image";
import Link from "next/link";
import { notFound } from "next/navigation";

const EVENT_QUERY = defineQuery(`*[
    _type == "event" &&
    slug.current == $slug
  ][0]{
  ...,
  "name": coalesce(newName.variants[experimentId == $experiment && variantId == $variant][0].value, newName.default, name),
  "date": coalesce(date, now()),
  "doorsOpen": coalesce(doorsOpen, 0),
  headline->,
  venue->
}`);

const { projectId, dataset } = client.config();
const urlFor = (source: SanityImageSource) =>
  projectId && dataset
    ? imageUrlBuilder({ projectId, dataset }).image(source)
    : null;

export default async function EventPage({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;

  const { variant } = await getExperimentValue("event-name");
  const trackingData = await getDeferredTrackingData();

  const queryParams = {
    slug,
    experiment: "event-name",
    variant: variant?.id || "",
  };

  const { data: event } = await sanityFetch({
    query: EVENT_QUERY,
    params: queryParams,
  });

  if (!event) {
    notFound();
  }
  const {
    name,
    date,
    headline,
    image,
    details,
    eventType,
    doorsOpen,
    venue,
    tickets,
  } = event;
  const eventImageUrl = image
    ? urlFor(image)?.width(550).height(310).url()
    : null;
  const eventDate = new Date(date).toDateString();
  const eventTime = new Date(date).toLocaleTimeString();
  const doorsOpenTime = new Date(
    new Date(date).getTime() - doorsOpen * 60000
  ).toLocaleTimeString();

  return (
    <main className="container mx-auto grid gap-12 p-12">
      <div className="mb-4">
        <Link href="/">← Back to events</Link>
      </div>
      <div className="grid items-top gap-12 sm:grid-cols-2">
        <Image
          src={eventImageUrl || "https://placehold.co/550x310/png"}
          alt={name || "Event"}
          className="mx-auto aspect-video overflow-hidden rounded-xl object-cover object-center sm:w-full"
          height="310"
          width="550"
        />
        <div className="flex flex-col justify-center space-y-4">
          <div className="space-y-4">
            {eventType ? (
              <div className="inline-block rounded-lg bg-gray-100 px-3 py-1 text-sm dark:bg-gray-800 capitalize">
                {eventType.replace("-", " ")}
              </div>
            ) : null}
            {name ? (
              <h1 className="text-4xl font-bold tracking-tighter mb-8">
                {name}
              </h1>
            ) : null}
            {headline?.name ? (
              <dl className="grid grid-cols-2 gap-1 text-sm font-medium sm:gap-2 lg:text-base">
                <dd className="font-semibold">Artist</dd>
                <dt>{headline?.name}</dt>
              </dl>
            ) : null}
            <dl className="grid grid-cols-2 gap-1 text-sm font-medium sm:gap-2 lg:text-base">
              <dd className="font-semibold">Date</dd>
              <div>
                {eventDate && <dt>{eventDate}</dt>}
                {eventTime && <dt>{eventTime}</dt>}
              </div>
            </dl>
            {doorsOpenTime ? (
              <dl className="grid grid-cols-2 gap-1 text-sm font-medium sm:gap-2 lg:text-base">
                <dd className="font-semibold">Doors Open</dd>
                <div className="grid gap-1">
                  <dt>Doors Open</dt>
                  <dt>{doorsOpenTime}</dt>
                </div>
              </dl>
            ) : null}
            {venue?.name ? (
              <dl className="grid grid-cols-2 gap-1 text-sm font-medium sm:gap-2 lg:text-base">
                <div className="flex items-start">
                  <dd className="font-semibold">Venue</dd>
                </div>
                <div className="grid gap-1">
                  <dt>{venue.name}</dt>
                </div>
              </dl>
            ) : null}
          </div>
          {details && details.length > 0 && (
            <div className="prose max-w-none">
              <PortableText value={details} />
            </div>
          )}
          {tickets && (
            <a
              className="flex items-center justify-center rounded-md bg-blue-500 p-4 text-white"
              href={tickets}
            >
              Buy Tickets
            </a>
          )}
        </div>
      </div>
      {trackingData && (
        <Tracking
          userGroup={trackingData.userGroup}
          userId={trackingData.userId}
        />
      )}
    </main>
  );
}

```

With this done you will now see that a cookie is set when you visit an event page, and that based on that cookie and the content in your Sanity Studio the name of the event will show 1 of 3 options (the default if no experiment matches, the control for an experiment, or the variant). 



![Removing A/B Testing cookie](https://cdn.sanity.io/images/3do82whm/next/4b07bb035898101349a9f48f92c4d468a9da85be-1080x647.gif)

If you remove the cookie and refresh the page you will see a new cookie is added and there is a random chance it will be assigned a different group, thus potentially showing you a different name.



Let's test everything you've learned in the final lesson with a quiz.



---

## Lesson 4: A/B Testing quiz
https://www.sanity.io/learn/course/a-b-testing/a-b-testing-quiz

You have made it this far 🎉, lets have a quiz to put your A/B testing knowledge to the ... test.

> **Question:** What is A/B Testing?
>
> 1. A way to compare two versions of content to see which has the most page views
> 2. A way to compare two versions of content to see which performs better. **[correct]**
> 3. A way to convince your boss that your idea is better by showing them two versions of it and hoping they pick the one you want.
> 4. A way to pick at random what content you should use

> **Question:** What are common names for the A/B test versions?
>
> 1. Control and Comparison
> 2. Control and Benchmark
> 3. Benchmark and Variant
> 4. Control and Variant **[correct]**
> 5. Comparison and Variant

> **Question:** How do you assign a user to a group?
>
> 1. Assign them based on what you think they will prefer
> 2. First x see version 1, then next y see version 2
> 3. At random but aware of biases like screen size **[correct]**
> 4. By location

> **Question:** When using the fieldLevelExperiments plugin from @sanity/personalization-plugin what would be the name of the type to use for an experiment on a text field?
>
> 1. experimentText **[correct]**
> 2. testText
> 3. customText
> 4. textExperiment

> **Question:** When using a function to get experiments in the fieldLevelExperiments plugin, what is passed in?
>
> 1. current user
> 2. schema
> 3. sanity client **[correct]**

> **Question:** How do you ensure a consistent user experience when doing A/B testing?
>
> 1. Only run one experiment at a time
> 2. Only show one variant at a time
> 3. Add a cookie to store which variant a user sees **[correct]**
> 4. Ask the user what version they want to see





---

## Related Resources

- [All courses and lessons](https://www.sanity.io/learn/sitemap.md)
- [Complete content for LLMs](https://www.sanity.io/learn/llms-full.txt)
