Key Principles of Maintainable JavaScript

Key Principles of Maintainable JavaScript

Tutorial Details
  • Topic: JavaScript
  • Difficulty: Intermediate
  • Estimated Completion Time: 30 Minutes

JavaScript is a curious language. It’s easy to write, but difficult to master. By the end of this article, hopefully, you’ll transform your spaghetti code into a five-course meal, full of readable, maintainable yumminess!


Why is it So Tough?

The thing to remember, above all else when writing JS code, is that it’s a dynamic language. This means there are a lot of ways to do things. You don’t have to deal with strongly typed classes, or some of the more complex features from languages, like C# and Java. This is both a blessing and a curse.

The “hardness” of JavaScript is clearly evident when considering the following image:

The teeny tiny book on the left is Douglas Crockford’s MUST READ book, JavaScript: The Good Parts. Towering next to it, on the right, is, JavaScript The Definitive Guide, by David Flanagan.

While both of these books are excellent reads, The Good Parts illustrates that, although JavaScript has a LOT of stuff in it, the good parts can be summed up in a considerably shorter read. So, if you’re looking for a good, quick read, go with The Good Parts – and read it a few times!

This, naturally, led to a lot of sleepless nights for web developers.

You can read an article on the history of JavaScript here, but the gist of it is that Brandon Eich, in 1995, was hired by Netscape to design a language. What he came up with was the loosely typed language that we know as JavaScript. Over the years, it became “standardized” as ECMAscript, but, throughout all the browser wars, the various browsers implemented these features differently. This, naturally, lead to a lot of sleepless nights for web developers. This problem, when combined with the fact that JavaScript was considered to be most applicable for manipulating images and performing quick bits of validation, led JavaScript to, incorrectly, be viewed as a terrible language.

It’s time to fix that! While, yes, there are plenty of bad things about JavaScript, when used properly, it can be a fantastic language – and it’s dynamic nature will grow on you!


Making it Better

Namespaces

One of the downfalls of how JavaScript is implemented is that it operates on top of a global object. In the case of browsers, this will wind up being the window object. So, anytime that code like this is present on a page…

            function doStuff(){
                alert('I am doing stuff');
            }
            
            function doMoreStuff(){
               var images = document.images.length;
               console.log("There are " + images + "on this page");
            }
            doStuff();
            doMoreStuff();
            

The functions doStuff and the doMoreStuff functions are immediately available to the global window object.

This means that if anyone comes along and attempts to write a function, which is also called, doStuff, there will be a conflict! All script tags are basically taking the code within them, and running it against the window in the order that they are referenced in the HTML. As a result, the second person to implement doStuff will overwrite the first doStuff.

A common technique for eliminating this problem is to take advantage of either self-executing anonymous functions, or namespaces. The object-oriented folks reading this are likely already familiar with the concept of a namespace, but the basic idea is to group functions into different areas for re-usability.

            var NS = NS || {}; // "If NS is not defined, make it equal to an empty object"
            NS.Utils = NS.Utils || {};
            NS.Models = NS.Models || {};
            NS.Views = NS.Views || {};
            

This will prevent pollution of the global namespace, and will aid in readability for your application. Now, you simply define functions in their respective namespace. A commonly defined namespace is app, which manages the rest of the application.

Design Patterns and Practices

In every language, there exists a set of design patterns. Addy Osmani says…

Design patterns are reusable solutions to commonly occurring problems in software design.

There are lots, and, when used correctly, they can greatly impact your application’s maintainabilty. Addy wrote a great JavaScript design patterns book, called Essential Design Patterns. Absolutely give it a read!

Another commonly used pattern is the Revealing Module Pattern.

        NS.App = (function () {
            // Initialize the application
            var init = function () {
                NS.Utils.log('Application initialized...');
            };
            
            // Return the public facing methods for the App
            return {
                init: init
            };
        }());
        
        NS.App.init();
        

Above, an App function is defined within the NS object. Inside, a function variable for init is defined, and returned as an anonymous object literal. Notice that, at the end, there’s that extra set of parenthesis: }());. This forces the NS.App function to automatically execute and return. Now, you can call NS.App.init() to initialize your app.

