Skip to content

Commit

Permalink
Reorganize incubation repo to allow hosting multiple projects (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexnj authored May 31, 2023
1 parent 606987a commit efe0ad6
Show file tree
Hide file tree
Showing 21 changed files with 146 additions and 110 deletions.
104 changes: 26 additions & 78 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,88 +1,36 @@
# ab-worker-prototype
# Client A/B testing

A proof of concept implementation of the performant client-side A/B testing specification [as outlined in `EXPLAINER.md`](/EXPLAINER.md), using a CloudFlare Edge Worker.
Client A/B testing refers to the method of performing experimentation related
changes to a web application at runtime, typically within a browser.
This method of experimentation is popular in the industry as it is easier to
deploy, requires minimal to no engineering bandwidth for creating experiments
and is accessible to non-engineering personnel as well.

## Architecture
This incubation's objective is to devise methods of conducting the same outcome
with all its benefits offered, but without the performance penalties.

![architecture-diagram](/assets/images/arch.png)
## Goals

To implement a proof of concept, we will use a CloudFlare Worker to apply the `PRE_UA` transforms. Alternatively, this could be any compute node that can act as a proxy — a connect middleware for an Express.js frontend application, a proxy or an Edge at the boundaries of an Origin, or a CDN with compute capabilities (like CF workers). The proxy should be able to intercept a request to the origin, minimally parse and modify the HTML and apply the transformations.
* Standardize A/B transformation operations to a spec.
* Enable application of experimentation changes at the Origin,
an intermediary CDN/Edge/Proxy or the Browser.
* Minimize or eliminate performance metrics degradation caused by
application of changes in the browser through performant methods and
through exploration of browser-native implementations.

Internally, CloudFlare production is [said to](https://blog.cloudflare.com/html-parsing-1/) use [lol-html](https://github.com/cloudflare/lol-html), a low-latency HTML parser that supports streaming. A non-CloudFlare version of this prototype will have to employ a similar mechanism with same performance characteristics for this to function at scale.
## Explainers and presentations

For the A/B configuration provider, we will make a pseudo-API out of GitHub gists — this would allow us to test multiple sites and variations without redeploying the CloudFlare worker. This will also mimic the real world dependency of an API call federation off the Edge/CDN/Server. We will use stale-while-revalidate (SWR) policy for the configuration fetch, which should be acceptable for most websites’ real A/B tests.
* [Explainer and supported operations](specs/EXPLAINER.md)
* [WICG: A/B testing: updates - March 2023](https://docs.google.com/presentation/d/1WX-E63jL7ZwGf_jNszhfkdxsvzlXLdJdPMSTxK3X0A0/edit?usp=sharing)
* [PerfWG: Client-side A/B testing - March 2022](https://docs.google.com/presentation/d/1-cxHITwVtWJ5x3ev0__XzDtDtJn2cB9CAgN9Mkia3Ag/edit?usp=sharing)
* [Incubation proposal](https://github.com/WICG/proposals/issues/54)

In addition to that, the prototype edge also implements the following essential A/B testing functions:
* Figure out if an A/B test configuration is in place for the requested URI, via consulting the pseudo API.
* Randomly select the request into an experiment based on the configuration, and set a cookie to make the selection sticky.
## Directory structure

## Sequence diagram
* `specs/` folder ontains the latest specification and explainers.
* `sdks/` contains sdks, libraries, etc.
* `prototypes/` hosts current prototypes, demos and sdks.

![Sequence diagram](/assets/images/sequence.svg)
## Contributing

## Demo

For a demo, we're going to A/B test ToDoMVC, that is built using React as a client-side SPA.

The following A/B test configuration consists of transformations that create a randomized experiment for 50% of the traffic:
* Change background-color of the page to _`beige`_. Since this is best done on the static markup, we'd like to do have this transformation applied before UA.
* Change heading (H1) to "_a/b test h1_".
* Change the placeholder text of the text box to "_What would you like to do today?_"
* Color the first todo item _`red`_.

In addition, we'll also inject correct `<base>` tag to make relative URLs work, and `<link rel=canonical>` tag to avoid getting our prototype indexed as a duplicate.

```javascript
const experimentConfigJson = {
"control": {
"url": "https://todomvc.com/examples/react",
"cache": {
}
},
"variants": [
{
"weight": 0.5,
"url": "https://todomvc.com/examples/react",
"transformations": [
[1 /* PRE_UA */, "head", 3 /* OP_PREPEND */,
"<base href=\"https://todomvc.com/examples/react/\" target=\"_blank\">"],
[1 /* PRE_UA */, "head", 3 /* OP_PREPEND */,
"<link rel=\"canonical\" href=\"https://todomvc.com/examples/react/\" />"]
]
},
{
"weight": 0.5,
"url": "https://todomvc.com/examples/react",
"transformations": [
[1 /* PRE_UA */, "head", 3 /* OP_PREPEND */,
"<base href=\"https://todomvc.com/examples/react/\" target=\"_blank\">"],
[1 /* PRE_UA */, "head", 3 /* OP_PREPEND */,
"<link rel=\"canonical\" href=\"https://todomvc.com/examples/react/\" />"],
[1 /* PRE_UA */, "head", 4 /* OP_APPEND */,
"<style>body{background:beige!important}</style>"],
[2 /* ON_UA */, "h1", 0 /* OP_CUSTOM_JS */,
"$.innerHTML=\"a/b test h1\";"],
[2 /* ON_UA */, ".new-todo", 0 /* OP_CUSTOM_JS */,
"$.placeholder=\"What would you like to do today?\""],
[2 /* ON_UA */, ".todo-list>li", 0 /* OP_CUSTOM_JS */,
"$.style.color=\"red\""]
]
}
]
}
```

The prototype deployed on CloudFlare Edge can be accessed at:
* [Control](https://ab-worker.alexnj.workers.dev/?experiment=todomvc-v01.json&force=0)
* [Experiment](https://ab-worker.alexnj.workers.dev/?experiment=todomvc-v01.json&force=1)
* [Select one of them at random](https://ab-worker.alexnj.workers.dev/?experiment=todomvc-v01.json)

Configuration for running this experiment is [hosted as a gist](https://gist.github.com/alexnj/4c8d9198d16b238e4c7040250f052284#file-todomvc-v01-json).

### Performance comparison

#### WebPageTest

![Filmstrip](/assets/images/filmstrip.png)

[Comparison with more stats here](https://webpagetest.org/video/compare.php?tests=220128_AiDcKT_e71a33f6dd31af1157630f95377bbb4c,220128_BiDcH4_8af7080f26f4bc2f030ccce7e2695045).
Please see [How to Contribute](CONTRIBUTING.md).
File renamed without changes.
File renamed without changes.
88 changes: 88 additions & 0 deletions prototypes/cf-worker-prototype/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# ab-worker-prototype

A proof of concept implementation of the performant client-side A/B testing specification [as outlined in `EXPLAINER.md`](/EXPLAINER.md), using a CloudFlare Edge Worker.

## Architecture

![architecture-diagram](assets/images/arch.png)

To implement a proof of concept, we will use a CloudFlare Worker to apply the `PRE_UA` transforms. Alternatively, this could be any compute node that can act as a proxy — a connect middleware for an Express.js frontend application, a proxy or an Edge at the boundaries of an Origin, or a CDN with compute capabilities (like CF workers). The proxy should be able to intercept a request to the origin, minimally parse and modify the HTML and apply the transformations.

Internally, CloudFlare production is [said to](https://blog.cloudflare.com/html-parsing-1/) use [lol-html](https://github.com/cloudflare/lol-html), a low-latency HTML parser that supports streaming. A non-CloudFlare version of this prototype will have to employ a similar mechanism with same performance characteristics for this to function at scale.

For the A/B configuration provider, we will make a pseudo-API out of GitHub gists — this would allow us to test multiple sites and variations without redeploying the CloudFlare worker. This will also mimic the real world dependency of an API call federation off the Edge/CDN/Server. We will use stale-while-revalidate (SWR) policy for the configuration fetch, which should be acceptable for most websites’ real A/B tests.

In addition to that, the prototype edge also implements the following essential A/B testing functions:
* Figure out if an A/B test configuration is in place for the requested URI, via consulting the pseudo API.
* Randomly select the request into an experiment based on the configuration, and set a cookie to make the selection sticky.

## Sequence diagram

![Sequence diagram](assets/images/sequence.svg)

## Demo

For a demo, we're going to A/B test ToDoMVC, that is built using React as a client-side SPA.

The following A/B test configuration consists of transformations that create a randomized experiment for 50% of the traffic:
* Change background-color of the page to _`beige`_. Since this is best done on the static markup, we'd like to do have this transformation applied before UA.
* Change heading (H1) to "_a/b test h1_".
* Change the placeholder text of the text box to "_What would you like to do today?_"
* Color the first todo item _`red`_.

In addition, we'll also inject correct `<base>` tag to make relative URLs work, and `<link rel=canonical>` tag to avoid getting our prototype indexed as a duplicate.

```javascript
const experimentConfigJson = {
"control": {
"url": "https://todomvc.com/examples/react",
"cache": {
}
},
"variants": [
{
"weight": 0.5,
"url": "https://todomvc.com/examples/react",
"transformations": [
[1 /* PRE_UA */, "head", 3 /* OP_PREPEND */,
"<base href=\"https://todomvc.com/examples/react/\" target=\"_blank\">"],
[1 /* PRE_UA */, "head", 3 /* OP_PREPEND */,
"<link rel=\"canonical\" href=\"https://todomvc.com/examples/react/\" />"]
]
},
{
"weight": 0.5,
"url": "https://todomvc.com/examples/react",
"transformations": [
[1 /* PRE_UA */, "head", 3 /* OP_PREPEND */,
"<base href=\"https://todomvc.com/examples/react/\" target=\"_blank\">"],
[1 /* PRE_UA */, "head", 3 /* OP_PREPEND */,
"<link rel=\"canonical\" href=\"https://todomvc.com/examples/react/\" />"],
[1 /* PRE_UA */, "head", 4 /* OP_APPEND */,
"<style>body{background:beige!important}</style>"],
[2 /* ON_UA */, "h1", 0 /* OP_CUSTOM_JS */,
"$.innerHTML=\"a/b test h1\";"],
[2 /* ON_UA */, ".new-todo", 0 /* OP_CUSTOM_JS */,
"$.placeholder=\"What would you like to do today?\""],
[2 /* ON_UA */, ".todo-list>li", 0 /* OP_CUSTOM_JS */,
"$.style.color=\"red\""]
]
}
]
}
```

The prototype deployed on CloudFlare Edge can be accessed at:
* [Control](https://ab-worker.alexnj.workers.dev/?experiment=todomvc-v01.json&force=0)
* [Experiment](https://ab-worker.alexnj.workers.dev/?experiment=todomvc-v01.json&force=1)
* [Select one of them at random](https://ab-worker.alexnj.workers.dev/?experiment=todomvc-v01.json)

Configuration for running this experiment is [hosted as a gist](https://gist.github.com/alexnj/4c8d9198d16b238e4c7040250f052284#file-todomvc-v01-json).

### Performance comparison

#### WebPageTest

![Filmstrip](assets/images/filmstrip.png)

[Comparison with more stats here](https://webpagetest.org/video/compare.php?tests=220128_AiDcKT_e71a33f6dd31af1157630f95377bbb4c,220128_BiDcH4_8af7080f26f4bc2f030ccce7e2695045).
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit efe0ad6

Please sign in to comment.