Skip to main content

Basic Usage

After the init call, all the variants for all running experiments will be cached in memory. The init call should take a single-digit number of milliseconds on the server side. On the client side, it may take a little longer (it’s another roundtrip to the server) unless you pass the data directly in the HTML which makes it ready immediately. Any experiment that is evaluated and is not running will return Variant 0 (the control group).

Selecting a Treatment

info

To make sure the SDK is properly loaded before asking it for a treatment, block your code until the SDK is ready as shown below.

The ready() method returns a promise. You can also just check if it is currently ready with the isReady() method.

After the SDK is loaded you can use the treatment() method to return the proper treatment based on the experiment_name and the units data that was passed when the SDK was instantiated.

Then use an if-else-if-else block as shown below and insert the code for the different treatments that you plan to create.

context.ready().then(function () {
// A/B/C Test
if (context.treatment("experiment_name") == 1) {
// insert code to show on variant 1
} else if (context.treatment("experiment_name") == 2) {
// insert code to show on variant 2
} else {
// insert the control treatment code
}
});

// or using async/await
async function runExperiment() {
await context.ready();

// A/B Test
if (context.treatment("experiment_name")) {
// insert code to show on variant 1
} else {
// insert the control treatment code
}
}

Treatment Variables

Treatment variables are a powerful tool that can be used to automate your experiments. When creating an experiment on your ABsmartly Web Console, you can give each variant a set of variables. You can then use your context to pull the values of these variables into your code.

For example, let's say you have an experiment to find out what color of button generates the most clicks on your homepage. In this experiment, you have two variants - Variant 1 and Variant 2. On both of these variants, you have assigned a variable:

  • Variant 1: { "button.color": "green" }
  • Variant 2: { "button.color": "blue" }

And you wish for the control group (Variant 0) to have { "button.color": red }.

You can select these values in your code like so:

const defaultButtonColorValue = "red";

const buttonColor = context.variableValue(
"button.color",
defaultButtonColorValue
);

Config API

If you use configuration files to change different aspects of your application, then you can use the Config API to greatly improve your workflow and save a lot of coding time.

// Import the mergeConfig function.
import { mergeConfig } from "@absmartly/javascript-sdk";

/*

Your current config might be something like:

const myAppConfig = { ... };

or

const myAppConfig = getConfigFromFile(config.json);

then you just need to add the mergeConfig function like this:
*/

const myAppConfig = mergeConfig(getConfigFromFile(config.json));
Basic Example

Let's say you use a configuration file to change various parameters in your application:

.../src/homepageConfig.js
let cfg = {
button: {
color: "blue",
cta: "Click me",
},
hero_image: "http://cdn.com/img1.png",
some_other_stuff: {
/* ... */
},
};

Then you could use the Config API to run experiments that change those parameters.

Imagine you start two experiments:

  • button_color
    • Control group variables = { button.color: "blue" }
    • Variant 1 variables = { button.color: "green" }
  • homepage_cta
    • Control group variables = { button.cta: "Click me", hero_image: "https://cdn.com/img1.png" }
    • Variant 1 Variables = { button.cta: "Click here", hero_image: "https://cdn.com/img2.jpg" }

For each user the SDK receives a payload similar to this:

{
"guid": "dhsUiLJ7xgQBEbivw_0cjiKo9O6UlnSg",
"units": [],
"assignments": [
{
"name": "button_color",
"variant": 1,
"config": {
"button.color": "green"
}
},
{
"name": "homepage_cta",
"variant": 0,
"config": {
"button.cta": "Click here",
"hero_image": "http://cdn.com/img1.png"
}
}
]
}

This user is in Variant 1 of the button_color experiment and in the control group for the homepage_cta experiment. Calling context.mergeConfig(cfg) from the Javascript SDK would return a config object like this:

{
button: {
get color: () => { context.treatment("button_color"); return "green"; },
get cta: () => { context.treatment("homepage_cta"); return "Click here"; },
}
get hero_image: () => { context.treatment("homepage_cta"); return "http://cdn.com/img1.png"; },
some_other_stuff: { /* ... */ },
}
notice

