How to Write Highly Scalable and Maintainable JavaScript

Originally published at innoarchitech.com here on August 24, 2014.

Articles in This Series

  1. The “Wild West” Syndrome
  2. Namespacing
  3. Modules
  4. Coupling

Introduction

This is the first chapter in a series on how to write highly scalable and maintainable JavaScript. Proper JavaScript application design, architecture, organization, and loose coupling are critical to the following:

  • Performance
  • Functionality
  • Scalability
  • Usability
  • Reusability
  • Maintainability
  • Sustainability
  • Extensibility
  • Productivity

Software applications are typically broken into layers, or tiers when there are multiple physical machines involved. A traditional 4-layer application architecture might consist of a presentation layer, service layer, business logic layer, and data layer.

JavaScript applications tend to follow some form of the MV* pattern on the client side, with the layers mentioned above being highly applicable as well. These layers are certainly relevant when writing full stack JavaScript applications using Node.js for the server implementation.

Cross-cutting Concerns and Coupling

It’s very important to consider cross-cutting concerns and coupling when designing large-scale JavaScript applications. Some examples of cross-cutting concerns include error handling, validation, authentication, authorization, caching, and logging. They are therefore not specific to a particular layer, but are usually required by multiple layers.

Aspect-oriented programming (AOP) is one paradigm used to address cross-cutting concerns, which are also called ‘aspects’. A very good introductory article on AOP as applied to JavaScript can be found here. Another approach involves the creation of reusable concern-specific code modules that can be consumed across layers. The disadvantage here is that dependent modules “know” about cross-cutting interfaces and are therefore coupled to some extent.

Coupling between modules and components should be reduced as much as possible in order to promote maximum reusability, scalability, sustainability, maintainability, and testability. Usage of an event-driven pattern such as Pub/Sub (discussed in a later chapter) is a solid way to reduce this coupling.

Industry subject experts such as Addy Osmani and Nicholas Zakas argue that a module should be self-contained and able to fail and/or be removed without breaking the application. In addition, A module should be completely interchangeable with another module implementing the same interface, or event-driven behavior without breaking the application.

The “Wild West” Syndrome

In my experience, classical server-side applications are given very deliberate and appropriate treatment with respect to proper application design and architecture. JavaScript-heavy applications on the other hand are often largely neglected in this respect.

JavaScript is a loosely-typed dynamic language, which leads to a multitude of ways to accomplish a given implementation goal. As a result, JavaScript code bases tend to be organized and written with a “wild west” approach. Conversely, strongly typed and statically-compiled classical frameworks such as C#/.NET typically involve a more common way of organizing layers, modules, components, classes, and code in general.

Developers in both cases often write very different code to solve a given aspect of the problem domain (or implement a new feature), but it seems that JavaScript architecture and implementations can vary much more wildly as compared to their strongly-typed server-side counterparts.

Summary

Since there are many articles that go over various code implementations and syntax in detail, this series will be focused primarily on proper architectural concepts and strategies that are applicable to creating highly scalable and maintainable JavaScript applications. The discussion will also account for cross-cutting concerns and loose-coupling across application modules.

A great starting point for designing, organizing, and implementing scalable and maintainable JavaScript applications is to use modules and proper namespacing. An arguably better solution is to implement a modular framework based on a specification such as AMD (Asynchronous Module Definition), CommonJS, or the upcoming ECMAScript Harmony native module proposals.

Chapter two of this series will examine these approaches and their potential advantages in detail. The goal is to “rope in” the “wild west” syndrome and ensure that large-scale JavaScript applications are more modern, orderly, and well built.

With that, yee-haw and giddy up!

About the Author: Alex Castrounis founded InnoArchiTech. Sign up for the InnoArchiTech newsletter and follow InnoArchiTech on Twitter at @innoarchitech for the latest content updates.