Organizing Code with Modules in Modern JavaScript Designing a clean, maintainable module hierarchy is what separates junior scripts from scalable, enterprise-grade applications. This tutorial explores best practices for folder structures, separation of concerns, barrel files, and circular dependency avoidance.
A production frontend or Node.js codebase organizes code by responsibility and domain:
1 src/
2 ├── api/ # Network requests, endpoints, Axios/Fetch clients
3 │ ├── apiClient.js
4 │ └── usersApi.js
5 ├── components/ # UI Components (DOM manipulation, widgets)
6 │ ├── Modal.js
7 │ └── Toast.js
8 ├── services/ # Business logic & domain operations
9 │ ├── authService.js
10 │ └── paymentService.js
11 ├── utils/ # Pure helper functions
12 │ ├── dateUtils.js
13 │ └── stringFormatters.js
14 ├── state/ # State management and stores
15 │ └── userStore.js
16 └── index.js # Application entry point
A barrel is an index.js file that aggregates and re-exports exports from multiple sub-modules. This allows consumers to import from a single clean directory path:
1 // src/utils/dateUtils.js
2 export function formatDate ( d ) { ... }
3
4 // src/utils/mathUtils.js
5 export function clamp ( val , min , max ) { ... }
6
7 // src/utils/index.js (The Barrel File!)
8 export * from './dateUtils.js' ;
9 export * from './mathUtils.js' ;
Clean Consumer Import: 1 // Clean single import from barrel:
2 import { formatDate , clamp } from './utils/index.js' ;
3
4 // Instead of messy individual imports:
5 // import { formatDate } from './utils/dateUtils.js';
6 // import { clamp } from './utils/mathUtils.js';
A healthy module architecture enforces a unidirectional dependency flow:
1 [ UI Components ] (Components/Views)
2 │
3 ▼
4 [ Domain Services ] (Business logic, state changes)
5 │
6 ▼
7 [ API Client / Utils ] (Network requests, pure helpers)
Pure Utilities (utils/) : Have no dependencies on UI or domain state. Highly testable.API Clients (api/) : Focus strictly on HTTP transport, headers, serialization.Services (services/) : Orchestrate business rules and caching.4. Circular Dependencies and How to Prevent Them A circular dependency occurs when Module A imports Module B, and Module B imports Module A:
1 Module A ────── imports ──────► Module B
2 ▲ │
3 └────────── imports ────────────┘
When circular dependencies occur, one of the modules may receive undefined for the other's exports because the file has not finished evaluating!
Extract the shared logic into a third module (Module C) that both A and B can import:
1 Module A ───► Module C ◄─── Module B
Interactive Quiz • Knowledge Check
Practice Quiz What is a "barrel file" in JavaScript module architecture?
A A binary file containing compiled WebAssembly
B An index.js file that rolls up and re-exports exports from multiple modules in a directory
C A compressed zip archive of dependencies
D A file used to store encrypted passwords
What issue can arise when Module A imports Module B while Module B simultaneously imports Module A?
A An infinite compiler crash
B Circular dependency issues where one module receives undefined exports
C The browser reboots
D All exports are converted to strings
How should a circular dependency between two modules best be resolved?
A Place all code into a single 10,000-line file
B Extract the shared logic or types into a third common module that both can import
C Convert both modules to CommonJS
D Use setInterval inside the imports
Why are utility functions in utils/ ideally written as pure functions without external state?
A They run on a separate CPU thread
B They have no side effects, making them predictable, reusable, and easy to unit test
C Modern browsers only allow pure functions
D Pure functions consume zero memory
What syntax is used in a barrel file to re-export everything from another module?
A export * from './dateUtils.js';
B reexport './dateUtils.js';
C import './dateUtils.js' as export;
D module.share('./dateUtils.js');