SlideShare a Scribd company logo
flickr.com/photos/jontysewell/4526861658/




                Maintainable JavaScript
                   Nicholas C. Zakas | Chief Architect, WellFurnished
New
@slicknet
Maintainability
Why do we care?
http://flickr.com/photos/indraw/4857101224/




            Most of your time is spent maintaining code
Maintainability
  Who cares?
http://www.flickr.com/photos/jbiljr/1935937825/
Maintainable JavaScript 2012
http://flickr.com/photos/protestphotos1/4726566233/




                               We all want to be rock stars
                     "Don't mess with my process, man! It's about the music!"
http://flickr.com/photos/12832008@N04/3027812968/
Maintainability
What is maintainable code?
Maintainable code works for
      five years without major
      changes
                                             • Intuitive
                                             • Understandable
                                             • Adaptable
                                             • Extendable
                                             • Debuggable
                                             • Testable
http://flickr.com/photos/simax/3390895249/
Be kind to your future
self.

Chris Eppstein,
Creator of Compass
Code Conventions




                                Programming
Code Style                        Practices
Code Style Guide
Communicating with each other through
               code
Programs are meant to be
read by humans and
only incidentally for
computers to execute.

H. Abelson and G. Sussman,
The Structure and Interpretation
of Computer Programs
https://twitter.com/sh1mmer/status/21446028923
http://javascript.crockford.com/code.html
http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
http://docs.jquery.com/JQuery_Core_Style_Guidelines
http://dojotoolkit.org/community/styleGuide
https://github.com/rwldrn/idiomatic.js/
http://flickr.com/photos/polinasergeeva/3052378826/




                                                        Tabs for
                                                      indentation




                    4 spaces for
                    indentation
if (wl && wl.length) {
            for (i = 0, l = wl.length; i < l; ++i) {
         p = wl[i];
         type = Y.Lang.type(r[p]);
         if (s.hasOwnProperty(p)) { if (merge && type
    == 'object') {

    Y.mix(r[p], s[p]);
} else if (ov || !(p in r)) {
                    r[p] = s[p];
                }
            }
        }
    }
if (wl && wl.length) {
    for (i = 0, l = wl.length; i < l; ++i) {
        p = wl[i];
        type = Y.Lang.type(r[p]);
        if (s.hasOwnProperty(p)) {
            if (merge && type == 'object') {
                Y.mix(r[p], s[p]);
            } else if (ov || !(p in r)) {
                r[p] = s[p];
            }
        }
    }
}
https://twitter.com/slicknet/status/169903570047614977
if (found) { doSomething(); doSomethingElse(); }
else { doAThirdThing(); doAFourthThing(); }
if (found) {
    doSomething();
    doSomethingElse();
} else {
    doAThirdThing();
    doAFourthThing();
}
http://flickr.com/photos/polinasergeeva/3052378826/




                                                      Comments
https://twitter.com/slicknet/statuses/3559283285
/**
 * Returns a new object containing all of the properties of
 * all the supplied objects. The properties from later objects
 * will overwrite those in earlier objects. Passing in a
 * single object will create a shallow copy of it. For a deep
 * copy, use clone.
 * @method merge
 * @for YUI
 * @param arguments {Object*} the objects to merge.
 * @return {object} the new merged object.
 */
Y.merge = function() {
    var a = arguments, o = {}, i, l = a.length;
    for (i = 0; i < l; i = i + 1) {
        Y.mix(o, a[i], true);
    }
    return o;
};
                      Every method
if (mode) {
    switch (mode) {
        case 1: // proto to proto
            return Y.mix(r.prototype, s.prototype, ov, wl, 0,
                         merge);
        case 2: // object to object and proto to proto
            Y.mix(r.prototype, s.prototype, ov, wl, 0, merge);
            break; // pass through
        case 3: // proto to static
            return Y.mix(r, s.prototype, ov, wl, 0, merge);
        case 4: // static to proto
            return Y.mix(r.prototype, s, ov, wl, 0, merge);
        default: // object to object is what happens below
    }
}




              Difficult-to-understand code
while (element &&(element = element[axis])){ //NOTE: assignment
    if ( (all || element[TAG_NAME]) &&
       (!fn || fn(element)) ) {
            return element;
    }
}




          Code that might seem to be wrong
