← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

JavaScript Module Patterns

javascript ES2015 Intermediate
debt(d6/e4/b7/t8)
d6 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5), but better. ESLint and TypeScript catch many patterns (require() in browser code, mixing CommonJS/ESM) automatically, and esbuild will fail visibly on incompatible module patterns. However, subtle issues like circular imports causing undefined values in ESM live bindings require runtime testing or careful code review, pushing this to d6.

e4 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3), but slightly worse. The quick_fix advises 'use ES modules everywhere + configure type: module in package.json,' which is a single parameter change. However, actual remediation of a mixed codebase requires identifying all require() calls and converting to import statements across potentially multiple files within a module boundary, making it e4 — more than a one-liner but still localized to a component.

b7 Burden Structural debt — long-term weight of choosing wrong

Closest to 'strong gravitational pull' (b7). Module choice is load-bearing across the entire codebase: once you commit to CommonJS or ESM, every dependency, build tool, and entry point must align. The choice shapes how the entire project is tooled (esbuild/webpack config), how packages are published, and how all future code is written. Changing module systems mid-project is a codebase-wide refactor.

t8 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The core misconception—that CommonJS and ESM are interchangeable—is directly false and contradicts how synchronous/asynchronous module loading works elsewhere (e.g., dynamic imports are async). Developers familiar with require() will assume import works the same way (synchronously, dynamically); the reality (static, asynchronous, parse-time analysis for tree-shaking) breaks that expectation. Circular imports behave differently between the two, adding another gotcha. This is t8, close to catastrophic, because the 'obvious' mental model (they're just different syntax for the same thing) is fundamentally wrong.

About DEBT scoring →

Also Known As

CommonJS ESM AMD UMD import export

TL;DR

The evolution of JS modules — IIFE (pre-modules), CommonJS (Node.js require/exports), AMD (browser async), UMD (universal), and ESM (native import/export, the standard).

Explanation

Module history: IIFE (Immediately Invoked Function Expression) created private scope before native modules. CommonJS (Node.js): synchronous require(), module.exports — still used in legacy Node. AMD (Asynchronous Module Definition): define() for browser async loading — largely replaced. UMD (Universal Module Definition): works in AMD, CommonJS, and global contexts — used in library builds. ESM (ECMAScript Modules, ES2015+): static import/export, tree-shakeable, works in browsers and Node.js — the modern standard. PHP: Composer autoloading is analogous to CommonJS require().

Common Misconception

CommonJS and ESM are interchangeable — CommonJS require() is dynamic and synchronous; ESM import is static (analysed at parse time) and asynchronous — this makes ESM tree-shakeable but incompatible with synchronous require().

Why It Matters

Mixing CommonJS and ESM in a Node.js project causes Cannot use import statement in a module errors — understanding module systems explains why and how to resolve the incompatibility.

Common Mistakes

  • Mixing require() and import in the same file — Node.js does not allow this without configuration.
  • Not setting type: module in package.json for ESM — .js files default to CommonJS.
  • Default exports everywhere — named exports are more refactoring-friendly and tree-shakeable.
  • Circular imports causing undefined values — ESM live bindings handle this differently from CommonJS.

Code Examples

✗ Vulnerable
// CommonJS — dynamic, not tree-shakeable:
const { helper } = require('./utils'); // All of utils loaded
module.exports = { myFunction };

// Mixing CommonJS and ESM — causes errors:
import { something } from './module';   // ESM import
const other = require('./other');        // CommonJS in same file — Error!
✓ Fixed
// ESM — static, tree-shakeable:
import { helper } from './utils.js';    // Only helper tree-shaken
export function myFunction() { }        // Named export
export default class MyClass { }        // Default export

// Dynamic import for code splitting:
const module = await import('./heavy-module.js'); // Loaded on demand

// package.json for ESM Node project:
// { "type": "module" }
// Files use .js extension with ESM syntax

Added 16 Mar 2026
Edited 22 Mar 2026
Views 33
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 1 ping F 0 pings S 2 pings S 0 pings M 0 pings T 2 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 1 ping T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 2 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 0 pings F
No pings yet today
No pings yesterday
Amazonbot 7 Perplexity 7 Unknown AI 3 Google 3 Ahrefs 2 Majestic 1 ChatGPT 1 SEMrush 1 Bing 1
crawler 23 crawler_json 2 pre-tracking 1
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: Low
⚡ Quick Fix
Use ES modules (import/export) in all new JavaScript — CommonJS require() is legacy Node.js; configure type: module in package.json for .mjs default
📦 Applies To
javascript ES2015 web cli
🔗 Prerequisites
🔍 Detection Hints
require() in browser code; mixing CommonJS and ESM; no tree-shaking due to CommonJS exports
Auto-detectable: ✓ Yes eslint typescript esbuild
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: Medium ✗ Manual fix Fix: Medium Context: File

✓ schema.org compliant