Clean up that Javascripts folder!

Posted by Adam Lassek | September 14, 2010

DHH has mentioned during one of his talks that the javascripts folder of any project has an unfortunate tendency to become a “junk drawer” of sorts. The plethora of Javascript frameworks and plugins combined with a lack of built-in module support makes this kind of inevitable.

Over the last couple of months I started feeling this way about the current project we are actively developing, Salesguide. While Rails’ MVC design has kept us reasonably disciplined in our server-side code, there is a distinct lack of conventions for Javascript.

As a result, our Javascript was a complete mess. I don’t mean to cast blame on Scott, our Front-end Developer; this is largely the result of a company decision to focus on getting features released as quickly as possible, and we have been piling up technical debt in our code as a result. Even the most talented Javascript developer in the world would have to cut corners in that situation.

But there comes a time when technical debt must be paid, either by choice or otherwise. I’m going to talk about three different techniques we used to solve this problem.

Use unobtrusive Javascript

In fact we were already doing this, but it makes the following steps possible. We prefer the excellent jQuery library for Event wire-up and Ajax, and it’s great for user-interface code as well. I’ve begun to really appreciate the design of the Prototype library, and we actually use a subset of it in Salesguide, but the biggest source of pain were the things we were using jQuery for, although switching to Prototype wouldn’t have solved them. This was a result of a lack of convention rather than a failing of jQuery as a framework.

Install Sprockets

Sprockets is a Ruby library for Javascript that gives you the ability to split up your code into modular files. First it pre-processes your files to sort out dependencies, and then concatenates them all into a single file for deployment.

There is another system called Juicer which does much the same thing. Since Sprockets was written by a developer working for 37Signals, and the fact that Juicer did a some things that were a bit redundant for our environment, Sprockets was a better fit for us. But try them both out, and if there are more I don’t know about, feel free to leave a comment.

With module support, we were able to refactor our code into a sensible file structure mirroring our server-side views.

It may at first seem like a good idea to just put the event wire-up alongside the markup it belongs to, but you must resist that temptation at all costs for two reasons:

First, it violates the separation of concerns between client and server. In fact, it defeats the whole purpose of using unobtrusive Javascript in the first place if you think about it. Javascript is strictly a client-side concern, and so it doesn’t belong in your views.

Second, it causes unnecessary duplication. There are all kinds of situations where you might need the same wire-up code in multiple locations in your application, and placing your wire-up in the HTML encourages you to cut-and-paste instead of engineering the code correctly.

Furthermore, simply dumping it in a file in the javascripts folder isn’t a solution either, because then you have no way to knowing where the wire-up for a particular chunk of HTML is located.

Listen to JSLint’s advice, but don’t take it too seriously

Textmate can quickly run your code through JSLint, and we use it in our workflow to catch errors. This is also an important step in preparing your Javascript for packing & minification.

Jslint is pretty opinionated, and we don’t follow it to the letter, but nonetheless I think it is valuable for catching simple errors.

Establish conventions

Most of our woes up to this point was a simple lack of conventions when it comes to writing our Javascript code. One of the basic philosophies of the Rails framework is that your conventions don’t matter quite as much as simply having one; if you don’t have to think about what to name something or where to put it, you can spend your time thinking about more important things instead.

Here’s a set of coding conventions we have settled on:

1. Global variables are bad, mmkay?

This is pretty uncontroversial. Use as few global variables as possible. Our namespace for Salesguide is located in the SG variable. All of the aforementioned event wire-up is located in SG.Observers in sub-objects corresponding to the controller/action they belong to. We don’t have to wonder where our wire-up is located anymore, and that makes it much easier to spot duplication.

2. Use private functions and variables

If you grok closures well enough, this is pretty easy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
SG.Flash = (function () {

  //this is private
  function alert (style, message) {
    // some code here...
  }

  function notice (message) {
    //some more code here...
  }

  function error (message) {
    //yet more code
  }

  return {
    notice: notice,
    error: error
  };

})();

This is a simple set of functions that add notice or error messages on the page in response to AJAX calls. Since ‘notice’ and ‘error’ are explicitly returned, they are publicly accessible. This is a variation of Douglas Crockford’s Module Pattern.

Any variables and functions that are not explicitly returned are effectively private. Thanks to Javascript’s closure support they are still accessible to functions that reference them. I recommend looking at the source code to the Prototype framework; this pattern is used all over the place there.

All of this is wrapped in a slightly weird looking function. The empty set of parentheses at the end means it is automatically executed as soon as the Javascript interpreter reads it, and the returned object is stored in SG.Flash. We’ve wrapped the function itself in parentheses by convention to avoid confusion; we wouldn’t want people mistakenly thinking SG.Flash is a function, so the parentheses are a convention that has arisen to tell you that it is self-executing.

3. Modularize your event wire-up

There are many situations that we have run into where it makes sense to execute event listener code both implicitly and explicitly. This technique leverages the compositional nature of Javascript. Bear with me; this is going to get a little weird.

The accounts#show page in Salesguide has several sections that are logically self-contained, so it made sense to create separate functions for these event listeners:

1
2
3
4
5
6
7
8
9
10
11
12
13
SG.Observers.Accounts.Show = (function () {
  function quickNote() {}
  function contacts() {}
  function journalEntries() {}
  function tasks() {}

  return {
    quickNote:        quickNote,
    contacts:         contacts,
    journalEntries:   journalEntries,
    tasks:            tasks
  };
})();

But this suffers from an immediate problem: the more functions you create, the more you have to execute on the page. This means you have to remember to update the views every time you add another function. This is what our wire-up code looks like:

1
2
3
4
5
6
$(document).ready(function () {
  SG.Observers.Accounts.Show.quickNote();
  SG.Observers.Accounts.Show.contacts();
  SG.Observers.Accounts.Show.journalEntries();
  SG.Observers.Accounts.Show.tasks();  
});

This is kind of annoying, so we define a top-level callback function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
SG.Observers.Accounts.Show = (function () {
  return function () {
    var self = arguments.callee;

    self.quickNote();
    self.contacts();
    self.journalEntries();
    self.tasks();
  }
})();

(function () {
  function quickNote() {}
  function contacts() {}
  function journalEntries() {}
  function tasks() {}

  $.extend(SG.Observers.Accounts.Show, {
    quickNote:      quickNote,
    contacts:       contacts,
    journalEntries: journalEntries,
    tasks:          tasks
  });
})();

Instead of adding the various wire-up functions to an inline object, we’re attaching them to a master function, which then has the ability to call them itself. Weird, huh?

But this is a really powerful design, because our wire-up now looks like this:

1
$(document).ready(SG.Observers.Accounts.Show);

The important thing is that we can still call them individually if we need to, just as when Show was a plain object.

By using better tools, establishing conventions, and sticking to the Good Parts, we’ve turned some rather dreadful code into a much more hygenic codebase that is actually enjoyable to work on.

1 Comment

Bryan Roth

about 3 years ago
Great blog post, Adam! I love working with JavaScript so I found this post particularly entertaining.