The anonymous function above is a best practice in JavaScript, and is referred to as a Self-Executing Anonymous Function. Because functions in JavaScript have their own scope – i.e. variables defined inside of functions are not available outside of them – this makes anonymous functions useful in multiple ways.

            // Wrap your code in a SEAF
            (function (global) {
            
                // Now any variables you declare in here are unavailable outside.
                var somethingPrivate = 'you cant get to me!';
                
                global.somethingPublic = 'but you can however get to me!';
                
            }(window));

            console.log(window.somethingPublic); // This works...
            console.log(somethingPrivate); // Error
        

In this example, because this function is automatically executed, you can pass the window in to the executing part }(window));, and it will be made available as global inside of the anonymous function. This practice limits the global variables on the window object, and will assist in preventing naming collisions.

Now, you can start using SEAF’s in other areas of your application to make the code feel more modular. This allows for your code to be re-usable, and promotes good separation of concerns.

Here’s an example of a potential use for these ideas.

        (function ($) {
            var welcomeMessage = 'Welcome to this application!'
            
            NS.Views.WelcomeScreen = function () {
                this.welcome = $('#welcome');
            };
            
            NS.Views.WelcomeScreen.prototype = {
                showWelcome: function () {
                    this.welcome.html(welcomeMessage)
                        .show();
                }
            };
        }(jQuery));
        
        $(function () {
            NS.App.init();
        });
        
        // Modify the App.init above
        var init = function () {
            NS.Utils.log('Application initialized...');
            this.welcome = new NS.Views.WelcomeScreen();
            this.welcome.showWelcome();
        };
        

So, above, there are a few different things going on. Firstly, jQuery is passed as an argument to the anonymous function. This ensures that the $ is actually jQuery inside of the anonymous function.

Next, there’s a private variable, called welcomeMessage, and a function is assigned to NS.Views.WelcomeScreen. Inside this function, this.welcome is assigned to a jQuery DOM selector. This caches the selector inside the welcomeScreen, so that jQuery doesn’t have to query the DOM for it more than once.

DOM queries can be memory intensive, so please ensure that you cache them as much as possible.

Next, we wrap the App init within $(function(){});, which is the same thing as doing $(document).ready().

Finally, we add some code to the app initializer. This keeps your code nice and separated, and will be considerably easy to come back to and modify at a later day. More maintainability!

Observer Pattern

Another excellent pattern is the Observer Pattern – sometimes referred to as “Pubsub.” Pubsub essentially allows us to subscribe to DOM events, such as click and mouseover. On one hand, we’re listening to these events, and, on the other, something is publishing those events – for example, when the browser publishes (or announces) that someone clicked on a particular element. There are many libraries for pubsub, as it’s a short bit of code. Perform a quick Google search, and thousands of choices will make themselves available. One solid choice is AmplifyJS’s implementation.

        // A data model for retrieving news.
        NS.Models.News = (function () {
            var newsUrl = '/news/'
            
            // Retrieve the news
            var getNews = function () {
                $.ajax({
                    url: newsUrl
                    type: 'get',
                    success: newsRetrieved
                });
            };
            
            var newsRetrieved = function (news) {
                // Publish the retrieval of the news
                amplify.publish('news-retrieved', news);
            };
            
            return {
                getNews: getNews
            };
        }());
        

This code defines a model to fetch news from some kind of service. Once the news has been retrieved with AJAX, the newsRetrieved method fires, passing through the retrieved news to Amplify, and is published on the news-retrieved topic.

        (function () {
            // Create a news views.
            NS.Views.News = function () {
                this.news = $('#news');
                
                // Subscribe to the news retrieval event.
                amplify.subscribe('news-retrieved', $.proxy(this.showNews));
            };
            
            // Show the news when it arrives
            NS.Views.News.prototype.showNews = function (news) {
                var self = this;
                $.each(news, function (article) {
                    self.append(article);
                });
            };
        }());
        

