JavaScript Module Systems: CJS vs ESM vs UMD (Interview Deep Dive)

Medium•

JavaScript module systems evolved to support different environments and constraints.

Understanding their differences is essential for debugging bundler issues, optimizing performance, and designing scalable frontend systems.

Asked In

Quick Navigation: CommonJS (CJS) • ES Modules (ESM) • UMD • Core Differences • Modern Usage • Interop (Very Important) • Common Pitfalls • Interview Scenarios

Quick Decision Guide

Senior-Level Decision Guide:

- ESM = modern standard (tree-shaking, static analysis) - CJS = legacy but still common in Node ecosystem - UMD = mostly for legacy library distribution

👉 Default choice today: ES Modules

Interview framing: The real difference is static vs runtime module resolution and its impact on performance and tooling.

CommonJS (CJS)

CommonJS

Used primarily in Node.js.

Key Characteristics

•synchronous loading
•runtime resolution
•dynamic imports allowed

Example

const math = require('./math');

Key Insight

Because it runs at runtime, bundlers cannot easily optimize it.

ES Modules (ESM)

ES Modules

Modern JavaScript module system.

Key Characteristics

•static analysis (compile-time)
•supports tree-shaking
•async loading
•supports top-level await

Example

import { add } from './math.js';

Key Insight

Because imports are static, tools can remove unused code.

UMD

Legacy pattern to support multiple environments.

Key Characteristics

•works in browser + Node + AMD
•larger bundle size

Modern Reality

UMD is rarely used today except for CDN libraries.

Core Differences

Key Difference

👉 CJS = runtime

👉 ESM = compile-time

This affects:

•performance
•tree-shaking
•tooling

Example

// Not tree-shakeable
const lib = require('./lib');

// Tree-shakeable
import { usedFn } from './lib';

Modern Usage

What is used today?

•Frontend → ESM
•Modern Node → ESM (type: module)
•Legacy Node → CJS

Most modern tools (Vite, Rollup, Webpack 5) prefer ESM.

Interop (Very Important)

Interop Issues

Common mistake

import something from 'cjs-module';

May behave differently because:

•CJS exports a single object
•ESM has named exports

Fix

import pkg from 'cjs-module';

Rule

CJS ↔ ESM interop is not always clean.

Common Pitfalls

Pitfalls

1. Mixing require and import

2. Default export confusion

3. Tree-shaking not working with CJS

4. Node ESM config issues (`type: module`)

👉 These are common interview traps.

Interview Scenarios

Key Questions

Q: Why prefer ESM?

→ tree-shaking + static analysis

Q: Why is CJS still used?

→ legacy + Node ecosystem

Q: When use UMD?

→ library distribution (legacy)

One-line Answer

👉 "ESM is the modern standard because it enables static analysis and better optimization."

Key Takeaways

1ES Modules are the modern JavaScript standard
2CommonJS is runtime-based and less optimizable
3UMD is mostly legacy
4Static vs runtime resolution is the core difference
5Tree-shaking works only with ESM
6Interop between CJS and ESM can be tricky