Not Everything Should Be Seen: Rethinking the Module Pattern in JavaScript
The module pattern in JavaScript uses closures to create private state and expose a controlled public interface, helping structure code by enforcing boundaries between internal implementation and external access.
Thereās a moment in every growing codebase when things start to feel⦠exposed.
Variables live everywhere.
Functions reach into places they shouldnāt.
Names begin to collide.
At first, it works.
But slowly, the system loses its shape.
Because everything is visibleā
and nothing is protected.
The Problem of Too Much Access
Early JavaScript didnāt have a strong sense of boundaries.
You declare something, and it becomes accessible.
var count = 0
function increment() {
count++
}Simple.
Direct.
But also fragile.
Anyone can change count.
Any part of the code can depend on it.
And once that happens, control disappears.
The Desire for Privacy
At some point, the need becomes clear:
- some data should be hidden
- some logic should be internal
- only a small surface should be exposed
Not everything needs to be public.
In fact, most things shouldnāt be.
A Pattern Emerges
The module pattern is not a feature.
Itās a response.
A way to introduce boundaries where none existed.
It creates a private spaceā
and then carefully decides what to reveal.
A Small Shift in Structure
Instead of writing everything in the open, you wrap it:
const counter = (function () {
let count = 0
function increment() {
count++
}
function getCount() {
return count
}
return {
increment,
getCount
}
})()At a glance, it looks like a function.
But something deeper is happening.
A Private World
Inside this function, count exists.
It can change.
It can be used.
But it cannot be accessed directly from the outside.
The outside world only sees what is returned.
And that changes everything.
Not Hiding DataāBut Controlling Access
The goal is not secrecy.
Itās control.
Instead of asking:
āWhat can access this variable?ā
You begin to ask:
āWhat should be allowed to access this variable?ā
That shift turns structure into design.
The Role of Closures
What makes this possible is closure.
Even after the function finishes executing,
its internal variables remain accessible to the returned functions.
They carry the private state with them.
But only through controlled entry points.
A Clear Boundary
The module pattern draws a line:
- inside ā private implementation
- outside ā public interface
And that boundary is intentional.
It reduces coupling.
It prevents accidental misuse.
It gives your code a defined shape.
Not Just a PatternāA Way of Thinking
Once you start using modules, your mindset changes.
You stop exposing everything by default.
You start designing interfaces.
You begin to think in terms of:
- what is internal
- what is external
- what should remain hidden
From Pattern to Language Feature
Modern JavaScript gives us ES Modules:
let count = 0
export function increment() {
count++
}The idea is the same.
count is still private to the module.
Only what you export becomes visible.
The pattern didnāt disappear.
It became native.
A Subtle but Powerful Shift
Without modules, code is a collection of statements.
With modules, code becomes a system of boundaries.
And those boundaries are what make large applications manageable.
A Final Thought
The module pattern is not about hiding things.
Itās about deciding what deserves to be seen.
Because once everything is exposed,
nothing is truly controlled.
And once you introduce boundaries,
your code stops being a loose collection of logicā
and starts becoming something intentional.