This code above is a view for displaying the retrieved news. In the News constructor, Amplify subscribes to the news-retrieved topic. When that topic is published, the showNews function is fired, accordingly. Then, the news is appended to the DOM.

        // Modify this the App.init above
        var init = function () {
            NS.Utils.log('Application initialized...');
            this.welcome = new NS.Views.WelcomeScreen();
            this.welcome.showWelcome();
            
            this.news = new NS.Views.News();
            
            // Go get the news!
            NS.Models.News.getNews();
        };
        

Again, modify the init function from the app to add the news retrieval… and you’re done! Now, there are separate pieces of the application, each of which is responsible for a single action. This is known as the Single Responsibility Principle.


Documentation and Files/Minification

One of the keys to maintainable code of any kind – not just JS – is documentation and commenting. Comments can serve to be invaluble for new developers coming into a project – needing to understand what’s occurring in the code. “Why did I write that one line again?”. An excellent tool for generating documentation is called, Docco. This is the same tool that generates the documentation for the Backbone.js web site. Basically, it takes your comments, and places them side by side with your code.

There are also tools, like JSDoc, which generate an API style documentation, describing every class in your code.

Another thing, which can prove to be difficult when starting a new project, is trying to determine how to best organize your code. One way is to separate pieces of functionality into separate folders. For example:

  • /app.js
  • /libs/jquery.js
  • /libs/jquery-ui.js
  • /users/user.js
  • /views/home.js

This structure helps keep pieces of functionallity apart from one another. There are, of course, several ways to organize code, but all that really matters is deciding on a structure… and then rolling with it. Next, you can make use of a build and minification tool. There are lots of choices:

These tools will strip out whitespace, remove comments, and combine all specified files into one. This reduces the file sizes and HTTP requests for the application. Even better, this means that you can keep all of your files separated during development, but combined for production.


AMD

Asynchronous Module Definition is a different way of writing JavaScript code.

Asynchronous Module Definition is a different way of writing JavaScript code; it divides all code into separate modules. AMD creates a standard pattern for writing these modules to load in code asynchronously.

Using script tags blocks the page, as it loads until the DOM is ready. Therefore, using something like AMD will allow the DOM to continue loading, while the scripts are also still loading. Essentially, each module is divided into its own file, and then there’s one file that kicks off the process. The most popular implementation of AMD is RequireJS.

            // main.js
            require(['libs/jquery','app.js'], function ($, app) {
                $(function () {
                    app.init();
                });
            });
            
            // app.js
            define(['libs/jquery', 'views/home'], function ($, home) {
                home.showWelcome();
            });
            
            // home.js
            define(['libs/jquery'], function ($) {
                var home = function () {
                    this.home = $('#home');
                };
                
                home.prototype.showWelcome = function () {
                    this.home.html('Welcome!');
                };
                
                return new home();
            });
            

In the code snippet above, there is a main.js file, which is where the process begins. The first argument to the require function is an array of dependencies. These dependencies are a list of files that are required for app.js. As they finish loading, whatever the module returns is passed as an argument to the function callback on the right.

Then, there is app.js, which requires jQuery, as well as a view. Next, the view, home.js, only requires jQuery. It has a home function within it, and returns an instance of itself. In your application, these modules are all stored within separate files, making your application very maintainable.


Conclusion

Keeping your applications maintainable is extremely important for development. It reduces bugs, and makes the process of fixing ones that you do find easier.

