[NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

62
Scaling A/B testing on Netflix.com with _________ Alex Liu @stinkydofu

description

At Netflix we run hundreds of A/B tests every year. Maintaining multivariate experiences quickly adds strain to any UI engineering team. In this talk, Alex Liu explores the patterns we’ve built in Node.js to tame this beast - ultimately enabling quick feature development and rapid test iteration on our service used by over 50 million people around the world. This is the condensed 20 minute version given at NodeConfEu 2014.

Transcript of [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

Page 1: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

Scaling A/B testing on Netflix.com with

_________Alex Liu @stinkydofu

Page 2: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

data driven product development

Page 3: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js
Page 4: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js
Page 5: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js
Page 6: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

A

B

C

D

E

F

G

A

B

C

D

E

F

G

A

B

C

D

E

F

G

A

B

C

D

E

F

G

A

B

C

D

E

F

G

A

B

C

D

E

F

G

A

B

C

D

E

F

G

Test 1 Test 2 Test 3 Test 4 Test 5 Test 6 Test 7

Page 7: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

2,097,152 unique experiences across seven tests

Page 8: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

hundreds of new A/B tests per year

Page 9: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

433518929550349486086117218185493567650…72061153709996

Page 10: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

2105 566 685templates CSS JS

Page 11: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

2.5M unique packages every week

Page 12: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

problem: conditional dependencies

Page 13: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

Packaging

Page 14: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js
Page 15: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

oldSearch

app.js

newSearch

dep1 dep2 dep3 dep4 dep5

sub-dep sub-depsub-dep sub-dep sub-dep sub-dep

Page 16: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

oldSearch

app.js

newSearch

dep1 dep2 dep3 dep4 dep5

sub-dep sub-depsub-dep sub-dep sub-dep sub-dep

Page 17: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

app.js

import jquery from 'jquery'; import oldSearch from 'oldSearch'; import newSearch from 'newSearch';

export ...

Page 18: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

oldSearch

app.js

newSearch

dep1 dep2 dep3 dep4 dep5

sub-dep sub-depsub-dep sub-dep sub-dep sub-dep

Page 19: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js
Page 20: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

685 files…?

2.5M packages…?

Page 21: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

oldSearch

app.js

newSearch

dep1 dep2 dep3 dep4 dep5

sub-dep sub-depsub-dep sub-dep sub-dep sub-dep

Page 22: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

problem: conditional dependencies

Page 23: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js
Page 24: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

requestbuild

Page 25: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

require('nf-include-when')

Page 26: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

/* * @includewhen rule.notInNewSearch */

oldSearch.js

Page 27: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

/* * @includewhen rule.inNewSearch */

newSearch.js

Page 28: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

var Rule = require('nf-rule-infrastructure'), inNewSearch;

inNewSearch = new Rule('inNewSearch', function(context, cb) { var test = context.abtests.get(1534); cb(test && test.cell(1)); });

module.exports = inNewSearch;

anatomy of a rule

Page 29: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

require('nf-asset-registry')

Page 30: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

import jquery from 'jquery'; import oldSearch from 'oldSearch'; import newSearch from 'newSearch';

export ...

app.js

Page 31: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

newSearch.js

jquery

oldSearch.js

app.js

registry

Page 32: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

"app.js": { "deps": [ "jquery", "oldSearch.js", "newSearch.js", ], "depsFull": [ "jquery", "oldSearchDep2.js", "oldSearchDep1.js", "oldSearch.js", "newSearchDep2.js", "newSearchDep1.js", "newSearch.js" ], "fileSize": "4.41 kB", "fileSizeFull": "120.52 kB" }

Page 33: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

"newSearch.js": { "rule": "inNewSearch", "deps": [ "jquery", "newSearchDep2.js", "newSearchDep1.js", ], "depsFull": [ "jquery", "newSearchSubDep3.js", "newSearchSubDep2.js" "newSearchSubDep1.js" "newSearchDep2.js", "newSearchDep1.js" ], "fileSize": "10.41 kB", "fileSizeFull": "40.52 kB" }

nf-include-when

Page 34: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

require('nf-packager')

Page 35: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

var packager = require('nf-packager'), includeWhen = require('nf-include-when'), registries = require('nf-asset-registry');

function getScriptUrl() return packager.getPackageDefinition('app.js', registries, includeWhen); }

Page 36: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

"app.js": { "deps": [ "jquery", "oldSearch.js", "newSearch.js", ], "depsFull": [ "jquery", "oldSearchDep2.js", "oldSearchDep1.js", "oldSearch.js", "newSearchDep2.js", "newSearchDep1.js", "newSearch.js" ], "fileSize": "4.41 kB", "fileSizeFull": "120.52 kB" }

Step 1: Get the full dependency tree for the requested package from the registry.

Page 37: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

[ "jquery", /* no rule */ "oldSearchDep2.js", /* no rule */ "oldSearchDep1.js", /* no rule */ "oldSearch.js", /* rules.notInNewSearch */ "newSearchDep2.js", /* no rule */ "newSearchDep1.js”, /* no rule */ "newSearch.js" /* rules.inNewSearch */ ]

Step 2: Determine which files have rules.

Page 38: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

[ "jquery", /* no rule */ "oldSearchDep2.js", /* no rule */ "oldSearchDep1.js", /* no rule */ "oldSearch.js", /* rules.notInNewSearch */ "newSearchDep2.js", /* no rule */ "newSearchDep1.js”, /* no rule */ "newSearch.js" /* rules.inNewSearch */ ]

Step 3: Run the rules. Filter out all deps that resolved false.

Page 39: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

[ "jquery", /* no rule */ "oldSearchDep2.js", /* no rule */ "oldSearchDep1.js", /* no rule */ "oldSearch.js", /* rules.notInNewSearch */ "newSearchDep2.js", /* no rule */ "newSearchDep1.js”, /* no rule */ "newSearch.js" /* rules.inNewSearch */ ]

Step 4: Filter out all extraneous sub deps.

Page 40: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

Step 5: Concatenate the files.

[ "jquery", /* no rule */ "newSearchDep2.js", /* no rule */ "newSearchDep1.js”, /* no rule */ "newSearch.js" /* rules.inNewSearch */ ]

Page 41: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

buildjavascript

registry

Page 42: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

request registry

rulespackager

Page 43: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

Bonus Round

Page 44: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

be creative with the registry

Page 45: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

"account/bb/models/ratingHistoryModel.js": { "rule": null, "deps": [...], "depsFull": [...], "depsCount": { "underscore": 2, "backbone": 1, "jquery": 2, "common/requirejs-plugins.js": 4, "requirejs-text": 4, "utils/contextData.js": 1, "common/nfNamespace.js": 1 }, "hash": "dd23b163", "fileSize": "1.21 kB", "fileSizeFull": "173.04 kB" }

dependency counting

dependency pruning

file sizes

Page 46: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

@import (reference) "/common/_nf_defs.less"; @import (reference) "/member/memberCore.less"; @import (reference) "/components/menu.less"; @import (reference) "/components/breadcrumbs.less";

@import modules

Page 47: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

"account/containerResponsive.css": { "rule": null, "deps": [...], "depsFull": [...], "depsCount": [...], "hash": "65a431f3", "fileSize": "709 B", "fileSizeFull": "709 B", "css": { "selectors": 8, "declarationBlocks": 6, "declarations": 17, "mediaQueries": 3 } }

css analysis

Page 48: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

the

best part

Page 49: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

"cache": { "account/pin.js": "define('account/pin.js', ['member/memberC…", "account/bb/models/changePlanModel.js": "define('account/b…", "account/bb/models/ratingHistoryModel.js": "define('account…", "account/bb/models/viewingActivityModel.js": "define('account…", "account/bb/views/changePlanView.js": "define('account/bb/vi…", "account/bb/views/changePlanView.js": "define('account/bb/vi…", "account/bb/views/emailSubView.js": "define('account/bb/views…", "account/bb/views/viewingActivityView.js": "define('account…", "common/UITracking.js": "define('common/UITracking.js, ['me…", "common/UITrackingOverlay.js": "define('common/UITrackingOve…", … … …

Page 50: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

css

mappings

javascript

templates templates

mappings

javascript

css

Page 51: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

templates

mappings

javascript

css

UI Bundle

Page 52: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js
Page 53: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

deploy UI bundles

anytime

Page 54: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

never touch the file system

Page 55: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

< 5ms package response times

Page 56: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

Takeaways▶ leverage the server ▶ static analysis FTW ▶ divide and conquer with modules

Page 57: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

Our Learnings

Page 58: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

learn by doing

Page 59: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

fail fastmove faster

Page 60: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

“I have not failed.I’ve just found 10,000 waysthat won’t work.”

Thomas Edison

Page 61: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

simplify

Page 62: [NodeConf.eu 2014] Scaling AB Testing on Netflix.com with Node.js

thanks! come find us!

Alex Liu @stinkydofu

Chris Saint-Amant @csaintamant

Micah Ransdell @mjr578

Kris Baxter @kristoferbaxter