bitHound Blog

Circular Dependencies in Node.js

In the Node.js world, we've gotten use to modularizing our own code and requiring it in where necessary. It's a great pattern to use since it keeps your code organized and your files as purpose-driven as possible.

Recently, we had a bug show up in bitHound where we required a module and got back an empty object– we had a circular dependency. The code was written, tested and reviewed but we missed a small problem– a new require statement was added to use some shared code that also introduced a circular dependency that caused one of our views to not render.

What are they?

A circular dependency describes a relation between two or more modules which depend on each other to function. It's quite simple. Say you have 3 files: a.js, b.js and c.js:

a.js

var b = require('./b.js');

module.exports: {  
  start: function () {
    b.continue();
  },
  enabled: function () { return true; }
}

b.js

var c = require('./c.js');

module.exports: {  
  continue: function () { 
    c.finish();
  }
}

c.js

var a = require('./a.js');

module.exports: {  
  finish: function () { 
     if (a.enabled()) {
       console.log('enabled');
     }
  }
}

This example doesn't really look that troublesome, but there is a chance that the reference to the a module in c.js could be an empty object (similar to what happened to us).

The Node.js docs touch briefly upon this and pretty much conclude that you may have unexpected results. While Node does support circular dependencies, you are probably better off restructuring your code to not require it.

They can be removed by ether combining two modules or splitting out a third module to accomplish what you need.


Discover your circular dependencies with bitHound

Once this problem snuck into our site, we quickly added support so that future issues like this would be detected right inside bitHound.

Circulare Dependencies on bitHound

bitHound will analyze your codebase to find and report on circular dependencies between any of your project modules. We ourselves saw the importance of this when trying to diagnose a bug only to realize it was all being caused by a circular dependency. While circular dependencies in a chain of files doesn't always point to a bug, if you know a file is causing some grief and it has circular dependencies it can be a good place to start your bug triage.

bitHound identifies risks and priorities in your Node.js projects.