Slide 1

Slide 1 text

Deploying client-side apps, 1,000 (or so) at a time Rebecca Murphey / CascadiaJS 2015

Slide 2

Slide 2 text

@rmurphey ~ rmurphey.com

Slide 3

Slide 3 text

this is a talk about how we’re rethinking the systems we 
 use to build and deploy client-side web applications 
 at Bazaarvoice this is not a talk about how we should all start building 
 and deploying client-side web applications

Slide 4

Slide 4 text

the scene ratings & reviews for brands & retailers SPA in OPP* single code base, multiple customers one build per site/locale combo lots of sites, lots of locales * Credit to Mike Pennisi for this way-cooler acronym for 3PJS.

Slide 5

Slide 5 text

what’s a build? verify API fetch config generate config make core app js make scout js make css embed font in css download images publish to s3 verify display publish scout file purge akamai probably some 
 other stuff i forgot

Slide 6

Slide 6 text

return util.format( 'grunt %s %s', args.join(':'), options.join(' ') );

Slide 7

Slide 7 text

how does it even no clear contract between steps primitive, blocking retries builds are lost for most failures no demand-based scaling of resources limited observability overall

Slide 8

Slide 8 text

what’s a build? a set of steps some serial, some parallel triggered by some initial input producing some final output always with the possibility of failure

Slide 9

Slide 9 text

what’s a step? a unit of work likely asynchronous possibly requiring input possibly depending on a service ideally producing output

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

{ steps : [ { name : 'step1', activityType : 'readMessageBody', input : 'message:workflow' }, { name : 'step2', activityType : 'randomNumber' }, { name : 'step3', activityType : 'email', input : 'step1:body,step2:number' } ] }

Slide 12

Slide 12 text

var Q = require('q'); var SES = new AWS.SES(); module.exports = function (input) { return Q.ninvoke(SES, 'sendEmail', { // ... Message : { Subject : { Data : 'A message from Pontiac' }, Body : { Text : { Data : input.number + '\n' + input.body } } } }); };

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

orchestration the automated arrangement, coordination, 
 and management of complex computer systems, middleware, and services

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

“The one thing about Simple Workflow is it can be really hard to reason about, because things can happen out of order.” — (approximately) Joe Pollard

Slide 20

Slide 20 text

“The one thing about Simple Workflow is it can be really hard to reason about, because things can happen out of order.” — (approximately) Joe Pollard

Slide 21

Slide 21 text

pontiac a tool for describing and performing a workflow, using SWF, where a workflow consists of 1. a set of steps, 2. information about the input 
 required for each step, 3. information about the ideal duration and
 concurrency of those steps, and 4. the strategy for retrying failed steps

Slide 22

Slide 22 text

chopper receive a build message fetch locales create new messages per locale delete original message

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

module.exports = { name : 'chopper', steps : [ { name : 'getDeploymentLocales', activityType : 'chopper!getDeploymentLocales', input : 'message:workflow', retries : 1, timeout : 10 }, { name : 'createChoppedMessages', activityType : 'chopper!createChoppedMessages', input : 'message:workflow,locales:getDeploymentLocales' }, { name : 'deleteMessage', activityType : 'shared!deleteMessage', input : 'message:workflow' } ] };

Slide 25

Slide 25 text

getDeploymentLocales module.exports = function (input, log) { var message = parseContents(input.message); var deploymentUrl = message.deploymentUrl; var deploymentRevision = message.deploymentRevision; var request = getDeploymentDoc({ v : deploymentRevision }, deploymentUrl); var promise = request(function (response) { return response.locales; }, function (err) { log.error(err, 'BHive request failed'); throw err; }); return promise; };

Slide 26

Slide 26 text

size of step output is limited and must be a string; so, we have to store large or binary data no guarantee that steps will run on the same machine; can’t rely on filesystem retries, concurrency are first class clear contract between steps framework abstracts known complexities adding a step is exactly as hard as writing the step itself, and no harder

Slide 27

Slide 27 text

reality check none of this is in production yet; rewriting a mission-critical build system that isn’t visibly broken is a hard sell planning to pilot this instead with teams that have more critical deployment issues good news: lots of common steps across projects, and sharing will be easy now

Slide 28

Slide 28 text

lessons / reminders grunt is tools are easy to abuse identify and abstract common complexity write all code as testable modules there is great power in convention smart people have probably already solved 
 [at least part of] your problem

Slide 29

Slide 29 text

thank you @rmurphey @ttl_podcast rmurphey.com ttlpodcast.com