“Friends don’t let friends write spaghetti code!”

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • Brian

    Your comment refers to modifying App.init “above” but you declare a new global variable…

    // Modify the App.init above
    var init = function () {
    NS.Utils.log(‘Application initialized…’);
    this.welcome = new NS.Views.WelcomeScreen();
    this.welcome.showWelcome();
    };

    • Mike S.

      I’m a little confused about this as well. I think he’s showing how the code that’s wrapped inside NS.App in the first ‘Design Patterns’ example would be modified to create a new View instance, but he just left out the wrapper in the ‘Modify…” example for brevity. Source files would help clarify this.

    • Liang

      ya. this also makes me very confused …

    • begs

      Yeah, this also confused me a lot …

    • Adam

      I think the author is saying use the code that he is showing in place of the original init method in App.init().

      I agree, however, that this is confusing.

      • Jonathan Creamer
        Author

        Yes, my mistake, I was trying to not have to duplicate the above code, but I probably should’ve. :)

  • Bertrand Dufresne

    Please stop using that picture or read this before: http://www.crockford.com/javascript/javascript.html

    “Nearly all of the books about JavaScript are quite awful. They contain errors, poor examples, and promote bad practices. Important features of the language are often explained poorly, or left out entirely. I have reviewed dozens of JavaScript books, and I can only recommend one: JavaScript: The Definitive Guide (5th Edition) by David Flanagan. (Attention authors: If you have written a good one, please send me a review copy.)” — Douglas Crockford (in JavaScript: The World’s Most Misunderstood Programming Language)

  • http://www.udgwebdev.com Caio Ribeiro Pereira

    Nice post man!! Javascript is the future of web!

    I have some posts about Javascript Design Patterns, I hope everybody enjoy it too

    http://www.udgwebdev.com/design-patterns-para-javascript-parte-1/
    http://www.udgwebdev.com/design-patterns-para-javascript-parte-2/

    and I`m creating some posts like a mini-course about Node.js

    http://www.udgwebdev.com/nodejs-para-leigos-introduo/

    I know it`s in portuguese, but I`m from Brazil! :D

  • Deejay

    which is more memory saver?

    a.
    $(function () {
    NS.App.init();
    });

    b.
    $(NS.App.init);

    or is it the same?

  • Mike S.

    Really great article – nicely done. Found the example code very relatable and helpful.

    There are a few things I’m confused about in the examples:

    1. Where should the code that defines the Utils, Models, and Views namespaces be located?

    2. You passed jQuery as an argument to the SEAF in the last ‘Design Patterns’ example, but didn’t do this in the examples in ‘Observer Pattern’. Why was this not necessary in the two Observer examples?

    3. In the second example in ‘Observer Pattern’ on line 11, should Login.prototype be News.prototype?

    4. If you write your JS code using AMD, do you still use namespaces, or is this an either/or choice?

    5. In your AMD example, should there be an init() method in app.js? Or does declaring the app.js dependency in line 2 automatically invoke showWelcome()?

    6. In the AMD example, does app.js really require jQuery or are you just showing how jQuery would be defined as a dependency if it were needed in the file?

    • http://elfsternberg.com Elf M. Sternberg

      I can answer the AMD questions.

      (4) It’s an either/or issue. You can use namespaces within a definition, but if you have to do that, you’re probably better off separating the two namespaces into separate definition files, and importing them. The automatically become namespaces, with locally assigned names, in the aggregating require().

      (5) No, it doesn’t need an init(). Home.js is defining the Object “home”, which exists when you invoke it by requiring it. You’re overthinking what require() and define() do; I know you are, because it’s exactly what I did when I started using AMD.

      (6) If you look closely, you’ll see that app.js uses jQuery’s $() function, which is the modern version of $(document).ready(). So, yes, it’s necessary there. Require() will only pull the jquery.js file down only once, regardless of how often it’s required() or included in a define().

  • http://inkwell.dotink.org Matthew J. Sahagian

    Good article. I’m not sure who the original author was, but there’s a boilerplate for Javascript classes which makes it pretty clear how to define equivalents to static and instantiated methods + public/private properties. I will tend to use self inside these types of definitions.

    var private_static_property = ‘value’;

    var self = function() {
    // instantiation constructor
    };

    var self.method = function() {
    // public static function
    // can access private_static_property
    };

    return self;

    Obviously using prototype for instantiated properties and methods. For the record, AMD is great. I’ve been using it with Dojo and it keeps things very clean.

  • Nikhil

    Clears most of of doubts. Thanks !

  • http://ferus.info Maciej Kołek

    Different question. How can i hide my JS code? I am working with AJAX powered by jQuery and… i don’t want others to see my ajax urls.

    • Ståle

      Hi! As far as I know, you can’t. Think of it this way: The JavaScript is to be run by the browser, so the browser has to see it. If the browser can see it, so can anyone else…
      Personally, I use random id’s stored in the SESSION variable in php, and references that in my ajax calls, like ‘resources.php?id=5215746624463″ to disable anyone not logged in from fetching anything.
      I’m sure others have different tricks up their sleeves, but it works for me…

  • pirate

    Thanks for the article

    “self-executing anonymous function” is misleading:
    http://benalman.com/news/2010/11/immediately-invoked-function-expression/

  • Cody

    should

    NS.Views.Login.prototype.showNews

    be ==>

    NS.Views.News.prototype.showNews

  • Dieter

    I use this to ensure the ‘this’ keyword is not the window object but in the local scope.

    var Module = new function()
    {
    var self = this;
    var factor = 0.5;

    this.make_half = function(x)
    {
    return self.factor * x;
    }
    };

    Module.make_half(5);
    // 2.5

    • Dieter

      Correction:

      var Module = new function()
      {
      var self = this;
      var factor = 0.5;

      this.make_half = function(x)
      {
      return factor * x;
      }
      };

      alert(Module.make_half(5));

  • gavin

    Sorry, off topic, but where did the pinterest story go?

  • http://www.gofootwork.com Tegan Snyder

    Great writeup!

  • James

    Overall great article and I passed it along to some JavaScript friends.

    But I think the explanation of AMD was not quite correct. AMD is more about how you define dependencies in your modules. Not so much about how they are loaded. So instead of developer managing the order in which each module is placed in a concatenated file the optimizer can analyze the dependencies of each module and build the concatenated file for you.

    Yes you can load the modules individually, but that is not the ideal situation for production. In production you would want to run the optimizer to build the modules into one large file. Or multiple larger modules depending on your application needs.

  • Kerry

    Confused here:

    // Show the news when it arrives
    NS.Views.Login.prototype.showNews = function (news) {
    var self = this;
    $.each(news, function (article) {
    self.append(article);
    });
    };

    The Login namespace is not mentioned anywhere else — yet you’re trying to call the function “showNews” — I don’t see how that can be referenced?

  • Potado

    Wow, thanks for that link to “Learning JavaScript Design Patterns” by Addy Osmani. This kind of thing is very useful for the project that I’m working on. Great article!

  • http://www.concrete5.org/r/-/365 synlag

    Nice article, thanks for this.

    BW
    Ron

  • http://www.altd.in Linish

    Very useful and informative article.

  • http://www.integrityinvoice.com Adeniyi

    Many thanks for this, really helpful! This should help our new developer intern.

  • http://www.joezimjs.com Joe Zim

    can we please get some better editors for this site to find the little grammar mistakes:
    - “Pubsub is essentially allows us…”
    - “This, naturally, [lead]->[led] to a lot of sleepless nights for web developers.”
    - and on…….

    I know you guys make enough money to hire a decent editor.

    • Jonathan Creamer
      Author

      Fixed. :)

  • http://cwebbdesign.tumblr.com Chris

    Nice simplified explanation of a few JavaScript approaches. Hopefully it whets the appetite and sparks the curiosity of others to take their next steps in learning to write better code! It’s a journey after all, one step at a time!

  • Nate

    Self-Executing Anonymous Function is technically incorrect. The function is not executing itself. Please read Immediately-Invoked Function Expression (IIFE) by Ben Alman… http://benalman.com/news/2010/11/immediately-invoked-function-expression/

  • http://uipress.com Bilal Niaz Awan

    I wrote some articles on design patterns intended for someone getting his/her hands into pattern based development. Thought might help too!

    http://www.uipress.com/patterns-in-javascript/

  • http://theusg.me Theus G.

    Awesome resource and good read. My brain is bursting with new ideas.

  • AntoxaGray

    Nettuts, are you gonna use RequireJS on your site?

  • hollandben

    Excellent article that covers most of the bases. Would have liked the read some more about RequireJS though