CommonJS: Here to stay or gone tomorrow?

CommonJS: Here to stay or gone tomorrow?

CommonJS (CJS), a standard for modules in JavaScript, remains in widespread use despite being superseded by an official JavaScript standard called ECMAScript modules (ESM) – causing the future of CJS to be hotly debated.

Deno product marketing manager Andy Jiang posted that JavaScript is “being sabotaged by its own baggage from the past … CommonJS.”

Deno is a JavaScript or TypeScript runtime which was created by Ryan Dahl, also the inventor of the older runtime Node.js. Deno was intended to be a modern equivalent to Node.js, fixing issues Dahl perceived with the earlier effort, and one of its features is first-class support for ESM. That said, because of the widespread use of CJS modules, Deno was given a “Node.js compatibility mode” in version 1.15, and since improved, which supports CJS modules. According to the docs, “CommonJS resolution is implemented as in Node.js and there should be no observable differences.”

Jiang’s argument though is that CJS has inherent problems, including synchronous module loading, less amenability to “tree-shaking”, where unused code is removed before bundling for deployment, and the biggest issue, that it is not browser-native. CJS modules have to be transpiled before deployment, if they are to run in the browser, which means a build process is required.

Node.js though is the dominant JavaScript runtime for out of browser use, and was established before ESM was standardized. The Node team has added support for ESM but has continued CJS compatibility as well. Many Node.js libraries require CJS, and developers accustomed to Node have no strong incentive to switch. The other option for library developers is to support both, which adds both bloat and maintenance burden.

It is easy to state, as Jiang does, that “CommonJS simply doesn’t cut it. ESM is a better solution for developers”, but that does not make it go away.

Bun creator Jarred Sumner, whose JavaScript runtime based on the Zig language and the JavaScriptCore engine has attracted attention for its fast performance, entered the debate with a post last week countering the Deno pitch. Sumner noted statistics from late last year which show that downloads of CJS packages stand at 73.6%, far ahead of ESM. “We think that better tooling can solve today’s developer experience issues with CommonJS and ESM interop,” said Sumner.

Sumner also said that CJS modules start faster. “ES Modules are slower by design. They need two passes in order to bind imports to exports,” he said. This can be an issue in the case of serverless applications which constantly start up new instances. In one benchmark, “with Node.js the difference was 1.8x)” for a cold start, Sumner said.

His key point though is that millions of npm modules use CJS, some of which are no longer actively maintained and will never be updated, but remain “critically important to existing projects.” According to Sumner, “We will never hit a point where all packages can be expected to use ES modules”.

Bun supports both ESM and CJS, and Sumner added that “As of Bun v0.6.5, the Bun runtime natively implements CommonJS,” as opposed to transpiling CJS modules to a form of ESM.

The dilemma with legacy technology is that the longer it is supported, the more time it takes to displace it; but not supporting it comes at a high cost. The late arrival of Node compatibility in Deno perhaps shows that Dahl’s team made wrong assumptions about the willingness of developers to transition, despite technical advantages in ESM. Despite publicizing the shortcomings of CJS, in practice Deno has been forced to support it.

A debate on Hacker News shows that developers too are divided on the subject. “Node is making the same mistake that Microsoft made with Windows, that Apple did not make with OSX – rather than letting go of a system that’s been outgrown and forcing the userbase to grow, they cling to the old way and the old API,” said one developer; but another said that “the benefits of ESM are not compelling enough to rewrite everything. Browser-native module loading is a niche use case which can never be as performant as bundling.”