Breaking Up with the Asset Pipeline

Thanks Pete, Brett, Maureen

~ $ WhoAmI

Alan Peabody - Developer @  Agilion

This Talk

  • Asset Pipeline Pain
  • Experimenting with JS build tools
  • Implementing SPA along side Rails
  • Benefits of Decoupling

Agilion's First Single Page Apps

The DHH Rails Way

  • Turbolinks
  • Caching
  • The Asset Pipeline

The UX Still Sucked

SIngle Page Apps

(on the asset pipeline)

Gem install Javascript

  • Usually not maintained by JS lib Author
  • Frequently out of date
  • Multiple gems wrapping same JS lib.

Manifest Disaster

  • Literally just concatenating files where required
  • Must organize code to ensure everything is in proper place

//= jquery
//= underscore
//= backbone
//= require my_app_setup
//= require_tree models/
//= require_tree collections/
//= require_tree templates/
//= require_tree views/
//= require_tree routers/
//= require my_app_start


  • Error Prone
  • Tedious
  • Antiquated
window.App = {};
App.Models = {};
App.Collections = {};
App.Controllers = {};
App.Routes = {};
App.Views = {};



Development is SLOW

  • 128 JavaScript files
  • 3 SCSS files
  • Each file is a separate request
  • 5-7 second reload times
The request response cycle is terrible for maintaining application state,
and the asset pipeline is terrible for building JavaScript Single Page Apps.


Seperate Repositories

Treating the UI as its own application


JS Tech Stack

  • Backbone
  • NPM
  • Bower
  • Browserify
  • Gulp.js

NPM JS Package Management

Development/build tools dependencies

Like development group Gemfile dependencies

  "name": "MyAppUI",
  "description": "UI for MyApp",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "gulp": "~3.8.6",
    "gulp-concat": "~2.3.4",
    "browserify": "~5.9.1",
    "less": "~1.7.4",
    "bower": "~1.3.8",
    "browser-sync": "~1.3.2"
npm install

Bower JS Package Management

  • Front end package dependencies
  • Often published by JS library authors
  • Like Gemfile with require: false

// bower.json
  "name": "MyAppUI",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "jquery": "~2.0.3",
    "bootstrap": "~3.0.3",
    "backbone": "~1.1.2"
bower install
Gem based bower wrapper: Rails Assets

Browserify Modules

  • Transpiles CJS Modules to browser compatible JavaScript
  • Based on Common JS and Node Modules
  • Replaces Manifest Files
  • One JS request, even in Development

// app/collections/users.js
var Backbone = require('backbone'),
    User     = require('./app/models/user');

module.export = Backbone.Collection.extend({
  model: User

Alternatives: require.js, ES6 Module Transpiler

Source Maps

  • Concatenation is transparent
  • You see your original source files, by file name, in:
    • Dev tools debugger
    • Error stack traces 
  • Maps compiled JS/CSS to the source on disk

Gulp config

var gulp       = require('gulp'),
    browserify = require('browserify'),
    gconcat    = require('gulp-concat');

gulp.task('javascript', function() {
    .pipe(browserify({debug: !gulp.env.production}))

gulp.task('default', function() {'js/**', ['js']);
gulp javascript

Alternatives: Grunt, Broccoli


var gulp        = require('gulp'),
    browserify  = require('browserify'),
    gconcat     = require('gulp-concat'),
    browserSync = require('browser-sync');

gulp.task('javascript', function() {
    .pipe(browserify({debug: !gulp.env.production}))
    .pipe(browserSync.reload({stream: true, once: true});

gulp.task('browser-sync', function() {
  browserSync({server: { baseDir: "./", port: 4200 }});

gulp.task('default', ['browser-sync'], function() {'js/**', ['js']);

Alternatives: Live Reload

What about deployment?

Heroku & CORS

2 Heroku Apps w/ Cross Origin Resource Sharing (CORS)

Your front end application deserves to be treated as an application in its own right,
not relegated to the asset directory.

Single Page Apps on Rails Redux

JS Tech Stack

  • Ember
  • NPM
  • Bower
  • Grunt & Broccoli (alternatives to Gulp)
  • ES6 Modules (alternative to browserify)


  • A gem you install in existing rails apps
  • Provides rails-api generator
  • ActionController::API
  • Cuts out some middle ware
  • No sessions
  • Faster responses


Finally a real RESTful API!

// POST /api/token
  grant_type: "password",
  username: "",
  password: "correct horse battery staple"
// response 200
  access_token: 'sdlkyuskdfyiwesdfkljsdf',
  token_type: 'Bearer',
  expires_in: 30.days,
  user_id: 1
GET /api/protected
Headers: 'Authorization': "Bearer #{access_token}"

Rails - Active Model Serializers

class UsersController < ApplicationController
  def show
    render json: User.find(params[:id]), serializer: UserSerializer
class UserSerializer < ActiveModel::Serializer
  attributes :id, :name, :email, :is_admin
  def is_admin
  "user": {
      "id"      : 1,
      "name"    : "alan",
      "email"   : "",
      "is_admin": true
On Github

Better Deployments

Heroku & Proxy

2 Heroku apps with Nginx Proxy

Custom Deploys

Both apps on same domain, same server(s)

Development Is Fast

  • The Largest App to Date:
    • 315 JavaScript files
    • 57 Less files
    • < 4 second build time
    • < 1 second browser load time
Oh yeah I would never go back by choice.
- Pete Brown

Feels Good

Development is Faster*

*Or at least it feels faster

Rails is Beautiful Again

Decoupled Deploys

API First development

Purple Farts Slide

We have the opportunity to build a synergistic, mobile enabled, web platform at an accelerated pace with a dynamic user experience years ahead of its time.


Twitter: @alanpeabody 

Breaking up with the asset pipeline

By Alan Peabody

Breaking up with the asset pipeline

2014 Burlington Ruby Conference, August 2nd.

  • 4,639
Loading comments...