Β© 2015 Gideon de Villiers Used with permission

Node.js

At Disney?

by

Adam Eivy \[._.]/
  @antic

updated: 2016.07.27

Talk Time: 25 minutes

Read Time: ~15 minutes

Contents

  1. ℹ️About Disney / My Team
  2. 🚧Challenges
  3. 🚨Open Source Approval
  4. πŸ€”Tools / Recommendations We Make
  5. πŸ†Problems We Solve With Node.js
  6. ☠Mistakes

Disney is huge!

Seattle skyline by Lena Eivy

Disney Technology Solutions and Services

Engineering Services Architecture

  • Security / Performance / Scalability
  • Developer Experience
  • CI/CD
  • Shared Solutions
  • Advise / Recommend Standards

I believe

A great Developer Experience

is necessary for developers to do the Right Thingβ„’

\[._.]/

Innovation Driven Company

To Support Grassroots

we

Build Tools, Share Solutions

The Problematic Carrot

β‡’ npm i -S buzzphrase
"dependencies": {
  "buzzphrase": "^2.0.0"
}

Ideologically Awesome

Who doesn't want (Minor, Patch) updates?

\[^_^]/

Because

Everyone

Understands SemVer

Nobody

Gets it wrong

Β―\_(ツ)_/Β―

Buzzphrase

CLI Mode

β‡’  npm install -g buzzphrase
β‡’  buzzphrase 2
distributed organizational engagement, independent of evolved behavioral channels

Module Mode

var buzzphrase = require('buzzphrase')
console.log('we are building', buzzphrase.getPhrase(2))
we are building extravehicular granular capabilities, in preparation for synchronized multi-layered convergence

ES2015 is Awesome!

˭̞̑(β—žβŽΛƒα†ΊΛ‚)β—žβ‚Žβ‚Ž=͟͟͞͞˳˚ΰ₯°Β°β‚’৹๐

var phrase = require('./lib/phrase')
module.exports = function(iterations) {
  return phrase(iterations)
}

β‡’

import phrase from 'phrase'
export default (iterations = 1) => eval( 'phrase('+iterations+')' )

β‡’ git commit -m "$(buzzphrase 2)"

[master aa48511] syndicated functional value-add on behalf of balanced disintermediate interfaces
  1 file changed, 15 insertions(+), 10 deletions(-)

β‡’ git tag 2.1.0 && git push && npm publish

Β―\_(ツ)_/Β―

It isn't working on my machine...

β‡’ npm install
β‡’ cat package.json | jq .dependencies.buzzphrase
"^2.0.0"
β‡’ cat node_modules/buzzphrase/package.json | jq .version
"2.1.0"
β‡’ node -v
v0.10.36

OK, not really

Buzzphrase is infallable

and the maintainer is

the most responsible person on the internet

But this has happened

Even Using

~

notation

"dependencies": {
  "bootstrap": "~3.3.0"
}

Broke Development Interface

Open Source Software Approval Committee

they don't like surprises

Hey, I want to use Node.js 4.4.6

submit a ticket with a spreadsheet
  • What's the License?
  • Usage Questions
  • specific version

Security / OSS Requirement

 Pin Versions

--save-exact or -E

β‡’  npm i -S -E buzzphrase@2.0.0
ALWAYS
β‡’  npm config set save-exact true
"dependencies": {
  "buzzphrase": "2.0.0"
}

And Shrinkwrap

β‡’  npm shrinkwrap [--dev]

VS

β‡’  npm-shrinkwrap [--dev]

Use updtr + nsp check

β‡’  npm i -g updtr
β‡’  updtr

What version of Node.js are you running?

0.10.364.2.44.4.75.10.16.2.1
nvm stable
Β―\_(ツ)_/Β―

Recommendation

β‡’  brew install node

^ installs 6.2.1, not LTS (yet)

Node Version Manager: NVM

β‡’  brew install nvm
# curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.2/install.sh | bash
β‡’  nvm install 4.4.7
β‡’  node -v
v4.4.7
β‡’  npm -v
2.15.8
β‡’  which node
/Users/antic/.nvm/versions/node/v4.4.7/bin/node

Add .nvmrc

β‡’  echo "4.4.7" > .nvmrc
β‡’  nvm use
Now using node v4.4.7 (npm v2.15.8)

.zshrc (shell-fu)

autoload -U add-zsh-hook
load-nvmrc() {
  if [[ -f .nvmrc && -r .nvmrc ]]; then
    nvm use &> /dev/null
  elif [[ $(nvm version) != $(nvm version default)  ]]; then
    nvm use default &> /dev/null
  fi
}
add-zsh-hook chpwd load-nvmrc
load-nvmrc

Use Autochecker

β‡’  npm i -D -E autochecker
"scripts": {
  "test": "./node_modules/mocha/bin/mocha",
  "nodeversions": "node ./node_modules/autochecker/cli.js"
},
β‡’  npm run nodeversions

I want to use Node.js

4.4.64.4.7

and updated 15 modules...

submit another ticket with a spreadsheet

Automate with Node.js CLI tool

#!/usr/bin/env node
// read package.json
const fs = require('fs')
// check versions against approved OSS list
const request = require('request')
// the list doesn't have an API so we scrape it
const cheerio = require('cheerio')
// give the user progress update
const progress = require('progress')
// populate spreadsheet with items needing to be approved
const xlsx = require('xlsx-template')
β‡’  snoden   
Usage:  <command> [options]

Commands:
  check   β˜‘οΈ  check this project for standards, security and OSS
  update  πŸ“¦  update this tool to the latest version