When the config API is used, you don't need to call the treatment() method. It will be called automatically when keys from the config are used.

So you can continue using your configuration file as you were before, but now, the correct experiments will be triggered when a value is being overridden by said experiment.

This greatly simplifies the process of setting up experiments and cleaning up your code. If, at somepoint, a large part of the code is setup in this way you will be able to create different experiments without touching a single line of code.

Translations Example

Let's look at a slightly more complex example where you have to run an experiment whilst accomodating for multiple languages.

Here, we have a languagesConfig.js file:

.../src/languagesConfig.js
export default // Copy text for English and Dutch
{
header: {
en: "Our nice header!",
nl: "Onze leuk kop!",
},
call_to_action1: {
en: "Click here",
nl: "Klik hier",
},
};

In our code, we can then get all of our variant variables using:

// Get the keys of the variant variables from the context
const tagsToFetch = context.variableKeys();

const translationVariations = fetchTranslations(tagsToFetch);

The translationVariations variable would then be returned as something like:

{
"header_v1": {
"en": "Our beautiful header!",
"nl": "Onze mooie kop!"
},
"call_to_action1_v1": {
"en": "Continue",
"nl": "Doorgaan"
}
}

...which you could then merge with your original translations, like so:

import appTranslations from "../languagesConfig.js";

const translations = mergeConfig(appTranslations, translationVariations);

...which would return an object similar to:

{
get header: () => {
exp.treatment("experiment1");
return {
en: "Our beautiful header!",
nl: "Onze mooie kop!",
};
}
get call_to_action1: () => {
exp.treatment("experiment2");
return {
en: "Click here!",
nl: "Klik Hier"
};
}
}

You can now use this object to run your experiments whilst still supporting multiple languages!

caution

We recommend that control group does not have variables set on the Web Console except in very specific use cases. Usually, they should be set using the "default value" parameter as shown above.

You can then use this variable to style your button appropriately for the variant that the current user is on. The benefit of this is that you can now make more variants of this experiment on the Web Console and they will automatically be implemented in your application!

Peek at Treatment Variants

Although generally not recommended, it is sometimes necessary to peek at a treatment without triggering an exposure. The ABsmartly SDK provides a peek() method for that.

if (context.peek("exp_test_experiment") == 0) {
// user is in control group (variant 0)
} else {
// user is in treatment group
}

Overriding Treatment Variants

Warning

Overriden events are typed as ineligible and are ignored by the ABsmartly statistics engines. If you want to force a particular experiment's variant and have the event be counted, you can use the customAssignment methods instead, although this is not recommended.

During development, for example, it is useful to force a treatment for an experiment. This can be achieved with the override() and/or overrides() methods. The override() and overrides() methods can be called before the context is ready.

context.override("exp_test_experiment", 1); // force variant 1 of treatment
context.overrides({
exp_test_experiment: 1,
exp_another_experiment: 0,
});

Overriding Based On URL Params

The most common use case for overriding is to override a treatment based on params in the URL. This allows for greater flexibility in the development and QA stages. The following Javascript function can be ported to any language and used to parse the URL query parameters and return an object of overrides. This object can then be passed to the overrides() context method to force a particular variant for one or multiple experiment(s).

Prefixes

Here we using absmartly_ as a prefix for the query parameters, but you can use whatever prefix you like. It could be exp_, test_, or even a simple _!

function getABsmartlyOverridesFromQuery(req) {
const overrides = {};

// Iterate through all query parameters
for (const [key, value] of Object.entries(req.query)) {
// Check if the query parameter starts with "absmartly_"
if (key.startsWith('absmartly_')) {
// Extract the experiment name (remove "absmartly_" prefix)
const experimentName = key.slice(9);

// Convert the value to a number if possible, otherwise keep it as a string
const variantValue = isNaN(value) ? value : Number(value);

// Add to overrides object
overrides[experimentName] = variantValue;
}
}

return overrides;
}