Brad Oyler

Lead Developer,  NBCNews.com

@bradoyler
~
github.com/bradoyler

Tweet questions with #BuxCoTech?

Building content sites


@

content


Traffic - Last 30 days


"BIG" redesign (feb '14)


previous cover


the response


Change.org petition

Nasty Tweets

Negative Facebook Comments

Drop in unique visitors


preview.nbcnews.com (may)


the stack


Express.js
+
Handlebars.js
+
Route-Cache
+
Redis

why?


web + worker

Worker:
"dependencies": {
    "redis": "0.12.0"
    "async": "0.9.0",
    "request": "2.34.0"
}

Web:
"dependencies": {
    "redis": "0.12.0",
    "route-cache": "0.2.0",
    "express": "4.0.0",
    "express-handlebars" : "1.1.0"
},

"devDependencies": {
   "node-sass": "0.16.0",
   "grunt": "~0.4.1"
}

route-cache

module.exports.cacheSeconds = function(ttl) {

  return function(req, res, next) {
    var cache = cachestore.get(req.originalUrl);
    if (cache) {
      res.send(cache);
    } else {
      var send = res.send;
      res.send = function(string) {
        var body = string instanceof Buffer ? string.toString() : string;
        cachestore.put(req.originalUrl, body, ttl);
        send.call(this, body);
      };
      next();
    }
  }
};
 npm install route-cache --save
  github.com/bradoyler/route-cache 

index route

var dataService = require('../dataService');
var routeCache  = require('route-cache');

function controller(req, res, next) {
   dataService.getKeys(['metadata','coverstories'], function(model) {
     res.render('index', model);
   });

}

var router = express.Router();
router.get('/', routeCache.cacheSeconds(15), controller);

module.exports = router;

data-service

var redis = require('redis');
var async = require('async');

function getKey(key, callback) {

       redis.get(key, function(error, value) {
         callback(null, value);
       }
}

exports.getKeys = function(keys, callback) {

   async.map(keys, this.getKey, callback);

};

view Engine

app.engine('.hbs', expressHbs({
    defaultLayout: 'default',
    extname: '.hbs',
    layoutsDir: __dirname + '/views/layouts',
    partialsDir: [__dirname + '/views/partials'],
    helpers: hbsHelpers
}));

Q: Why handlebars? 

A: Familiar syntax, partials & helpers

HBS helpers

 module.exports = function() {
  return {
    ifindexby: this.ifindexby,
    formatdate: this.formatdate,
    decodeuri: this.decodeuri,
    link: this.linkHelper,
    'if-equal': this.ifEqual,
    'each-slice': this.eachSlice,
    'if-contains': this.ifContains,
    articleurl: this.articleurl,
    routeurl: this.routeurl,
    coverheadline: this.coverheadline
  };
};
this is where the magic happens, as it makes building out your app with templates & partials... dead simple

{{if-equal}}

exports.ifEqual = function(val, test, options) {
  var arrTest = test.split('||');
  for (var i = 0; i < arrTest.length; i++) {
    if (val === arrTest[i]) {
      return options.fn(this);
    }
  }
  return options.inverse(this);
};

{{#if-equal type 'photo'}}
  {{> article_photo}}
{{/if-equal}}

{{#if-equal type 'post||externallink'}}
  {{> article_post}}
{{/if-equal}}

true, these aren't logic-less templates, but so easy to work with

the client

Bower packages
"dependencies": {
  // css
    "bootstrap-sass-official": "~3.1.0",
    "typesettings": "~2.0.0",
    
  // javascript    
    "modernizr": "~2.6.2",
    "jquery": "~1.11.0",
    "jquery.lazyload": "~1.9.3",
    "jquery-waypoints": "~2.0.5",
    "jquery.cookie": "~1.4.1",
    "underscore": "1.5.2",
    "mustache": "~0.8.1",
    "backbone": "~1.1.2",
    "velocity": "~1.1.0"
}

and now...


(science)

benchmarking R/S

ApacheBench test
ab -n 200 -c 4 http://localhost:9001/

// 1. express(‘view cache’, false) & !routecache
Requests per second:    12.88 [#/sec] (mean)

// 2. express(‘view cache’, true) & !routecache
Requests per second:    21.38 [#/sec] (mean)

// 3. express(‘view cache’, true) & routecache(15)
Requests per second:    530.56 [#/sec] (mean)
github.com/bradoyler/express-content-demo

web performance


 


*webpagetesT results 

wpt filmstrip view


test results

Why does this really matter?


SEO
User Experience
SEO
Mobile  3G
SEO
Google likes us now




results

search traffic

unique visitors


final word

Testing/CI tools
Mocha, Runscope, Jenkins, SauceLabs

Deployment
grunt & usemin
AWS S3
gulp (soon)

thanks ;)



Made with Slides.com