Naming
http://flickr.com/photos/kaatje/243834320/
Naming
• Use logical names for variables and functions
   – Don't worry about length
• Variable names should be nouns
• Function names should begin with a verb (i.e.
  getName())
   – Functions return booleans should begin with
     "is" or "has", such as isValid() or hasItem()
• Avoid useless names such as foo and temp
if (wl && wl.length) {
    for (i = 0, l = wl.length; i < l; ++i) {
        p = wl[i];
        type = Y.Lang.type(r[p]);
        if (s.hasOwnProperty(p)) {
            if (merge && type == 'object') {
                Y.mix(r[p], s[p]);
            } else if (ov || !(p in r)) {
                r[p] = s[p];
            }
        }
    }
}
// Variables, functions, properties methods
var myName = "Nicholas";

function sayName() {
    alert(myName);
}

var person = {
    name: "Nicholas",
    sayName: function() {
        alert(this.name);
    }
};




                       Camel Casing
What about acronyms?
var element = document.getElementById("my-div");

element.innerHTML = "Hello world!";      WTF?

var xhr = new XMLHttpRequest();
                                      No, seriously

                                      WTF?


                    Camel Casing
// Constant-like variables
var HOVER_CLASS = "mouse-over";

// Constructors
function Person(name) {
    this.name = name;
}

var me = new Person("Nicholas");




             Camel Casing- Exceptions
Programming Practices
Small patterns for common problems
There are two ways of
constructing a software
design: One way is to make it
so simple that there are
obviously no deficiencies and
the other way is to make it so
complicated that there are no
obvious deficiencies.

C.A.R. Hoare,
Quicksort Developer
Front End Layers



Presentation      Behavior
    (CSS)       (JavaScript)
                 Base JS
       Data/Structure
          (HTML)
Don’t cross the
streams
<button onclick="doSomething()">Click Me</button>




      Keep JavaScript out of HTML
var element = document.getElementById("container");
element.innerHTML = "<div class="popup"></div>";




              Keep HTML out of JavaScript
.foo {
    width: expression(document.offsetWidth + "px");
}




               Keep JavaScript out of CSS
var element = document.getElementById("container");
element.style.color = "red";
element.style.cssText = "background:blue;border:1px solid red";




               Keep CSS out of JavaScript
//the wrong way!!!
function handleClick(event){

    var popup = document.getElementById("popup");
    popup.style.left = event.clientX + "px";
    popup.style.top = event.clientY + "px";
    popup.className = "reveal";

}




      Event handlers should only handle events
//better, but still wrong
function handleClick(event){
    showPopup(event);
}

function showPopup(event){
    var popup = document.getElementById("popup");
    popup.style.left = event.clientX + "px";
    popup.style.top = event.clientY + "px";
    popup.className = "reveal";
}




          Don't pass the event object around
//win!!
function handleClick(event){
    showPopup(event.clientX, event.clientY);
}

function showPopup(x, y){
    var popup = document.getElementById("popup");
    popup.style.left = x + "px";
    popup.style.top = y + "px";
    popup.className = "reveal";
}




          Properly separated event handling
//don't add new methods
Array.prototype.awYeah = function(){
    alert("Aw yeah!");
};

//don't override methods
YUI.use = function(){
    alert("Aw yeah!");
};




         Don't modify objects you don't own
          If you didn't define the object yourself, you don't own it
http://nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/
function handleClick(event){
    showPopup(event.clientX, event.clientY);
}

function showPopup(x, y){
    var popup = document.getElementById("popup");
    popup.style.left = x + "px";
    popup.style.top = y + "px";
    popup.className = "reveal";
}




         Avoid global functions and variables
var Controller = {
    handleClick: function(event){
        this.showPopup(event.clientX, event.clientY);
    },

     showPopup: function (x, y){
         var popup = document.getElementById("popup");
         popup.style.left = x + "px";
         popup.style.top = y + "px";
         popup.className = "reveal";
     }
};




           Avoid global functions and variables
         Create a single global (if necessary) and attach everything to it
var Controller = {
    addClass: function(element, className){
        element.className += " " + className;
    }
};




                 Throw your own errors
                  When you know a function will fail
var Controller = {
    addClass: function(element, className){
        if (!element) {
            throw new Error("addClass: 1st argument missing.");
        }
        element.className += " " + className;
    }
};




                 Throw your own errors
                  When you know a function will fail
