Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

experiments

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

experiments - npm Package Compare versions

Comparing version 0.2.1 to 0.3.0

dist/experiments.js

6

bower.json
{
"name": "experiments",
"version": "0.2.1",
"main": "dist/ab.min.js",
"dependencies"" {
"version": "0.3.0",
"main": "dist/experiments.min.js",
"dependencies": {
"seedrandom": "2.3.4"
}
}

@@ -1,3 +0,3 @@

var Ab = require('./src/ab')
var Experiments = require('./src/experiments')
module.exports = Ab;
module.exports = Experiments;
{
"name": "experiments",
"version": "0.2.1",
"version": "0.3.0",
"author": "Matt Chadburn <matt.chadburn@ft.com>",
"description": "Simple split testing",
"contributors": [
"Matt Chadburn"
"Matt Chadburn",
"Guardian News & Media Ltd"
],
"repository": {
"type": "git",
"url": "git@github.com:commuterjoy/experiments.git"
},
"engines": {
"node": "*"
},
"keywords": [
"ab testing",
"split testing"
],
"dependencies": {

@@ -41,3 +50,3 @@ "seedrandom": "2.3.4"

},
"license": "MIT"
"license": "Apache-2.0"
}

@@ -1,45 +0,192 @@

A JavaScript AB testing framework, ported from [http://www.github.com/guardian/frontend](guardian/frontend).
A JavaScript AB testing framework, ported from
[http://www.github.com/guardian/frontend](guardian/frontend).
[AB testing's](http://en.wikipedia.org/wiki/A/B_testing) goal is to identify changes to web pages that increase or maximize an outcome of interest.
[AB testing's](http://en.wikipedia.org/wiki/A/B_testing) goal is to identify
changes to web pages that increase or maximize an outcome of interest.
### Goals
## Goals
- 100% client-side.
- Basic segmentation of audience.
- Deterministic segmentation - allowing allocation of users in to tests based on some external key (Eg, user name)
- 100% client-side
- Audience segmentation:-
- Basic - audience is segmented in to random buckets.
- Deterministic - allows predictable allocation of users in to tests based
on some external key (Eg, username)
- Fixed duration tests - that automatically close and delete their footprint.
- Isolation of each test audience - so a user can not accidently be in several tests at once.
- Agnostic of where the data is logged - most companies have their own customer data repoisitories.
- Minimal payload - ~1kb (minified + gzip) with no additional cookie overhead created.
- Isolation of each test audience - so a user can not accidentally be in several
tests at once.
- Agnostic of where the data is logged - most companies have their own customer
data repositories.
- Minimal payload - ~2kb (minified + gzip) with no additional cookie overhead
created.
### Experiment profiles
## Usage
Each AB test is represented by a JavaScript object describing the profile of the test to be undertaken.
Let's say we want to test the effectiveness of a button that says 'help' versus
one that says 'stuck?'. Our hypothesis is that, when a user sees an error
message, 2% more people will click 'help' than 'stuck?'.
To represent the test in code form we create a **profile** - a plain JavaScript
object with a unique test identifier.
```
var p = {
id: 'background', // A unique name for the test.
audience: 0.1, // A percent of the users you want to run the test on, Eg. 0.1 = 10%.
audienceOffset: 0.8, // A segment of the users you want to target the test at.
expiry: new Date(2015, 1, 1), // The end date of the test
variants: [ // An array of two functions - the first representing the control group, the second the variant.
var profile = {
id: 'help'
}
```
Next we need to create our two **variants** - 'help' and 'stuck'.
Each variant is represented by a JavaScript function, so we add them to the
profile. In this experiment we simply need to update the copy of two buttons in
the page, but tests can largely do whatever is needed - change the layout, load
in extra data, truncate bodies of text etc.
```
var profile = {
id: 'help',
variants: [
{
id: 'control',
id: 'help',
test: function () {
document.body.style.backgroundColor = '#ffffff';
$('.help-button').text('help');
}
},
{
id: 'pink',
id: 'stuck',
test: function () {
document.body.style.backgroundColor = '#c52720'; // this test turns the page background red
$('.help-button').text('stuck?');
}
}
],
canRun: function () { // Preconditions that all the test to run, or not
return true;
]
}
```
Now we need an **audience** for the test. Again, we define a property in our
profile. The profile defines the audience as a % of your total visitors, so if
we want 10% of people to participate in the test we can state an audience of
'0.1', or for 5%, an audience value of '0.05'.
```
var profile = {
id: 'help',
audience: 0.1, // ie. 0.1 = 10%
variants: [
...
]
}
```
Often we want to run several tests simultaneously and be confident that
variations in one test aren't influencing a second. To avoid this overlap we
use the idea of a **audience offset**.
Each user in a test is assigned a persistent integer, evenly distributed from 1
to 1,000,000, and we allocate blocks of these users to individual tests.
For example, a profile with an audience of 10% and an offset of 0.3 will
allocate all the users with an identifier in the range 300,000 to 400,000 to
the test and split the variants evenly between this group of people.
```
var profile = {
id: 'help',
audience: 0.1,
audienceOffset: 0.3,
variants: [
...
]
}
```
In the profile described above we would expect,
- 5% of people to be allocated to the 'help' group
- 5% of people to be allocated to the 'stuck' group
- 90% of people to not be in the test.
The later group can obviously be selected for other tests.
A good AB test runs for a fixed period of time. For this reason each test
profile must have an **expiry** date, represented as JavaScript Date object.
If the date is the past the test framework will refuse to execute the test.
```
var profile = {
id: 'help',
expiry: new Date(2015, 1, 1) // ie. runs until 1 Jan, 2015
...
}
```
Lastly we might want to scope the test to an particular context. For example,
our help test should not be executed on pages that do not produce error
messages.
The **canRun** function is evaluated each time the test is loaded and decides
if the test should be run.
```
var profile = {
id: 'help',
canRun: function () {
return (page.config.type === 'article');
}
...
}
```
With our profile we can now instantiate a test, segment the audience and run it.
```
var help = new Experiments(profile).segment().run();
```
### Events
Each experiment will at least need to track the number of participants and
number of successful conversions.
The framework itself does not do this, but provides events for each experiment
to listen out for and hook their tracking code to.
The events are fired on `document.body` and contain the following `event.detail`.
#### experiemnts.{profile.id}.start
- variant - the segment the user has been assigned to.
#### experiemnts.{profile.id}.complete
_None_.
### Seeding
Often the goal of an AB test is to track a users behaviour over a period of
time - typically this will mean collecting data over several sessions across
several devices.
Purely client-side test frameworks make this hard to do as cookies and
localStorage are somewhat volatile and, more crtically, local to a web browser
on a single device.
One approach to solving this problem is using [psuedorandom number
generators](https://github.com/davidbau/seedrandom).
These are deterministic algorithms that can, given the same input, generate an
evenly distributed set of numbers, which we can fairly segment on.
For example, lets say we have user ID of 'UID=314146' stashed in a cookie. We
can extract that and pass it to the framework to repeatedly bucket the user in
to the same ID and same test variant, Eg.
```
var k = getCookie('UID').split('=')[1]; // k = 314146
new Experiments(profile, { seed: key }).segment().run();
```
This is very useful when testing people across different devices over long
periods of time and, helpfully, independent of needing a persistance layer
beyond any current sign-in system.
## Demo

@@ -51,20 +198,23 @@

With developer tools, we can feed the above profile in to the AB test framework, force our variant to '_pink_', then _run_ the test.
With developer tools, we can feed the above profile in to the AB test
framework, force our variant to '_pink_', then _run_ the test.
```
var a = new Ab(p, { variant: 'pink' })
a.run();
var a = new Experiments(p, { variant: 'pink' }).run()
```
You should see the page background turn pink, and running the test on every subsequent visit will turn the page pink until the test has expired.
You should see the page background turn pink, and running the test on every
subsequent visit will turn the page pink until the test has expired.
Allocate yourself in to the control group and re-run the test and the background should turn white.
Allocate yourself in to the control group and re-run the test and the
background should turn white.
```
var a = new Ab(p, { variant: 'control' })
a.run();
var a = new Experiments(p, { variant: 'control' }).run();
```
For the duration of the test we can track the data of that user (say, pages per visit or scroll depth) and compare with the control group to see if that variant had the positive impact we thought it would have.
For the duration of the test we can track the data of that user (say, pages per
visit or scroll depth) and compare with the control group to see if that
variant had the positive impact we thought it would have.

@@ -75,6 +225,7 @@ ### Segmentation

Firstly, each test subject is allocated a persistant id (an integer) that is shared across tests.
Firstly, each test subject is allocated a persistant id (an integer) that is
shared across tests.
```
localStorage.getItem('ab__uid'); // Eg, "3467"
localStorage.getItem('ab__uid');
```

@@ -85,15 +236,14 @@

```
localStorage.getItem('ab__background'); // Eg, '{"id":"background","variant":"pink"}'
localStorage.getItem('ab__background');
```
In the real world we want the test subjects allocated randomly in to a variants (or excluded from the test), so we don't specify the variant in the _Ab_ constructor and invoke `segment()` instead, before running the experiment,
In the real world we want the test subjects allocated randomly in to a variants
(or excluded from the test), so we don't specify the variant in the _Experiments_
constructor and invoke `segment()` instead, before running the experiment,
```
var a = new Ab(profile);
a.segment();
a.run();
var a = new Experiments(profile).segment().run();
```
The `segment()` function decides if a user should be in the test, and, if they are, splits the audience between a 'control' group and a number of 'variants'.
Segmentation is fairly trivial at the moment, but later it can be used to target certain types of users (Eg, every international user who has visited more than 3 times a week, or persona x).
The `segment()` function decides if a user should be in the test, and, if they
are, splits the audience between a 'control' group and a number of 'variants'.

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc