Defensive JavaScript? Yes, Please!

Every developer knows the feeling of hours wasted hunting down some obscure bug. Programming would be so much faster and more enjoyable if we could avoid these bugs before they happen. Since no magical solution is on the horizon, we can at least write our code as defensively as possible. But what does defensive code really mean? According to Wikipedia:

Defensive programming is a form of defensive design intended to ensure the continuing function of a piece of software under unforeseen circumstances.

(So yeah, they used the word "defensive" in the definition.)

As a JavaScript developer whose background is in Java, I have been seriously thinking about how defensive coding techniques might be applied to the JavaScript world. JavaScript is not particularly well-suited to this style. It is very dynamic and there is no built-in compile-time type or syntax checking. Every JavaScript developer has probably experienced the pain of prototype overriding and global namespace conflicts.

It's not impossible to write code defensively in JavaScript, but it takes much more effort than in Java or other languages that take active steps to protect developers from themselves.

Here are a few tips that I would recommend for JavaScript developers who want to minimize the chance of hard-to-spot errors creeping into their code.

Picking lint

When I installed a linting plugin into my editor, it had a dramatic effect on my JavaScript code quality. Hopefully everyone already knows about JSLint. It performs static analysis on JavaScript code and flags mistakes and poor coding practices that are much harder to track down at runtime. Having it lurking in the background in your editor lets you catch a lot of problems before they even happen.

Even if you have a linting step in your build scripts (which you should), there is no substitute for the real-time feedback of an editor plugin. If your editor doesn't support a linting plugin, you are probably using the wrong editor.

Encapsulation everywhere

Encapsulation makes it harder to (mis)use variables in inappropriate places. JavaScript does not have private methods out of the box, but we can take advantage of function scoping to achieve a similar effect. As you probably know, when you declare a new variable without the var keyword (JSLint complains loudly about this), it is created in global scope and can therefore cause conflicts with existing variables or third-party code.

This is why you should use the var keyword for every single variable declaration to scope it to the containing function. You can seize on this fact and achieve simple encapsulation using the wonderfully named immediately-invoked function expression or IIFE. This technique should be very familiar to Node.js developers.

var Foo = function() {
    var privateBar = 0; // We are sure nothing but this function scope can modify this

    this.incrementPrivateVar = function() {
        privateBar++;
    };

    this.printPrivateVar = function() {
        console.log(privateBar);
    };
};

// We use an IIFE so that all variable declarations are
// invisible outside the block
(function() {
	var innerState;

	// This function cannot be seen or changed outside
    // the IIFE
    var changeName = function(name) {
    	// perform some decoration of inner state
    	innerState = 'Stranger ' + name;
    };

    Foo.prototype.joinSession = function(name) {
		changeName(name);
        console.log('Hello ' + innerState);
    };

    Foo.prototype.secondPublicMethod = function() {
        console.log('I dare you to override me.');
    };
})();

// Does nothing because the function is declared
// inside the IIFE.
changeName = null;

