What you'll learn
ES modules turn files into explicit dependency boundaries. Good module design exposes a small stable API and keeps implementation details local.
By the end of this lesson, you'll be able to:
- Use named, default, and dynamic imports
- Design a focused module API
- Recognize circular dependency risks
Core mental model
Use this decision table as a compact reference. Focus on what each tool means and when it earns its place in production code.
| Concept | What it means | Decision rule |
|---|---|---|
| Named export | A module exposes a specific live binding | Prefer for discoverable multi-value APIs |
| Default export | One primary value is exported | Use sparingly when the module clearly has one main product |
| Dynamic import | A module loads on demand and returns a promise | Use for optional or route-level code splitting |
Professional workflow
Build the behavior in small, observable steps. Each step should leave something you can inspect or test.
- Describe the module boundary boundary: inputs, outputs, state, timing, and expected failures.
- Implement the smallest correct path with names that expose intent.
- Add edge cases and failure handling before introducing abstractions.
- Verify behavior with realistic data and one deliberately adversarial example.
- Refactor only after the observable behavior is protected.
Make behavior observable
Guided code lab
Expose a narrow domain API
Only supported operations are exported; tax configuration remains private.
const taxRate = 0.11;
export function subtotal(lines) {
return lines.reduce((sum, line) => sum + line.price * line.quantity, 0);
}
export function total(lines) {
const value = subtotal(lines);
return value + value * taxRate;
}Consume named exports
Static imports make dependencies visible before execution.
import { subtotal, total } from "./pricing.js";
const lines = [{ price: 10, quantity: 2 }];
console.log(subtotal(lines));
console.log(total(lines));Production practice
Contract
Exports are the supported public API; unexported names may change freely.
Verification
Import the module from a consumer test and verify both behavior and failure cases.
Operations
Use dynamic imports at measured boundaries and monitor chunk-loading failures.
Common failure mode
Independent workshop
Split the Phase 1 grade tracker into data, calculation, formatting, and application modules.
Your finished workshop must include:
- Named exports with no global variables
- One dynamic import for an optional report
- A dependency diagram with no cycle
Definition of done
Recap & quick check
Key takeaways
- Modules create scope and explicit dependencies
- Named exports improve discoverability
- Imports are live bindings
- Dynamic import enables on-demand loading
Quick check
1. What does dynamic import return?
2. Are imported bindings copies?
3. What is private to an ES module by default?
Keep the workshop. Later modules deliberately build on these decisions, so each exercise can become part of your final portfolio architecture.