var Controller = {
    process: function(items){
        if (items != null){
            items.sort();
            items.forEach(function(item){
                //do something
            });
        }
    }
};




                 Avoid null comparisons
var Controller = {
    process: function(items){
        if (items instanceof Array){
            items.sort();
            items.forEach(function(item){
                //do something
            });
        }
    }
};




                   Avoid null comparisons
           Test for precisely what you want to know if it matters
Avoid null comparisons
• Use instanceof to test for specific object types
   – object instanceof MyType
• Use typeof to test for primitive types
   – typeof value == "string"
   – BEWARE: typeof null == "object"
function validate(value) {
    if (!value) {
        alert("Invalid value");
        location.href = "/errors/invalid.php";
    }
}




               Separate config data
var config = {
    urls: {
        invalid: "/errors/invalid.php"
    },
    strs: {
        invalidmsg: "Invalid value"
    }
};


function validate(value) {
    if (!value) {
        alert(config.strs.invalidmsg);
        location.href = config.urls.invalid;
    }
}


                    Separate config data
Separate Config Data
•   All URLs needed by the JavaScript
•   Any strings that are displayed to the user
•   Any HTML that needs to be created from JavaScript
•   Settings (i.e., items per page)
•   Repeated unique values
•   Any value that may change in the future
https://github.com/nzakas/props2js
Automation
Make everyone’s life easier
Build Process




  Build
Build
 Add/Remove               Validate
  Debugging                Code

 Concatenate
                         Test Code
    Files

  Generate
                         Minify Files
Documentation

                Deploy
                 Files
Add/Remove
                                          Debugging




https://github.com/moxiecode/js-build-tools
Generate
                      Documentation




http://usejsdoc.org
Generate
                                         Documentation




http://yuilibrary.com/projects/yuidoc/
Generate
                                     Documentation




http://jashkenas.github.com/docco/
Validate
                     Code




http://jslint.com
Validate
                     Code




http://jshint.com
Minify Files




http://yuilibrary.com/projects/yuicompressor/
Minify Files




https://github.com/mishoo/UglifyJS/
Minify Files




https://developers.google.com/closure/compiler/
Build




https://ant.apache.org
Build




http://www.julienlecomte.net/blog/2007/09/16/
Build




https://github.com/cowboy/grunt
Build




http://weblog.bocoup.com/introducing-grunt/
Build


Development      Testing       Deployment
 Add/Remove     Add/Remove      Add/Remove
  Debugging      Debugging       Debugging
   Validate      Validate        Validate
    Code          Code            Code

  Test Code      Test Code       Test Code

  Generate      Concatenate     Concatenate
Documentation      Files           Files

                Minify Files    Minify Files

                                  Deploy
                                   Files
Recap
Remember
• Code style guidelines ensure everyone's speaking
  the same language
• Loose coupling of layers make changes and
  debugging easier
• Good programming practices allow for easier
  debugging
• Code organization and automation help to bring
  sanity to an otherwise crazy process
https://twitter.com/kylerichter/status/15101151292694529
Etcetera
•My company:     wellfurnished.com
•My blog:        nczonline.net
•Twitter:        @slicknet
•These Slides:   slideshare.net/nzakas

More Related Content

Maintainable JavaScript 2012

Editor's Notes

  1. Over the past couple of years, we&apos;ve seen JavaScript development earn recognition as a true discipline. The idea that you should architect your code, use patterns and good programming practices has really elevated the role of the front end engineer. In my opinion, part of this elevation has been the adoption of what has traditionally been considered back end methodologies. We now focus on performance and algorithms, there&apos;s unit testing for JavaScript, and so much more. One of the areas that I&apos;ve seen a much slower than adoption that I&apos;d like is in the area of error handling.How many people have an error handling strategy for their backend? How many have dashboards that display problems with uptime and performance? How many have anything similar for the front end?Typically, the front end has been this black hole of information. You may get a few customer reports here and there, but you have no information about what&apos;s going on, how often it&apos;s occurring, or how many people have been affected.
  2. So what have we talked about? Maintainable JavaScript is made up of four components.First is Code Conventions that describe the format of the code you’re writing.Second is Loose Coupling – keeping HTML, JavaScript, and CSS on separate layers and keeping application logic out of event handlers.Third is Programming Practices that ensure your code is readable and easily debugged.Fourth is creating a Build Process