DEV Community

Sai Swaroop Bijinapalli
Sai Swaroop Bijinapalli

Posted on

Week-06 Day 1: CommonJS vs ESM and Modules in JavaScript

JavaScript Modules: CommonJS vs ESM and Why Modules Matter

When a JavaScript application is small, it is possible to keep everything inside a single file. But as an application grows, putting all the code in one file becomes difficult to understand and maintain.

For example, imagine an application containing:

  • User authentication
  • Database operations
  • Payment processing
  • Notification services
  • Utility functions
  • API routes

If all of this code is placed in one JavaScript file, the file can quickly become difficult to manage.

This is where JavaScript modules become important.

What is a Module?

A module is a separate, self-contained piece of JavaScript code that handles a particular responsibility.

For example, instead of having one large file:

app.js
Enter fullscreen mode Exit fullscreen mode

we can organize the application as:

project/
│
├── app.js
├── user.js
├── database.js
├── payment.js
└── utils.js
Enter fullscreen mode Exit fullscreen mode

Each file becomes responsible for a particular part of the application.

The important idea is:

A module allows us to organize code into separate units and share only the functionality that other parts of the application need.


Why Do We Need Modules?

Modules solve several problems.

1. Code Organization

Instead of putting everything into one file, related functionality can be grouped together.

For example:

user.js
Enter fullscreen mode Exit fullscreen mode

can contain user-related functionality, while:

database.js
Enter fullscreen mode Exit fullscreen mode

contains database-related functionality.


2. Reusability

Suppose we create an add() function.

Instead of rewriting it in multiple files, we can export it from one module and reuse it.


3. Avoiding Global Variables

Without modules, variables can accidentally become globally accessible.

Modules provide a controlled scope for variables and functions.


4. Maintainability

When functionality is separated into modules, changing one part of the application becomes easier.

For example, if database logic is inside:

database.js
Enter fullscreen mode Exit fullscreen mode

we don't need to search through the entire application to find database-related code.


CommonJS

CommonJS is a module system traditionally associated with Node.js.

It uses:

require()
Enter fullscreen mode Exit fullscreen mode

for importing modules and:

module.exports
Enter fullscreen mode Exit fullscreen mode

for exporting functionality.

Creating a CommonJS Module

Suppose we have:

math.js
Enter fullscreen mode Exit fullscreen mode

Inside it:

function add(a, b) {
    return a + b;
}

function subtract(a, b) {
    return a - b;
}

module.exports = {
    add,
    subtract
};
Enter fullscreen mode Exit fullscreen mode

Here we are exporting both functions.

Now another file can use them.

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

console.log(math.add(10, 5));
console.log(math.subtract(10, 5));
Enter fullscreen mode Exit fullscreen mode

Output:

15
5
Enter fullscreen mode Exit fullscreen mode

The important flow is:

math.js
   |
   | module.exports
   ↓
Exports functions
   |
   ↓
app.js
   |
   | require()
   ↓
Uses functions
Enter fullscreen mode Exit fullscreen mode

ESM — ECMAScript Modules

ESM stands for ECMAScript Modules.

It is the standardized JavaScript module system and uses:

export
Enter fullscreen mode Exit fullscreen mode

and:

import
Enter fullscreen mode Exit fullscreen mode

Creating an ESM Module

math.js:

export function add(a, b) {
    return a + b;
}

export function subtract(a, b) {
    return a - b;
}
Enter fullscreen mode Exit fullscreen mode

Then:

import { add, subtract } from "./math.js";

console.log(add(10, 5));
console.log(subtract(10, 5));
Enter fullscreen mode Exit fullscreen mode

Output:

15
5
Enter fullscreen mode Exit fullscreen mode

The concept is the same as CommonJS:

One file
   ↓
exports functionality
   ↓
Another file
   ↓
imports functionality
Enter fullscreen mode Exit fullscreen mode

Only the syntax and module system are different.


Named Exports

With ESM, we can export multiple named values.

export function add(a, b) {
    return a + b;
}

export function multiply(a, b) {
    return a * b;
}
Enter fullscreen mode Exit fullscreen mode

