JavaScript Architecture: Organization and Quality

12.05.2011

Never build large apps

Justin Meyer, the main guy behind JavaScriptMVC, said something I feel is a very simple principle every architect should ingrain into their brain:

The secret to building large apps is NEVER build large apps. Break up your applications into small pieces. Then, assemble those testable, bite-sized pieces into your big application.

I don’t think any other principle will carry more weight in architecture–especially with JavaScript. You may be thinking, “Oh, my application isn’t big enough to follow this principle.” Re-think this. Every application I’ve architected–even the smallest of the small–have benefited from this principle. The pieces of your application must be decoupled and cohesive as much as humanly possible to withstand the test of time. Each piece should be as black-box as possible–pass information over the wall and the next component does its job. And I don’t just mean different views of your app; I also mean individual, small components of your views, models, and everything you build. This presentation on Scalable JavaScript Application Architecture does a great job of explaining these principles in more detail.

When you think of JavaScript apps you may think of a few gigantic, spaghetti JavaScript files and a few beefy HTML files and a mess throughout. You might even think of fat strings of html text hand-concanated, escaped, and declared right within your JavaScript. Escape this mindset. That, indeed, is a rightful stigma of JavaScript apps because so many exist that fall into this category. We’re not going to be following that trend. We want to favor many small classes, files, and HTML templates over a few large ones. Small classes lead to small scope. Small scope leads to better maintainability and easier debugging because there is less your mind needs to comprehend. It also encourages strong, well-planned APIs and low coupling.

You may be thinking, “But JavaScript doesn’t have namespaces and everything’s asynchronously loaded and depends on everything else! And what about all the requests to the server? And how do I get all those HTML templates into my app? This is going to be a nightmare!” Don’t worry about it. We’ll get there. Don’t fall into the trap of making excuses for poor design just because you’re in JavaScript.

Admit what you don’t know

The only thing that’s guaranteed not to change is change. Therefore, never act like you know how your app will grow and change. Nicholas Zakas said it well:

The key is to acknowledge from the start that you have no idea how this will grow. When you accept that you don’t know everything, you begin to design the system defensively.

Naming conventions

There are many conventions in JavaScript–some more popular than the others. I find it important to maintain consistency with most JavaScript code out there. Unfortunately, I haven’t found empirical evidence showing which conventions are most popular, but I have done some rudimentary research and will propose a few seemingly most-popular conventions. Again, these are only suggestions. The most important point is that you agree on a standard as a team and stay consistent.

  • Names of directories and files should be all lowercase. Add hyphens between words.
  • Variable names should be camelCase.
  • Objects (functions) meant to be instantiated should start with a capital letter.
  • Private scope doesn’t exist in JavaScript except through closures. See the poorly-named module pattern for more info but I don’t really love the consequences of the pattern. Instead, I suggest prefixing names of members meant to be private with an underscore. It doesn’t provide true privacy, but it’s a pretty solid red flag that you shouldn’t use it publicly.

Testing

Strongly-typed languages (e.g., Java, C#, ActionScript 3) specify the type of each variable throughout the application. This means the compiler can raise red flags when you try to use an object in a manner that wasn’t intended by its type. In essence, strong typing can be considered to be a bunch of built-in, simple unit tests that are run each time the app is compiled.

JavaScript is not a strongly-typed language but instead is a very loosely-typed language. It’s much easier to make mistakes by using objects in ways that weren’t intended or just fat-fingering a variable name and you won’t find out about it until you run the code and see the error (a good IDE can also help). For this reason, I believe testing is even more important in loosely-typed languages.

There are many JS libraries available to help you test your code. QUnit seems to work well and is popular. If you don’t already have a favorite, I suggest to start there and research outward.

JsTestDriver can also be very useful (and happens to have a QUnit adapter). JsTestDriver allows you to run your tests within a variety of browsers. This can be very helpful since browsers aren’t consistent in their implementation. For example, I might write code using myArray.indexOf() which will work on most browsers but not older Internet Explorers. By running your tests within actual browsers these issues will crop up and you’ll find out about them before your users do.

Strict mode

The 5th version of ECMAScript (the standard upon which JavaScript is based) provides an option with which you can tell browsers to be more strict with your code. Newer browsers will respect it while older browser will stay lenient. Why would you want browsers to be more strict with your code? Because you want to decrapify your code. Strict mode provides the following (copied from MDN):

  • Strict mode eliminates some JavaScript pitfalls that didn’t cause errors by changing them to produce errors.
  • Strict mode 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 (Firefox 4 generally hasn’t optimized strict mode yet, but subsequent versions will).
  • Strict mode prohibits some syntax likely to be defined in future versions of ECMAScript.

I highly recommend invoking strict mode to keep your code squeaky clean.

Code Analysis Tools

A couple popular code analysis tools exist to likewise help decrapify your code. They scan your code and raise flags when they see you’ve done something dangerous or undesirable. They mainly focus on syntax, style, and structure. They do not verify that your logic is correct.

The first is JSLint. It’s “the original”. The second is JSHint. JSHint explains the difference:

JSHint is a fork of JSLint, the tool written and maintained by Douglas Crockford.

The project originally started as an effort to make a more configurable version of JSLint—the one that doesn’t enforce one particular coding style on its users—but then transformed into a separate static analysis tool with its own goals and ideals.

Both tools have online versions where you can copy-paste a snippet into a textarea and have the code analysis do its thing. This works okay for bits of code here and there, but even better is setting up the analysis code to run on your full app. Both tools have integrations to you allow you to analyze your app’s code from your IDE or command line. On our team, we run the tool from an Ant script that we can then run from the command line, our IDE, or our deployment servers. I highly recommend you run these tools often right off the bat. Note that each have flags you can set to be more or less strict in its analysis.

JavaScript Architecture: jQuery >>

Tags: , , , , , , , , , , ,


Comments

01.13.2012 / Ksenia said:

Hi Aaron, very interesting post series, especially as I’m discovering JS apps architecture.
Have a couple of questions, though. In his presentation on scalable JS arch Nicholas Zakas advises us to abstract out the frameworks (JQuery is in his list too). I wanted to ask your opinion on how much libraries/frameworks are allowed to be used in modules? For me it seems a little bit paranoic to build own abstraction around another abstraction built around DOM operations. I understand the implications that some day I will fall in love with some other lib, but is it worth the effort?
Another side of this same question is using backbone, does this also contradict to Zakas’ architecture and we should abstract out backbone?

Thanks again for your effort on working on this series!

Regards,
Ksenia

01.16.2012 / Aaron Hardy said:

Very observant and those are great questions. I think it comes down to your project, organization, and style. I do feel like completely abstracting the core libraries, though maybe ideal, is overboard in most cases and can be difficult and resource-intensive to do right. If you really like the features of Backbone then writing an abstraction layer would likely end up being basically a pass-through layer. The theory is right, but if you decide to change libraries, you’ll likely be changing not only because of speed, robustness, etc, but because of improved APIs. At that point, better APIs won’t make much of an impact without also changing your abstraction layer…which can defeat its purpose.

The line doesn’t have to be completely on one side or the other though. For example, I’d consider making a toggle button backbone-agnostic because it’s very possible someone in the organization might want to use it without backbone (even if it’s us in the future) and backbone isn’t crucial in something so granular. On the other hand, the more container-like (less granular) views are quite specific to my project and backbone really comes in handy so I feel more comfortable using backbone. Where that line starts or where you start abstracting is up to you and your team. How’s that for vague?


Leave a Comment

Your email address is required but will not be published.




Comment