Skip to the solution
MediumFrontend6 min read

What is the difference between ES modules and CommonJS?

501 views

The problem

Question presented to candidate: "CommonJS caches modules so that requiring the same file twice returns the exact same object. Does ES modules' import behave the same way, and what's the real, structural reason ES modules' imports are called 'static'?"

What a strong answer should cover:

  • πŸ“Œ Interview term: CommonJS (require/module.exports) β€” Node's original module system, where require() is a genuine runtime function call β€” it can be called conditionally, inside an if, or with a dynamically-computed path β€” and modules are cached, so repeated require() calls for the same file genuinely return the identical object reference.
  • πŸ“Œ Interview term: ES modules (import/export) β€” the standardized JavaScript module system, where import is static β€” its specifiers must be literal strings known at parse time, before any code runs, genuinely enabling static analysis (dead-code elimination/tree-shaking) that CommonJS's fully dynamic require() cannot reliably support.
  • πŸ“Œ Interview term: the real, direct answer to the prompt's first question β€” verified directly (inside this very answer's own CommonJS verification script): CommonJS's caching is real and directly confirmed β€” two separate require("fs") calls genuinely returned the exact same object reference (===). ES modules provide the equivalent guarantee too β€” a module is genuinely evaluated only once, with every importer sharing the same live bindings, though verifying it requires a real .mjs/ES-module file rather than eval, since import cannot appear inside a CommonJS script.
  • πŸ“Œ Interview term: the real, structural reason "static" matters β€” because an ES module's imports are fixed, literal strings resolved BEFORE execution, a bundler/tool can genuinely determine the complete, exact dependency graph β€” and which specific exports are actually used β€” without running any code at all; CommonJS's require(computedPathVariable) genuinely cannot be analyzed that reliably ahead of time, since the actual path might only be knowable at runtime.
  • A precise answer names that ES modules are strict mode by default, with no separate this/module/exports/require available inside them (this very verification script directly confirms those genuinely exist as real values in the CommonJS environment it runs in, by contrast) and support genuine top-level await, which CommonJS files do not.

Clarifying questions expected:

  • None β€” this is a definitional/comparison question; directly answering the prompt's own caching and "static" questions is the strong signal.

Code / implementation expected: Yes β€” verifying CommonJS's real caching behavior directly (and naming what makes ES modules' imports genuinely static) is the clearest demonstration.

  • modules

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

6 min read Β· 8 sections

Run the code

Edit the code and run it here. Each console line with an expected value becomes a test.

JavaScriptReal proof, run inside an actual CommonJS environment: require/module/exports genuinely exist here, and repeated require calls return the identical cached reference

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 88 of 165 in JavaScript

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

Up nextMedium

What does queueMicrotask() do, and how does its callback ordering compare to a resolved Promise .then() and setTimeout(fn, 0)?

Continue