Import them using their names:

import { add, multiply } from "./math.js";
Enter fullscreen mode Exit fullscreen mode

The names need to match the exported names unless aliases are used.


Default Export

ESM also supports a default export.

export default function greet(name) {
    return `Hello ${name}`;
}
Enter fullscreen mode Exit fullscreen mode

Then:

import greet from "./greet.js";

console.log(greet("Sai"));
Enter fullscreen mode Exit fullscreen mode

With a default export, the importing code can choose the local name.


CommonJS vs ESM

The main difference can be understood from the syntax.

CommonJS

Export:

module.exports = {
    add
};
Enter fullscreen mode Exit fullscreen mode

Import:

const { add } = require("./math");
Enter fullscreen mode Exit fullscreen mode

ESM

Export:

export function add(a, b) {
    return a + b;
}
Enter fullscreen mode Exit fullscreen mode

Import:

import { add } from "./math.js";
Enter fullscreen mode Exit fullscreen mode

Comparison

Feature CommonJS ESM
Import require() import
Export module.exports export
Standard Node.js module system JavaScript standard
Common usage Traditional Node.js projects Modern JavaScript
Default syntax require("./file") import x from "./file.js"

The important thing is not to memorize the table.

Remember:

CommonJS → require + module.exports

ESM → import + export


Why This Matters in Node.js

Modern Node.js applications commonly use ESM, but CommonJS is still widely used in existing Node.js projects.

For example, a project may have:

const express = require("express");
Enter fullscreen mode Exit fullscreen mode

which indicates CommonJS.

Another project may have:

import express from "express";
Enter fullscreen mode Exit fullscreen mode

which indicates ESM.

Both approaches allow modules to be created and reused.

The important thing when working on a project is to understand which module system the project is configured to use.


Modules and Separation of Responsibility

One of the biggest benefits of modules is separation of responsibility.

Imagine an employee-management application.

Instead of:

server.js
   |
   ├── Database code
   ├── Employee code
   ├── Authentication code
   ├── Validation code
   └── Utility code
Enter fullscreen mode Exit fullscreen mode

we can organize it:

server.js
   |
   ├── database.js
   ├── employee.js
   ├── auth.js
   ├── validation.js
   └── utils.js
Enter fullscreen mode Exit fullscreen mode

Now each module has a clear responsibility.

This idea becomes extremely important when we start learning design patterns.


Connection to Design Patterns

Modules and design patterns solve different problems.

Modules answer:

"How should I organize and share my code?"

Design patterns answer:

"How should I structure my code to solve a recurring design problem?"

For example:

Modules
   ↓
Organize code

Design Patterns
   ↓
Structure solutions

Open/Closed Principle
   ↓
Make code easier to extend
Enter fullscreen mode Exit fullscreen mode

These concepts work together when building larger applications.


A Simple Real-World Example

Imagine an online shopping application.

We might have:

shop/
│
├── user.js
├── product.js
├── cart.js
├── payment.js
└── notification.js
Enter fullscreen mode Exit fullscreen mode

notification.js might export notification functionality.

Later, we might use a Factory Pattern inside that module to create:

Email Notification
SMS Notification
Push Notification
Enter fullscreen mode Exit fullscreen mode

So modules give us the structure, while design patterns help us solve specific design problems inside that structure.


Key Takeaways

From this topic, the most important concepts are:

  1. Modules divide an application into smaller, manageable pieces.
  2. Modules improve organization, reuse, and maintainability.
  3. CommonJS uses require() and module.exports.
  4. ESM uses import and export.
  5. ESM is the standardized JavaScript module system.
  6. Modules help separate responsibilities.
  7. Modules and design patterns are different concepts, but they work together to create maintainable applications.

The most important mental model is:

Large Application
       ↓
   Divide into
     Modules
       ↓
Organize responsibilities
       ↓
Use Design Patterns
       ↓
Solve recurring design problems
       ↓
Maintainable Application
Enter fullscreen mode Exit fullscreen mode

This understanding of modules provides the foundation for the next part of Week-06: design patterns such as Singleton, Factory, and Observer.

Top comments (0)