A practical digest of “Learning Patterns” by Lydia Hallie & Addy Osmani
“A module keeps related code together and unrelated code out.” — Learning Patterns
Modern JavaScript gives us many ways to group behaviour, but the Module Pattern remains timeless: bundle variables and functions behind a clear public API while hiding implementation details.
1 Why Modules?
Hallie & Osmani frame modules as the answer to two perennial problems:
- Global‑scope pollution – scripts that stomp on each other’s variables.
- Mental overload – codebases with no obvious boundary lines.
Encapsulation tackles both: each module owns its name‑space and reveals only what consumers need.
2 Two Faces of the Module Pattern
2.1 Classic IIFE / Revealing Module
const Counter = (function () {
// Private state
let count = 0;
// Private helper
function log() {
console.log(`Current count 👉 ${count}`);
}
// Public API
return {
increment() {
count++;
log();
},
reset() {
count = 0;
log();
},
value() {
return count;
},
};
})();
Counter.increment(); // 1
Counter.value(); // 1
Counter.count; // undefined (private!)
How it works: an immediately‑invoked function expression creates a new scope; the returned object reveals only selected members.
2.2 ES Modules (ESM)
Since ES2015, the language itself provides first‑class modules:
//📄 math.js
let _cache = {};
export function square(n) {
if (_cache[n]) return _cache[n];
return (_cache[n] = n * n);
}
export const PI = 3.14159;
//📄 usage.js
import { square, PI } from "./math.js";
export
and import
give the same encapsulation benefits with better tooling (tree‑shaking, static analysis).
Hallie & Osmani treat ESM as the modern evolution of the classic pattern—same goals, nicer syntax.
3 Real‑World Use Cases (5 examples)
# | Scenario | Why Modules Shine |
---|---|---|
1 | Analytics SDK wrappers (e.g., Google Analytics, Mixpanel) | Expose trackEvent() while hiding vendor‑specific setup & keys. |
2 | Feature‑flag / Config managers | Keep environment secrets private; export a clean getFlag() API. |
3 | UI component libraries (Modal, Toast, Tooltip) | Bundle markup, styling helpers, and state logic into one importable unit. |
4 | Service layer in Node micro‑services | Each service (userService , emailService ) exports operations; internal DB queries stay private. |
5 | Redux/Vuex slices & Zustand stores | Encapsulate state + actions; consumers import hooks/selectors only. |
These examples all echo the book’s advice: “When behaviour and data belong together, package them as a module.”
4 Pros & Cons
👍 Pros
- Prevents accidental name collisions.
- Clarifies boundaries for easier testing and refactoring.
- Enables lazy‑loading and code‑splitting (ESM + dynamic
import()
).
⚠️ Cons
- Over‑fragmentation can lead to “too many files” syndrome.
- Misusing singletons (stateful modules) may complicate server‑side rendering.
Hallie & Osmani recommend treating modules as “cohesive homes for related code, not catch‑alls.”
5 Best Practices Checklist
- Single Responsibility: one idea per module.
- Explicit Exports: export only what callers need; keep the rest private.
- Immutable Interfaces: changing a module’s API should be deliberate & versioned.
- Documentation at Source: README or JSDoc next to the code.
6 Wrap‑up
The Module Pattern is older than modern JavaScript—but still essential. By packaging logic behind clear interfaces, you shrink mental overhead and grow reusability. Next time you catch your code leaking globals or mixing concerns, reach for this pattern.
Coming soon in #LearningPatterns24: the Observer Pattern!
This summary is based on concepts outlined in *“Learning Patterns” (Patterns.dev)** by Lydia Hallie & Addy Osmani. The original content is licensed CC BY‑NC 4.0.*
Top comments (0)