Options:
    --dev              🚧  include devDependencies in npm shrinkwrap
    --skip-clean       ⏩  skip breaking on dirty git repo state
    --skip-gitignore   πŸ™ˆ  do not check for node_modules in .gitignore
    --skip-npm         ⏩  skip npm install step (speeds up check if already done)
    --skip-nsp         ☠  skip nsp (not recommended, enables offline test)
    --skip-shrinkwrap  ⚠️  skip npm shrinkwrap (not recommended)
    --skip-update      πŸ“¦  skip updating node packages (using updtr)
    --skip-xls         ⏩  skip excel spreadsheet creation (useful on build server)
    -a, --auto         ⛏  auto-fix issues without prompting or throwing errors
    -b, --build        πŸ€–  bot mode: skip corrective user prompts (throws errors)

Every Day

β‡’  snoden check
# submit OSS request

Drowning

the

Committee

but

It's getting better

Automation FTW

β‡’  snoden check
❄️   \[._.]/ - Hi, I'm Snoden, the Standard Node.js Enforcer. Checking your package...
βœ…   git workspace is clean
βœ…   .nvmrc set to 4.4.7
⚑️   npm install
βœ…   node_modules installed
βœ…   dependencies packages are pinned
βœ…   devDependencies packages are pinned
⚑️   updating package deps with updtr
βœ…   packages up-to-date
βœ…   npm-shrinkwrap found
βœ…   .gitignore contains node_modules
⚑️   nsp check
βœ…   (+) No known vulnerabilities found

  compiling xlsx [====================================] 100% 0.0s

Why Do We Use Node.js?

Big on Java

but with Node.js
  • JSON all the way down
  • no (de)serialization
  • speed of development
  • awesome command-line apps
  • TPS: 350 vs 3K per instance

50 Years of Back-End Services

  • new apps all the time
  • make 20 calls to APIs
  • make 1 call to Node.js orchestration API
  • 3 years ago:
    • A service took 4 TPS
    • Can node handle 8 TPS?
    • ( Ν‘Β° ΝœΚ– Ν‘Β°) - LoL

1st DTSS Production Node.js

  • Augment a Java REST API
  • 1 Month Deadline
  • 1 Developer + Node.js: 8 days
  • 99% code coverage (Hapi.js + Joi + Lab)
  • Performance: 2K TPS per node vs 450 TPS
  • Speed of Change (amazing)

Cloud Infrastructure

CLI

Format CLI Output

/**
 * output module: unify ouput formatting logic
 * json, table, or yaml
 */
var _ = require('lodash')
var argv = require('yargs').argv
var prettyjson = require('prettyjson')
var Table = require('cli-table')
module.exports = function(data, columns) {
  if (argv.json)
    return console.log(JSON.stringify(data, null, 2))
  if (argv.yaml)
    return console.log(prettyjson.render(data))
  var table = new Table({ head: columns })
  [].push.apply(
    table,
    _.map(data, function(row){
      return _.toArray(_.pick(row, columns))
    })
  )
  console.log(table.toString())
};

l10n/i18n Service

Parsing and Returning JSON

{
  "welcome": {
    "filter": {
      "FR": {
        "adult": "You seem to be an adult in France.",
        "child": "You seem to be a child in France.",
        "teen": "You seem to be a teen in France.",
        "default": "You seem to be in France."
      }
    }
  }
}

?locale=en-CA&country=FR&age=child

{
  "welcome": "You seem to be a child in France."
}

CDN Origin Asset Bundling

Turn 7 HTTP/1.1 requests into 1

And a Whole Lot More

9,439 git repos with a package.json

Simple Mistake

app.use(function(req, res, next) {
  // ... do some things
})
oops
app.use(function(req, res, next) {
  // ... do some things
  next()
})

Bigger Mistake   

const Hapi = require('hapi')
const server = new Hapi.Server(1337)
// complex response template (this is process memory!)
const bodySchema = require('./templates/response.schema')
server.route({
  method: 'GET',
  path: '/foo',
  handler: function(request, reply){
    bodySchema.data = 'foo data'
    bodySchema.error.push({message: 'foo error'})
    reply(bodySchema)
  }
)
server.route({
  method: 'GET',
  path: '/bar',
  handler: function(request, reply){
    bodySchema.data = 'bar data'
    bodySchema.error.push({message: 'bar error'})
    reply(bodySchema)
  }
)
server.start()
curl /foo
{
  "data": "foo data",
  "error": [{
    "message": "foo error"
  }]
}
curl /bar
{
  "data": "bar data",
  "error": [{
    "message": "foo error"
  }, {
    "message": "bar error"
  }]
}

Fixing with Hapi.js

const Hapi = require('hapi')
const server = new Hapi.Server(1337)
const cloneDeep = require('lodash.clonedeep')
const bodySchema = Object.freeze(require('./templates/response.schema'))
server.ext('onRequest', function (request, next) {
  // We can ammend request.app.payload
  // without polluting future requests
  request.app.payload = cloneDeep(bodySchema)
  next()
})
server.route({
  method: 'GET',
  path: '/foo',
  handler: function(request, reply){
    request.app.payload.data = 'change data in request memory!'
    request.app.payload.error.push({message: 'request error'})
    reply(request.app.payload)
  }
)
server.start()

eslint didn't catch that!

so

your tests better notice

In Closing

Node.js

is

awesome 

Thanks!

\[._.]/  Adam Eivy

  @antic

The Walt Disney Company

Seattle
  restart