Automating Google Workspace (GWS) & more with Apps Script
Â
Refactor Large applications with Backbone
1. From Mess to Success,
Refactoring Large Applications with Backbone
dev.Objective()
May 14, 2015
Stacy London
@stacylondoner
McWay Falls, Julia Pfeiffer Burns State Park - Big Sur, CA - 2015 by Stacy London
2. About Me
Iâve been making things for the web since 1998.
Currently Iâm a Senior Application Architect focused on
Front-End Engineering at Northwestern Mutual.
@stacylondoner
3. Northwestern Mutual
Northwestern Mutual has been helping families and businesses achieve
ďŹnancial security for nearly 160 years. Our ďŹnancial representatives build
relationships with clients through a distinctive planning approach that integrates
risk management with wealth accumulation, preservation and distribution. With
more than $230 billion in assets, $27 billion in revenues, nearly $90 billion in
assets under management and more than $1.5 trillion worth of life insurance
protection in force, Northwestern Mutual delivers ďŹnancial security to more than
4.3 million people who rely on us for insurance and investment
solutions, including life, disability and long-term care insurance; annuities; trust
services; mutual funds; and investment advisory products and services.
Northwestern Mutual is the marketing name for The Northwestern Mutual Life Insurance Company, Milwaukee, WI, and its
subsidiaries. Northwestern Mutual and its subsidiaries offer a comprehensive approach to ďŹnancial security solutions including: life
insurance, long-term care insurance, disability income insurance, annuities, investment products, and advisory products and
services. Subsidiaries include Northwestern Mutual Investment Services, LLC, broker-dealer, registered investment adviser,
member FINRA and SIPC; the Northwestern Mutual Wealth Management Company, limited purpose federal savings bank; and
Northwestern Long Term Care Insurance Company.
4. What do I mean by large application?
⢠a multi-page web application (MPA) built with ASP.NET
MVC
⢠1500+ ďŹles | 343,000+ lines of code
⢠UI (CSS / JS)
⢠230+ ďŹles | 42,000+ lines of code
5. Development Culture
⢠JEE/Java shop turned .NET/C#
⢠view JS as a bit of a âtoyâ language
⢠as JS was being added it was just being added without much
thought to maintainability, extensibility, testability, patterns,
etc.
⢠app was going to be heavy with JS yet initially on a platform
that wasnât optimized for this kind of app dev.
⢠path of least resistance was to just put everything in â¨
$(document).ready()
9. Problems
⢠one large JavaScript ďŹle in the <head> (all code included
on every page regardless if it needed it)
⢠all JavaScript global and in $(document).ready()
⢠some pages had inline JavaScript
⢠not commented
⢠no unit tests
⢠unclear if a function / event was used on multiple pages
10. Iterative Refactoring
⢠big codebase
⢠large team working on 2 week sprints that couldnât be
slowed down
⢠refactors had to ďŹt into a sprint
11.
12.
13. Refactor, Iteration 1: Break Apart JS
⢠ďŹgure out what JS belongs with each page and move
that JS into a separate ďŹle for that page
⢠include that JS with the partial HTML
14. Refactor, Iteration 1: Start Adding Unit Tests
⢠start adding unit tests for original code then make sure it
passed after refactoring as a safety/conďŹdence
mechanism
⢠to prepare for this journey of breaking code into smaller,
more testable chunks it was good to start early with
creating a test bed
⢠help make subsequent refactors less stressful
15. Refactor, Iteration 1: Start Adding Unit Tests
⢠picked Jasmine (BDD style) - http://
jasmine.github.io/
⢠a lot of the JS tied to DOM manipulation so not in
an ideal state for unit testing with Jasmine out of
the box
⢠needed DOM testing helper so added jquery-
jasmine library to add ďŹxtures (HTML snippets
to run tests against)
⢠no external UI Regression tools (e.g. Selenium) in a
state to do this validation throughout the refactor
16.
17.
18. Refactor, Iteration 2: Namespacing & Object Literal
⢠create an app namespace to get custom code out of the global
namespace (window) to avoid collisions with external libs/
frameworks
⢠introduce an Object Literal notation style in preparation for
Backbone (which uses this pattern)
⢠helps to organize the code and parameters logicallyâ¨
âan object containing a collection of key:value pairs with a colon
separating each pair of keys and values where keys can also
represent new namespacesâ
⢠add closures with Instantly Invoked Function Expressions
(IIFEs)
20. ;
var myApp = myApp || {};
(function($, myApp) {
'use strict';
$.extend(myApp, {
init: function() {
this.commonStuff();
},
commonStuff: function() {
// common code across many or all pages
}
});
})(window.jQuery, window.myApp);
$(document).ready(function() {
myApp.init();
});
my-app.js
21. my-app.js
with comments
// prefix with semi-colon as safety net against concatenated
// scripts and/or other plugins that are not closed properly
;
// check for existence of myApp in the global namespace
var myApp = myApp || {};
// Use IIFE to:
// * encapsulate app logic to protect it from global namespace
// * pass in namespace so can be modified locally and isn't
// overwritten outside of our function context
// * ensure $ only refers to window.jQuery (defensive programming)
// * $ passed through as local var rather than as globals and this
// (slightly) quickens the resolution process and can be more
// efficiently minified (especially if regularly referenced)
(function($, myApp) {
22. my-app.js
with comments
(function($, myApp) {
// Strict mode makes several changes to normal JavaScript semantics:
// * eliminates some JS silent errors by changing them to throw errors.
// * fixes mistakes that make it difficult for JavaScript engines to
// perform optimizations: strict mode code can sometimes be made to
// run faster than identical code that's not strict mode. Add inside
// the IIFE so it's defined for just the functions defined within and
// doesn't flip concatenating/minified code to strict inadvertently
'use strict';
// extend the namespace with more functionality
$.extend(myApp, {
24. Better but not great
⢠code is aligned with the screen to which is pertains
⢠pages now have mid-page script includes which isnât
good for performance / rendering
⢠still hand-wiring Ajax calls
⢠entire-page-JS is not very modular
⢠want to break screens down into smaller features and
keep events neatly associated
25. Backbone to the rescue
⢠wanted something that provided/enforced structure but in a
lightweight way
⢠itâs a MPA not a SPA so full-featured SPA frameworks didnât make
sense (e.g. Angular, Ember)
⢠wanted to be able to use just a small feature of the library/
framework and add more full integration over time (refactor in
multiple iterations)
⢠for these reasons Backbone.js made sense - http://backbonejs.org/
26.
27. Refactor, Iteration 3: Page Level Backbone Views
⢠reducing boilerplate code
⢠views enforce organization of events
⢠views enforce an Object Literal notation pattern
⢠views helped developers think about encapsulating pieces of the
screen
⢠starting with just Views gave the team time to start planning for
refactoring back-end data provided by ASP.NET MVC Controllers
into more RESTful web services (Web API) which is necessary to
take full advantage of Backbone.js Models/Collections
28. Refactor, Iteration 3: Page Level Backbone Views
⢠my-app.js - is now responsible for instantiating the app
namespace and setting up a super light-weight view
manager for Backbone.js
⢠instantiate all available views on the page upon DOM
ready.
⢠page-one-view.js - no longer namespaced individually
since being added to the views object of the app
namespace
34. Refactor, Iteration 4: Sub-Page Backbone Views
⢠start breaking the large page views into sub-page views
so that you can get truly modular
⢠share modules/code if a module exists on more than
one page
35.
36.
37. Refactor, Iteration 5: Backbone Models, Collections
⢠move code that is getting data and doing any business
logic out of the Views and into Models and Collections
⢠e.g. move phone number formatting out of View and
into the Model
⢠remove hand written Ajax, use BB API (e.g. fetch)
⢠make sure server side controllers were written in a
RESTful way
⢠still not complete across the application
40. Use Backbone.js API
to get data
// initialize view
var pageOneView = new PageOneView({
collection: new PageOneCollection([]),
});
var PageOneView = Backbone.View.extend({
el: '#pageOneMainContainer',
template: _.template($('#page-one-template').html()),
initialize: function() {},
render: function() {
this.$el.html(this.template({'collection': this.collection}));
// maintain chainability
return this;
}
});
var PageOneCollection = Backbone.Collection.extend({
initialize: function(models, options) {},
model: PageOneModel,
// RESTful web service URL
url: '/SomeEndpoint'
});
41.
42. Refactor, Iteration 6: Mini-SPA
⢠treat each page of the multi-page app (MPA) like itâs a
miniature single page app (SPA)
⢠each page has a single entry point (main.js)
⢠this will setup the code for a module loader / build system
so that we can ďŹnally move the JS to the bottom of the
page and remove mid-page scripts
⢠improve performance (time to ďŹrst paint - how long it
takes between a user entering a URL into the browser
and when he/she sees visual activity on screen)
43.
44.
45.
46. Refactor, Iteration 7: Modules
⢠current version of JavaScript (ECMA-262) doesnât provide a
way to import modules of code like more traditional
programming languages do
⢠modules are proposed for the next version of JS (ES6/
ES2015/Harmony)
⢠to use modules and manage dependencies with the current
version of JS you can use community driven methodologies
⢠there are two popular styles with associated script
loaders / build systems
47.
48. Refactor, Iteration 7: Modules - AMD
⢠AMD - Asynchronous Module DeďŹnitionâ¨
â¨
âThe Asynchronous Module DeďŹnition (AMD) API
speciďŹes a mechanism for deďŹning modules such that the
module and its dependencies can be asynchronously
loaded. This is particularly well suited for the browser
environment where synchronous loading of modules
incurs performance, usability, debugging, and cross-
domain access problems.ââ¨
https://github.com/amdjs/amdjs-api/wiki/AMD
49. Refactor, Iteration 7: Modules - CommonJS
⢠CommonJSâs module format was made popular for
server-side JavaScript development (namely for Node.js/
NPM)
⢠itâs synchronous
⢠syntax is a bit easier as it frees you from the deďŹne()
wrapper that AMD enforces
⢠requires a build in a JS runtime
50. Refactor, Iteration 7: Modules - RequireJS
⢠popular script loader written by James Burke that helps you load
multiple script ďŹles and deďŹne modules with or without
dependencies
⢠use RequireJS as ďŹrst pass at module loading since async nature
means no build step during dev time (least disruption to the dev
team which is important because there is a learning curve)
⢠use almond (AMD API shim) so donât have to add a special script
tag to load RequireJS and change any HTML
⢠this will move the code away from IIFEs to modules with
dependency management
51. my-app.js
// requireJS simplified commonJS wrapper
// do this so can use the commonJS style &
// make switch to browserify easier
define('app', function(require, exports, module) {
'use strict';
var $ = require('jquery');
var _ = require('underscore');
var Backbone = require('backbone');
var Modernizr = require('modernizr');
var HeaderController = require('./header-controller');
var FooterController = require('./footer-controller');
var app = new Backbone.Application();
module.exports = app;
});
(function() {
var $ = require('jquery');
var app = require('app');
// dom ready
$(function() {
app.start();
});
}());
52. ;
(function() {
'use strict';
var $ = require('jquery');
var PageOneController = require('./page-one-controller');
var app = require('app');
// dom ready
$(function() {
app.start({
ScreenController: PageOneController
});
});
}());
page-one-main.js
53. define('page-one-controller', function(require, exports, module) {
'use strict';
var Backbone = require('backbone');
var PageOneView = require('PageOneView');
var PageOneController = Backbone.Controller.extend({
initialize: function(options) {
var pageOneView = new PageOneView();
}
});
module.exports = PageOneController;
});
page-one-controller.js
55. Refactor, Iteration 7: Modules - Browserify
⢠Browserify lets you require('modules') in the browser by
bundling up all of your dependencies during a build step
⢠itâs the end-state module loader because it allows for easy
bundle splitting
⢠ďŹgure out common/shared JS and create a common
bundle then a bundle for the unique code on each page
⢠by having a main.js for each screen this will act as an
entry point so Browserify can ďŹnd all the modules
required and create a screen-speciďŹc bundle
56. Refactor, Iteration 7: Modules - Browserify
⢠means build during dev but we were already doing that
for our CSS with LESS
⢠âItâs great the dev community has
embraced compilation because itâs
inevitable.â - Brendan Eich at Fluent 2015
⢠the code will become very clean
58. Summary
⢠the JavaScript is now modular and easier to maintain
⢠Backbone.js helps devs keep consistent with coding patterns and
organization (Object Literal notation, events)
⢠events are scoped to the smallest part of the page to which they matter
(Backbone.js)
⢠namespacing/IIFEs and then later module loader/build removes the
possibility of collisions with other frameworks/libs
⢠unit tests mean you can feel more conďŹdent to change things
⢠only sending JS to the browser that is necessary is good for
performance (think mobile)
60. "The secret to building large apps is never
build large apps. Break your applications
into small pieces. Then, assemble those
testable, bite-sized pieces into your big
application"
- Justin Meyer, author JavaScriptMVC
61. Team Shout Out
⢠This was over the course of several years and I worked
with two other fantastic front-end engineers:
⢠Ryan Anklam ( @bittersweetryan )
⢠Zeek Chentnik ( http://ezekielchentnik.com )
62. JavaScript References
⢠âPatterns For Large-Scale JavaScript Application Architectureâ by Addy
Osmani â¨
http://addyosmani.com/largescalejavascript/
⢠âLearning JavaScript Design Patternsâ by Addy Osmaniâ¨
http://addyosmani.com/resources/essentialjsdesignpatterns/book/
⢠âUsing Objects to Organize Your Codeâ by Rebecca Murpheyâ¨
http://rmurphey.com/blog/2009/10/15/using-objects-to-organize-your-code/
⢠âItâs time to start using JavaScript strict modeâ by Nicholas Zakasâ¨
http://www.nczonline.net/blog/2012/03/13/its-time-to-start-using-javascript-strict-
mode/
⢠âWriting Modular JavaScript with AMD, CommonJS & ES Harmonyâ by Addy
Osmaniâ¨
http://addyosmani.com/writing-modular-js/
63. Backbone.js References
⢠âDeveloping Backbone.js Applicationsâ by Addy
Osmaniâ¨
http://addyosmani.github.io/backbone-fundamentals/
⢠âCommunicating Between Views in Client-Side
Appsâ by Rebecca Murpheyâ¨
http://bocoup.com/weblog/communicating-between-
views-in-client-side-apps/
⢠Talks from past Backbone Conferences are free/online:
http://backboneconf.com/â¨
http://backboneconf.com/2013/