// Because the function is declared on the prototype,
// anybody can override it
Foo.prototype.secondPublicMethod = function() {
    console.log('Oh no! You've been hacked.');
};

var foo = new Foo,
    bar = new Foo;

// We used function scoping properly so the original
// `changeName` method is used
foo.joinSession('John Doe');
// Whoops, this one has been overwritten
foo.secondPublicMethod();
foo.incrementPrivateVar();
// returns 1
foo.printPrivateVar();
// returns 0 since the private variable is tied to a
// specific instance of constructor
bar.printPrivateVar();

Organize your code in small modules wrapped in IIFEs and you will prevent a lot of subtle mistakes.

Let those variables be constant!

Wait a second... Constant? Variable? Isn't that an oxymoron? Well yes, but in the real world there are plenty of cases (maybe even the majority) where a "variable" does not need to be changed from its initial value.

Scala has the great concept of val (immutable value) versus var (mutable variable). Purely functional languages tend to be even more strict about this; Haskell and Clojure, for example, offer only immutable variables and require special mechanisms to achieve mutability.

JavaScript has the const keyword for creating constant values, but browser support is still spotty. Use it if you can. If not, think twice before you modify the value of an existing variable.

var foo = function(operand1, operand2, callback) {
    var sum = operand1 + operand2;

    // ... a lot of code

    // BAD!
    // A new team member wants to use the sum,
    // process it and pass it to a callback.
    // But wait! Do they really have to modify the
    // original variable?
    // This may inadvertently break code further down
    // in the function.
    sum += 100;
    callback(sum);

	// BETTER!
	// Instead, copy the modified value to a new variable.
    var mySuperSum = sum + 100;
    callback(mySuperSum);

    // ...a lot of code

    return sum;
};

 

Immutability matters

Obviously there are cases when we really need to use a variable. Let's say we need to increment a date. Consider this ugly code snippet:

var startDate = new Date();

for (var i = 0; i < 7; i++) {
    startDate = new Date(startDate.getTime() + 86400000);
    console.log(startDate);
}

This code contains many anti-patterns. Most experienced developers prefer using a date wrapper instead of the standard Date class as it simplifies basic manipulation like adding and subtracting dates. With this is in mind, we can improve the code by rewriting it to use Moment.js.

var startDate = moment();
for (var i = 0; i < 7; i++) {
    startDate.add(1, 'days');
    console.log(startDate);
};

Now consider the naming of the variable. As Phil Karlton once said:

There are only two hard things in Computer Science: cache invalidation and naming things.

Mutable variables make naming decisions even more difficult, since the broader domain of possible values make it less clear exactly what they are being used for.

The root problem with this code is therefore the mutability of the datetime instance. It is common in software engineering to use models that correspond to real-world entities. In this case, the datetime fills the role of a timestamp. And a timestamp shouldn't change, right? Besides the conceptual considerations, it is also easier to overlook bugs when using mutable datetime instances.

var today = moment(),
    tomorrow = moment().add(1, 'days');

This is a classic example of how to produce a subtle bug that will one day give you hours of fun-filled debugging "pleasure". There are no doubt countless repos on GitHub that do something similar. If we execute this code right at midnight, so the first line is executed right before midnight and the second right after, the difference between the two variables will be two days instead of one.

"But the chance of that happening is so small," you might protest. "Why even worry about it?" Well that's what defensive JavaScript is all about. If this code is used in e.g. a Node.js service that handles webhooks from a payment gateway, a seemingly minor issue could have major consequences.

var today = moment(),
    tomorrow = today.clone().add(1, 'days');

This is better but it is still easy to make a mistake by forgetting the clone. One might argue that Moment.js itself is not written defensively enough. If the add method didn't mutate the original datetime instance, we could write the code like this:

// Better but unfortunately this would cause Moment.js
// to mutate `today`.
var today = moment(),
    tomorrow = today.add(1, 'days');

 

String concatenation

I have come across code like the following many times while doing code reviews:

el.text(a + b);

It is far from obvious what is going on here. For starters, it would be worth the effort to choose understandable names for the variables. But even if they are renamed to something more meaningful like firstName and lastName, they might still contain an unexpected data type like an integer. Did the author intend in that case to stringify and concatenate the two numbers, or to sum them?

The other problem is that the value of the variables might be null or undefined. Since these stringify to "null" and "undefined", the following improved snippet still leaves something to be desired:

name.text(title + ' ' + firstName + ' ' + lastName);

For this reason I strongly discourage the use of the plus operator for string concatenation (and String.prototype.concat is no better). Instead, use a library with a proper string formatting API or even ES6 template strings.

Where there's an if, there's an else

How many times have you written something like this?

var foo = 'default value';
...
if (condition) {
  foo = 'bar';
}

I find this code relatively difficult to read and understand since what is conceptually an else branch is simulated by the default value. This isn't such a big deal in this short snippet, but imagine what happens as it inevitably grows more complex over time. It also breaks the immutability of the default value when the condition is met. It is better to write the else condition explicitly:

var foo;
...
if (condition) {
  foo = 'bar';
} else {
  foo = 'default value';
}

 

Always wrap blocks with braces

Why would you want to wrap one line of code in braces? It looks ugly! There's no arguing with taste, but braces are essential if you want to code defensively.

if (condition)
	foo();
bar();

This looks innocent enough, but in the real world you may comment out the foo() call while hunting some bug, leading to unexpected and undesirable behavior. Wrapping the block in braces also makes refactoring easier since it minimizes changes (and makes the diff more readable) if you end up adding more code to the block later.

 

Only the paranoid (and defensive) survive

To sum it all up: programming is generally about working in a team. By writing code as if we expect someone to break it, we minimize the risk that they actually do.

Tomas Weiss

Tomas Weiss

Full-stack JavaScript developer at Salsita