Skip to main content

Swab: Simple WSGI A/B testing

Project description

Simple WSGI A/B testing - Swab

What is A/B testing?

A/B testing is a way of comparing two versions of a web page against each other, to see which performs best for your visitors. It could be testing changes to your website copy, visual design or user interface.

When you run an A/B test experiment you need to tell Swab what variants you have, and what goals you want to optimize for. Swab will then randomly assign visitors to each variant and keep track of how many times each variant is shown, along with how many of those visits resulted in a conversion.

Using this data, Swab can show you the conversion rate for each variant along with some basic statistics to help you decide whether there is a meaningful difference between the versions.

Setting up a Swab instance

Swab needs a directory where it can save the data files it uses for tracking trial and conversion data:

from swab import Swab
s = Swab('/tmp/.swab-test-data')

Then you need to tell swab about the experiments you want to run, the variants available and the name of the conversion goal:

s.add_experiment('button-color', ['red', 'blue'], 'signup')

Finally you need to wrap your WSGI app in swab’s middleware:

application = s.middleware(application)

Integrating swab in your app

Swab makes a number of functions available to you that you can put in your application code:

show_variant(environ, experiment, record=False, variant=None)

Return the variant name to show for the current request. In the above example, a call to show_variant(environ, 'button-color') would return either 'red' or 'blue'

record_trial_tag(environ, experiment)

Return the HTML tag for a javascript beacon that should be placed in the page you are testing. The tag causes the user’s browser to load a referenced javascript file, triggering swab to record a trial for the given experiment.

If you only have a single experiment running on the requested page and have previously called show_variant you can safely omit the experiment name.

record_trial(environ, experiment)

If you don’t want to use the javascript beacon to track trials, you can call record_trial directly. The javascript beacon method is preferred as it is unlikely to be triggered by bots.

If you only have a single experiment running on the requested page and have previously called show_variant you can safely omit the experiment name.

record_goal(environ, goal, experiment)

Record a goal conversion for the named experiment

Viewing results

Test results are available at the URL /swab/results.

Caching

Swab automatically adds a Cache-Control: no-cache response header if show_variant or record_trial was called during the request. This helps avoid proxies caching your test variants. It will also remove any other cache related headers (eg ‘ETag’ or ‘Last-Modified’). If you don’t want this behaviour, you need to pass cache_control=False when creating the Swab instance.

Viewing the variants

To test your competing pages append ‘?swab.<experiment-name>=<variant-name>’ to URLs to force any given variant to be shown.

Basic design

Each visitor is assigned an identity which is persisted by means of a cookie. The identity is a base64 encoded randomly generated byte sequence. This identity is used as a seed for a RNG, which is used to switch visitors into test groups.

Every time a test is shown, a line is entered into a file at <datadir>/<experiment>/<variant>/__all__. This is triggered by calling record_trial

Every time a goal is recorded (triggered by calling record_goal), a line is entered into a file at <datadir>/<experiment>/<variant>/<goal>

Each log line has the format <timestamp>:<identity>\n.

No file locking is used: it is assumed that this will be run on a system where each line is smaller than the fs blocksize, allowing us to avoid this overhead. The lines may become interleaved, but there should be no risk of corruption even with multiple simultaneous writes. See http://www.perlmonks.org/?node_id=486488 for a discussion of the issue.

0.2.4

  • Add support for Python 3.12

  • Drop support for Python 3.8

0.2.3 (released 2024-05-02)

  • Bugfix: fix cookie max-age value

0.2.2 (released 2018-02-23)

  • Bugfix: fix for exception triggered when a bot visits a page containing record_trial_tag

0.2.1 (released 2018-02-23)

  • Bugfix: fixed link rendering on test results page

0.2.0 (released 2018-02-23)

  • Compatibility with python 3

  • Allow the application to force a variant when calling show_variant

  • Improved JS snippet no longer blocks browser rendering

  • No longer records duplicate trials if show_variant is called twice

  • Allow experiments to customize the swabid generation strategy - useful if you want to deterministically seed the RNG based on some request attribute.

  • Allow weighted variants: add_experiment('foo', 'AAAB') will show variant A 75% of the time.

  • Include bayesian results calculation based on http://www.evanmiller.org/bayesian-ab-testing.html#binary_ab_implementation

  • Better caching: only sets cookies on pages where an experiment is invoked

  • record_trial_tag can now infer the experiment name from a previous call to show_variant: less duplicated code when running an experiment.

  • Results now show results per visitor by default

Version 0.1.3

  • Added a javascript beacon to record tests (helps exclude bots)

  • Better exclusion of bots on server side too

  • Record trial app won’t raise an error if the experiment name doesn’t exist

  • Removed debug flag, the ability to force a variant is now always present

  • Strip HTTP caching headers if an experiment has been invoked during the request

  • Improved accuracy of conversion tracking

  • Cookie path can be specified in middleware configuration

Version 0.1.2

  • Minor bugfixes

Version 0.1.1

  • Bugfix for ZeroDivisionErrors when no data has been collected

Version 0.1

  • Initial release

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

swab-0.2.3-py3-none-any.whl (18.9 kB view details)

Uploaded Python 3

File details

Details for the file swab-0.2.3-py3-none-any.whl.

File metadata

  • Download URL: swab-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 18.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.11.10

File hashes

Hashes for swab-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 e49715c371a8c8fc37c5c0d9fe46c373292a7beb3e1ffef982822f806791e30e
MD5 36f9a45ac8bf95e7c7a65dba445459fd
BLAKE2b-256 14944ca87d1b21df608a39ac1a1967e642e07eeae8e6ad4b11f78323dd032ef3

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page