index.js requires AbstractNode.js The module loader starts loading AbstractNode.js and running the module code. The thing it first encounters is a require (import) statement to Leaf So the module loader starts to load the Leaf.js file. Which, in turn, starts by requiring Abstractnode.js . AbstractNode.js is already being loaded, and is immediately being returned from the module cache. However, since that module did not run beyond the first line yet (the require of Leaf ), the statements introducing the AbstractNode class have not yet been executed! So, the Leaf class tries to extend from the undefined value, rather than a valid class. Which throws the runtime exception shown above. BOOM!

Fix attempt 1

So, it turns out that our circular dependency causes a nasty problem. However, if we look closely it is pretty easy to determine what the loading order should be:

Load the AbstractNode class first Load the Node and Leaf class after that.

In other words, let’s define the AbstractNode class first, and then have it require Leaf and Node . That should work, because Leaf and Node don’ t have to be known yet when defining the AbstractNode class. As long as they are defined before AbstractNode.from is called for the first time we should be fine. So let’s try the following change:

Turns out, there are a few problems with this solution:

First, this is ugly and doesn’t scale. In a large code base, this will result in moving imports randomly around until stuff just happens to work. Which is often only temporary, as a small refactoring or change in import statements in the future can subtly adjust the module loading order, reintroducing the problem.

Secondly, whether this works is highly dependent on the module bundler. For example, in codesandbox, when bundling our app with Parcel (or Webpack or Rollup), this solution doesn’t work. However, when running this locally with Node.js and commonJS modules this workaround might work just fine.

Avoiding the problem

So, apparently, this problem cannot be fixed easily. Could it have been avoided? The answer is yes, there are several ways to avoid the problem. First of all, we could have kept the code in a single file. As shown in our initial example, that way we can solve the problem as it gives full control over the order in which module initialization code runs.

Secondly, some people will use the above problem as argument to make statements like “One should not use classes”, or “Don’t use inheritance”. But that is an over-simplification of the problem. Although I agree that programmers often resort to inheritance too quickly, for some problems it is just perfect and might yield great benefits in terms of code structure, reuse or performance. But most importantly, this problem is not limited to class inheritance. Exactly the same problem can be introduced when having circular dependencies between module variables and functions that run during module initialization!

We could re-organize our code in such a way that we break up the AbstractNode class into smaller pieces, so that AbstractNode has no dependencies on Node or Leaf . In this sandbox the from method has been pulled out the AbstractNode class and put into a separate file. This does solve the problem, but now our project and API is structured differently. In large projects it might be very hard to determine how to pull this trick off, or even impossible! Imagine for example what would happen if the print method depended on Node or Leaf in the next iteration of our app…

Bonus: an additional ugly trick I used before: return base classes from functions and leverage function hoisting to get things loaded in the right order. I’m not even sure how to explain it properly.

The internal module pattern to the rescue!

I have fought with this problem on multiple occasions across many projects A few examples include my work at Mendix, MobX, MobX-state-tree and several personal projects. At some point, a few years ago I even wrote a script to concatenate all source files and erase all import statements. A poor-mans module bundler just to get a grip on the module loading order.

However, after solving this problem a few times, a pattern appeared. One which gives full control on the module loading order, without needing to restructure the project or pulling weird hacks! This pattern works perfectly with all the tool-chains I’ve tried it on (Rollup, Webpack, Parcel, Node).

The crux of this pattern is to introduce an index.js and internal.js file. The rules of the game are as follows:

The internal.js module both imports and exports everything from every local module in the project Every other module in the project only imports from the internal.js file, and never directly from other files in the project. The index.js file is the main entry point and imports and exports everything from internal.js that you want to expose to the outside world. Note that this step is only relevant if your are publishing a library that is consumed by others. So we skipped this step in our example.

Note that the above rules only apply to our local dependencies. External module imports are left as is. They are not involved in our circular dependency problems after all. If we apply this strategy to our demo application, our code will look like this:

When you apply this pattern for the first time, it might feel very contrived. But it has a few very important benefits!