WEB ATELIER (UDIT) Β· Learning by doing, with theory, practice and shared reflection

JavaScript Modules: From Chaos to Modularity

URL: https://ruvebal.github.io/web-atelier-udit/lessons/en/js-modules/

πŸ“‹ Table of Contents

🎯 Learning Objectives

  • Understand the historical evolution of JavaScript modularization from global scripts to ES6 Modules
  • Master import/export syntax including default and named exports
  • Apply modular patterns to create scalable, maintainable web applications
  • Connect theory with practice through hands-on exercises following Atelier methodology
  • Recognize the principles that guide modern JavaScript architecture

πŸ“œ The Evolution: From Global Chaos to Modular Enlightenment

β€œThe history of programming is the history of managing complexity. Modules are our salvation.” β€” Kyle Simpson, You Don’t Know JS (inspired)

1995-2009: The Era of Global Variables

In the beginning, JavaScript had no module system. Every script shared the same global namespace:

<!-- index.html -->
<script src="utils.js"></script>
<script src="app.js"></script>
<script>
	// Everything lives in global scope
	var message = 'Hello World'; // Pollutes global namespace

	// What if utils.js also declares 'message'? πŸ’₯ Collision!
</script>

The Problems:

  • πŸ”₯ Namespace pollution - Variables collide
  • πŸ”₯ No encapsulation - Everything is public
  • πŸ”₯ Implicit dependencies - Load order matters but isn’t explicit
  • πŸ”₯ No code reusability - Copy-paste was the norm

Pattern 1: IIFE (Immediately Invoked Function Expression)

The community’s first solution was the Module Pattern using IIFEs:

// utils.js - circa 2006
var Utils = (function () {
	// Private variables (hidden in closure)
	var privateSecret = 'This is private';

	// Public API
	return {
		add: function (a, b) {
			return a + b;
		},
		getSecret: function () {
			return privateSecret;
		},
	};
})();

// Usage
Utils.add(2, 3); // βœ… 5
Utils.privateSecret; // ❌ undefined - encapsulated!

What changed:

  • βœ… Encapsulation through closures
  • βœ… Explicit public API
  • βœ… Reduced global pollution
  • ❌ Still manual dependency management
  • ❌ No standardization

β€œThe IIFE pattern was a hack β€”a brilliant oneβ€” but a hack nonetheless. We needed it because the language lacked built-in modularity.” β€” Douglas Crockford, JavaScript: The Good Parts (inspired)

2009: CommonJS - Modules for Server-Side

When Node.js emerged in 2009, it adopted the CommonJS module system:

// math.js (CommonJS)
function add(a, b) {
	return a + b;
}

function multiply(a, b) {
	return a * b;
}

// Export API
module.exports = {
	add: add,
	multiply: multiply,
};

// Or shorthand:
exports.add = add;
exports.multiply = multiply;
// app.js
const math = require('./math.js');

console.log(math.add(2, 3)); // 5
console.log(math.multiply(4, 5)); // 20

Characteristics:

  • βœ… Synchronous loading - Perfect for server (file system)
  • βœ… Explicit dependencies - require() makes it clear
  • βœ… Single export object - module.exports
  • ❌ Not native to browsers – Needs bundlers (like Browserify, Webpack, or Vite)

β€œCommonJS brought order to server-side JavaScript. For the first time, we had a standard way to organize code.” β€” Ryan Dahl, Node.js creator (inspired)

2011: AMD (Asynchronous Module Definition)

For browsers, RequireJS introduced AMD for asynchronous loading:

// math.js (AMD)
define(['dependency1', 'dependency2'], function (dep1, dep2) {
	function add(a, b) {
		return a + b;
	}

	// Return public API
	return {
		add: add,
	};
});
// app.js
require(['math'], function (math) {
	console.log(math.add(2, 3)); // 5
});

Characteristics:

  • βœ… Asynchronous loading - Non-blocking for browsers
  • βœ… Dependency management - Declared upfront
  • ❌ Verbose syntax - Wrapping overhead
  • ❌ Different from CommonJS - Split ecosystem

2015: ES6 Modules - The Native Standard

ECMAScript 2015 (ES6) finally introduced native modules:

// math.js (ES6 Modules)
export function add(a, b) {
	return a + b;
}

export function multiply(a, b) {
	return a * b;
}

// Default export
export default function subtract(a, b) {
	return a - b;
}

In ES6 module import syntax, named exports must be imported using curly braces {}, while the default export is imported without them.

Both can be combined in a single import line as shown below:

// app.js
// - Use { add, multiply } to import the named exports 'add' and 'multiply'.
// - Use 'subtract' (without braces) to import the default export.

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

console.log(add(2, 3)); // 5
console.log(multiply(4, 5)); // 20
console.log(subtract(10, 3)); // 7

Why ES6 Modules Won:

  • βœ… Native browser support - Browser-native support and features like top-level await
  • βœ… Static analysis - Tools can optimize (tree-shaking)
  • βœ… Cleaner syntax - More readable
  • βœ… Async by default - <script type="module">
  • βœ… Strict mode - Safer code automatically

β€œES6 Modules represent the culmination of JavaScript’s journey toward maturity. They are the standard we’ve been waiting for.” β€” Axel Rauschmayer, Exploring ES6 (inspired)

In fact, CommonJS is still very much alive and actively used in the JavaScript ecosystem, particularly within Node.js and the npm package repository.

Timeline Visualization

1995 ━━━━━━━━━━━━━━━━━━━━━━━━ Global Scope Era
     β”‚                          β€’ No modules
     β”‚                          β€’ Global variables everywhere
     β”‚                          β€’ <script> tag soup
     β”‚
2006 ━━━━━━━━━━━━━━━━━━━━━━━━ Module Pattern (IIFE)
     β”‚                          β€’ Closures for encapsulation
     β”‚                          β€’ Still manual dependency management
     β”‚                          β€’ Design pattern, not standard
     β”‚
2009 ━━━━━━━━━━━━━━━━━━━━━━━━ CommonJS (Node.js)
     β”‚                          β€’ require() / module.exports
     β”‚                          β€’ Server-side standard
     β”‚                          β€’ Synchronous loading
     β”‚
2011 ━━━━━━━━━━━━━━━━━━━━━━━━ AMD (RequireJS)
     β”‚                          β€’ define() / require()
     β”‚                          β€’ Asynchronous for browsers
     β”‚                          β€’ Verbose syntax
     β”‚
2015 ━━━━━━━━━━━━━━━━━━━━━━━━ ES6 Modules
     β”‚                          β€’ import / export
     β”‚                          β€’ Native browser support
     β”‚                          β€’ Static analysis
     β”‚                          β€’ THE STANDARD
     β”‚
2020+ ━━━━━━━━━━━━━━━━━━━━━━━ Universal Adoption
                               β€’ All modern browsers support
                               β€’ Node.js supports ESM
                               β€’ Build tools optimize
                               β€’ Future is modular

Theory: Understanding ES6 Module Syntax

Named Exports

Export multiple values with their names:

// components/Button.js
export function PrimaryButton(text) {
	return `<button class="btn-primary">${text}</button>`;
}

export function SecondaryButton(text) {
	return `<button class="btn-secondary">${text}</button>`;
}

export const buttonStyles = {
	primary: 'bg-blue-500',
	secondary: 'bg-gray-500',
};

Import named exports:

// Option 1: Import specific items
import { PrimaryButton, SecondaryButton } from './components/Button.js';

// Option 2: Import all as namespace
import * as Buttons from './components/Button.js';
Buttons.PrimaryButton('Click me');

// Option 3: Rename imports
import { PrimaryButton as PBtn } from './components/Button.js';

Default Exports

Export a single β€œmain” value:

// components/Card.js
export default function Card({ title, content }) {
	return `
    <article class="card">
      <h3>${title}</h3>
      <p>${content}</p>
    </article>
  `;
}

Import default export:

// You choose the name
import Card from './components/Card.js';
import MyCard from './components/Card.js'; // Same thing!

Mixing Default and Named Exports

// utils.js
export default function mainUtility() {
	return 'Main function';
}

export function helperOne() {
	return 'Helper 1';
}

export function helperTwo() {
	return 'Helper 2';
}

Import both:

import mainUtility, { helperOne, helperTwo } from './utils.js';
// Default first, then named in braces

Re-exporting (Barrel Pattern)

Create an index.js to aggregate exports in a single entry point:

// components/index.js
export { PrimaryButton, SecondaryButton } from './Button.js';
export { default as Card } from './Card.js';
export { Section } from './Section.js';

// Now import everything from one place:
// import { PrimaryButton, Card, Section } from './components/index.js';

β€œThe barrel pattern is not just convenienceβ€”it’s architecture. It creates a facade that hides implementation details.” β€” Martin Fowler, Refactoring (inspired)

Dynamic Imports

Load modules conditionally or on-demand:

// Static import (loaded immediately)
import utils from './utils.js';

// Dynamic import (loaded when needed)
async function loadFeature() {
	const module = await import('./heavy-feature.js');
	module.initialize();
}

// Use case: Code splitting (splitting your app's code into smaller chunks that are loaded only when needed, improving performance by reducing initial load time)
button.addEventListener('click', async () => {
	const { animate } = await import('./animations.js');
	animate(element);
});

πŸ—οΈ Atelier Methodology: Hands-On Module Workshop

πŸ› οΈ Setup: Creating a Modular Project

Step 1: Project Structure

project-root/
β”œβ”€β”€ index.html
└── src/
    β”œβ”€β”€ main.js           # Entry point
    β”œβ”€β”€ components/       # Reusable UI components
    β”‚   β”œβ”€β”€ Button.js
    β”‚   β”œβ”€β”€ Card.js
    β”‚   └── index.js      # Barrel file
    β”œβ”€β”€ utils/            # Helper functions
    β”‚   β”œβ”€β”€ math.js
    β”‚   └── dom.js
    └── views/            # Page views
        β”œβ”€β”€ home.js
        └── about.js
        └── index.js      # Barrel file

Step 2: HTML with Module Support

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>JS Modules Workshop</title>
		<style>
			body {
				font-family: system-ui, sans-serif;
				max-width: 800px;
				margin: 2rem auto;
				padding: 0 1rem;
			}
			.btn {
				padding: 0.5rem 1rem;
				margin: 0.5rem;
				border: none;
				border-radius: 0.25rem;
				cursor: pointer;
			}
			.btn-primary {
				background: #3b82f6;
				color: white;
			}
			.btn-secondary {
				background: #6b7280;
				color: white;
			}
			.card {
				border: 1px solid #e5e7eb;
				border-radius: 0.5rem;
				padding: 1.5rem;
				margin: 1rem 0;
			}
		</style>
	</head>
	<body>
		<h1>JavaScript Modules Workshop</h1>
		<div id="app"></div>

		<!-- ⚠️ Important: type="module" enables ES6 modules -->
		<script type="module" src="src/main.js"></script>
	</body>
</html>

πŸ“¦ Exercise 1: Create Utility Modules

src/utils/math.js - Named exports

/**
 * Math utilities
 * Demonstrates multiple named exports
 */

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

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

export function multiply(a, b) {
	return a * b;
}

export function divide(a, b) {
	if (b === 0) throw new Error('Division by zero');
	return a / b;
}

// Export constant
export const PI = 3.14159;
export const E = 2.71828;

// Export object
export const MathConstants = {
	PI,
	E,
	GOLDEN_RATIO: 1.61803,
};

src/utils/dom.js - Default + named exports

/**
 * DOM utilities
 * Demonstrates default and named exports together
 */

// Default export - main utility
export default function createEl(tag, attrs = {}, children = []) {
	const el = document.createElement(tag);

	// Set attributes
	Object.entries(attrs).forEach(([key, value]) => {
		if (key === 'className') {
			el.className = value;
		} else if (key.startsWith('on')) {
			// Event listener
			el.addEventListener(key.substring(2).toLowerCase(), value);
		} else {
			el.setAttribute(key, value);
		}
	});

	// Append children
	children.forEach((child) => {
		if (typeof child === 'string') {
			el.appendChild(document.createTextNode(child));
		} else {
			el.appendChild(child);
		}
	});

	return el;
}

// Named exports - helper functions
export function append(parent, ...children) {
	children.forEach((child) => parent.appendChild(child));
}

export function clear(element) {
	element.innerHTML = '';
}

export function setText(element, text) {
	element.textContent = text;
}

export function addClass(element, ...classes) {
	element.classList.add(...classes);
}

export function removeClass(element, ...classes) {
	element.classList.remove(...classes);
}

🎨 Exercise 2: Create Component Modules

src/components/Button.js

/**
 * Button Component
 * Demonstrates component patterns with modules
 */

// Named exports for button variants
export function PrimaryButton(text, onClick) {
	const button = document.createElement('button');
	button.className = 'btn btn-primary';
	button.textContent = text;
	if (onClick) button.addEventListener('click', onClick);
	return button;
}

export function SecondaryButton(text, onClick) {
	const button = document.createElement('button');
	button.className = 'btn btn-secondary';
	button.textContent = text;
	if (onClick) button.addEventListener('click', onClick);
	return button;
}

// Configuration object export
export const ButtonConfig = {
	defaultClass: 'btn',
	variants: ['primary', 'secondary'],
	sizes: ['sm', 'md', 'lg'],
};

// Generic button factory (default export)
export default function Button({ text = 'Button', variant = 'primary', onClick = null } = {}) {
	const button = document.createElement('button');
	button.className = `btn btn-${variant}`;
	button.textContent = text;
	if (onClick) button.addEventListener('click', onClick);
	return button;
}

src/components/Card.js

/**
 * Card Component
 * Demonstrates default export pattern
 */

export default function Card({ title, content, footer = null } = {}) {
	const card = document.createElement('article');
	card.className = 'card';

	if (title) {
		const h3 = document.createElement('h3');
		h3.textContent = title;
		card.appendChild(h3);
	}

	if (content) {
		const p = document.createElement('p');
		p.textContent = content;
		card.appendChild(p);
	}

	if (footer) {
		const div = document.createElement('div');
		div.className = 'card-footer';
		div.appendChild(footer);
		card.appendChild(div);
	}

	return card;
}

// Named export for card utilities
export function CardGrid(cards = []) {
	const grid = document.createElement('div');
	grid.className = 'card-grid';
	cards.forEach((card) => grid.appendChild(card));
	return grid;
}

src/components/index.js - Barrel file

/**
 * Components barrel file
 * Aggregates all component exports
 *
 * This is the "facade pattern" - provides a clean public API
 * while hiding internal implementation details
 */

// Re-export everything from Button
export { PrimaryButton, SecondaryButton, ButtonConfig, default as Button } from './Button.js';

// Re-export from Card
export { default as Card, CardGrid } from './Card.js';

// Now consumers can do:
// import { Button, Card, PrimaryButton } from './components/index.js';

🎬 Exercise 3: Create View Modules

src/views/home.js

/**
 * Home view
 * Demonstrates importing from multiple modules
 */

import { PrimaryButton, SecondaryButton, Card } from '../components/index.js';
import { add, multiply, MathConstants } from '../utils/math.js';
import createEl, { append, clear } from '../utils/dom.js';

export default function HomeView(container) {
	clear(container);

	// Create header
	const header = createEl('header', {}, [createEl('h2', {}, ['Welcome to Modular JavaScript'])]);

	// Create card with math demo
	const mathCard = Card({
		title: 'Math Utilities Demo',
		content: `
      2 + 3 = ${add(2, 3)}
      4 Γ— 5 = ${multiply(4, 5)}
      Ο€ β‰ˆ ${MathConstants.PI}
    `,
		footer: SecondaryButton('Calculate More', () => {
			alert(`Golden Ratio: ${MathConstants.GOLDEN_RATIO}`);
		}),
	});

	// Create buttons section
	const buttonsSection = createEl('section', {}, [
		createEl('h3', {}, ['Component Library']),
		PrimaryButton('Primary Action', () => alert('Primary clicked!')),
		SecondaryButton('Secondary Action', () => alert('Secondary clicked!')),
	]);

	// Append all to container
	append(container, header, mathCard, buttonsSection);
}

src/views/about.js

/**
 * About view
 * Demonstrates dynamic imports
 */

import { Card, CardGrid } from '../components/index.js';
import createEl, { append, clear } from '../utils/dom.js';

export default async function AboutView(container) {
	clear(container);

	const header = createEl('h2', {}, ['About Modular Architecture']);

	const cards = [
		Card({
			title: 'Encapsulation',
			content: 'Each module has its own scope, preventing global pollution.',
		}),
		Card({
			title: 'Reusability',
			content: 'Write once, import anywhere. DRY principle in action.',
		}),
		Card({
			title: 'Maintainability',
			content: 'Changes are localized. Fix once, benefit everywhere.',
		}),
	];

	const grid = CardGrid(cards);

	// Dynamic import demo
	const dynamicButton = createEl(
		'button',
		{
			className: 'btn btn-primary',
			onClick: async () => {
				// Lazy load math module only when needed
				const math = await import('../utils/math.js');
				alert(`Dynamically loaded PI: ${math.PI}`);
			},
		},
		['Load Math Dynamically']
	);

	append(container, header, grid, dynamicButton);
}

πŸš€ Exercise 4: Main Entry Point

src/main.js - Application entry

/**
 * Main entry point
 * Demonstrates module orchestration
 */

import HomeView from './views/home.js';
import AboutView from './views/about.js';
import { PrimaryButton, SecondaryButton } from './components/index.js';

// Get app container
const app = document.getElementById('app');

// Simple router
const routes = {
	home: HomeView,
	about: AboutView,
};

let currentRoute = 'home';

// Navigation
function navigate(route) {
	if (routes[route]) {
		currentRoute = route;
		routes[route](app);
	}
}

// Create navigation
function createNav() {
	const nav = document.createElement('nav');
	nav.style.marginBottom = '2rem';

	nav.appendChild(PrimaryButton('Home', () => navigate('home')));
	nav.appendChild(SecondaryButton('About', () => navigate('about')));

	document.body.insertBefore(nav, app);
}

// Initialize app
function init() {
	console.log('πŸš€ Modular JavaScript App Initialized');
	console.log('πŸ“¦ All modules loaded via ES6 imports');

	createNav();
	navigate('home');
}

// Start
init();

// Make navigate available globally for debugging
window.navigate = navigate;

πŸ§ͺ Testing Your Module System

Method 1: Local Development Server

# Option A: Python
python3 -m http.server 8000

# Option B: Node.js (if you have http-server)
npx http-server -p 8000

# Option C: PHP
php -S localhost:8000

Then open: http://localhost:8000

⚠️ Important: ES6 modules will not work by just double-clicking your index.html in your file explorer.
This is because browsers enforce security rules (CORS) that block module loading when using the file:// protocol.

You must run a local development server (like the options above) and access your site using http://localhost:8000.
Modules will fail to load if you only open files directly. Always use a server during development!

npm create vite@latest js-modules-workshop -- --template vanilla
cd js-modules-workshop
npm install
npm run dev

🎯 Practice Exercises: Build Your Modular Skills

Exercise 5.1: Create a Notification Component

Requirements:

  1. Create src/components/Notification.js
  2. Default export: Notification({ message, type })
  3. Named exports: SuccessNotification(), ErrorNotification(), WarningNotification()
  4. Use the component in HomeView

Starter code:

// src/components/Notification.js
export default function Notification({ message, type = 'info' } = {}) {
	// TODO: Create notification element
	// Types: 'success', 'error', 'warning', 'info'
}

export function SuccessNotification(message) {
	return Notification({ message, type: 'success' });
}

// TODO: Add ErrorNotification and WarningNotification

Exercise 5.2: Create a Form Module

Requirements:

  1. Create src/components/Form.js
  2. Export FormField(), FormButton(), Form()
  3. Create src/views/contact.js that uses these components
  4. Add route to main.js

Exercise 5.3: Build a State Management Module

Requirements:

  1. Create src/utils/store.js
  2. Implement simple state management:
// src/utils/store.js
const state = {};
const listeners = [];

export function setState(key, value) {
	state[key] = value;
	listeners.forEach((fn) => fn(state));
}

export function getState(key) {
	return state[key];
}

export function subscribe(callback) {
	listeners.push(callback);
	// Return unsubscribe function
	return () => {
		const index = listeners.indexOf(callback);
		if (index > -1) listeners.splice(index, 1);
	};
}

Use it in your app:

// src/main.js
import { setState, getState, subscribe } from './utils/store.js';

subscribe((state) => {
	console.log('State changed:', state);
});

setState('user', { name: 'Alice', role: 'developer' });
console.log(getState('user'));

πŸ† Best Practices: The Module Manifesto

1. One Concern Per Module

β€œA module should do one thing and do it well.” β€” Unix Philosophy applied to JavaScript

// βœ… Good: Focused responsibility
// src/utils/validation.js
export function isEmail(str) {
	/* ... */
}
export function isURL(str) {
	/* ... */
}
export function isPhone(str) {
	/* ... */
}

// ❌ Bad: Mixed responsibilities
// src/utils/everything.js
export function isEmail(str) {
	/* ... */
}
export function fetchAPI(url) {
	/* ... */
} // Different concern!
export function formatCurrency(num) {
	/* ... */
} // Different concern!

2. Explicit Dependencies

// βœ… Good: All dependencies visible at top
import { Button } from './components/Button.js';
import { Card } from './components/Card.js';
import { add, multiply } from './utils/math.js';

function myFeature() {
  // Use imports
}

// ❌ Bad: Hidden dependencies (dynamic imports everywhere)
function myFeature() {
  const math = await import('./utils/math.js'); // Hard to track
}

3. Prefer Named Exports for Utilities

// βœ… Good: Named exports for multiple utilities
// utils/array.js
export function unique(arr) {
	/* ... */
}
export function flatten(arr) {
	/* ... */
}
export function chunk(arr, size) {
	/* ... */
}

// Import what you need
import { unique, flatten } from './utils/array.js';

4. Prefer Default Exports for Single-Purpose Modules

// βœ… Good: Default export for single component
// components/Hero.js
export default function Hero({ title, subtitle }) {
	// Single main component
}

// Import with semantic name
import Hero from './components/Hero.js';

5. Use Barrel Files for Public APIs

// βœ… Good: Clean public API
// components/index.js
export { Button, PrimaryButton } from './Button.js';
export { Card } from './Card.js';
export { Hero } from './Hero.js';

// Consumers see clean API
import { Button, Card, Hero } from './components/index.js';

// ❌ Bad: Expose internal structure
import Button from './components/internal/ui/Button.js';

6. Avoid Circular Dependencies

// ❌ Bad: Circular dependency
// A.js
import { functionB } from './B.js';
export function functionA() {
	functionB();
}

// B.js
import { functionA } from './A.js'; // ⚠️ Circular!
export function functionB() {
	functionA();
}

// βœ… Good: Extract shared logic
// shared.js
export function sharedLogic() {
	/* ... */
}

// A.js
import { sharedLogic } from './shared.js';

// B.js
import { sharedLogic } from './shared.js';

7. Document Your Modules with JSDoc

/**
 * Card Component
 *
 * Creates a styled card element for displaying content
 *
 * @module components/Card
 * @param {Object} options - Card configuration
 * @param {string} options.title - Card title
 * @param {string} options.content - Card content
 * @param {HTMLElement} [options.footer] - Optional footer element
 * @returns {HTMLElement} The card element
 *
 * @example
 * const card = Card({
 *   title: 'Hello',
 *   content: 'World'
 * });
 */
export default function Card({ title, content, footer = null } = {}) {
	// Implementation
}

🌍 Real-World Impact: Why This Matters

β€œThe difference between a beginner and a professional is not what they know, but how they organize what they know.” β€” Anonymous

Before Modules: The Maintenance Nightmare

// app.js - 5000 lines of unmaintainable code
var app = {
	// 100+ functions all in one file
	// Global variables everywhere
	// Dependencies unclear
	// Testing? Good luck.
};

After Modules: Scalable Architecture

src/
β”œβ”€β”€ components/        # 50+ reusable components
β”œβ”€β”€ utils/             # Tested, documented utilities
β”œβ”€β”€ views/             # Clear page structure
β”œβ”€β”€ services/          # API interactions
└── main.js            # Clean orchestration

Benefits Realized:

  • βœ… Team Collaboration: Different devs work on different modules
  • βœ… Code Reusability: Button component used in 20 places, maintained in 1
  • βœ… Testing: Unit test each module independently
  • βœ… Performance: Tree-shaking removes unused code
  • βœ… Onboarding: New developers understand structure quickly

πŸ“š Further Reading & References

Essential Articles

Books (Inspired Quotes)

  • Kyle Simpson, You Don’t Know JS: ES6 & Beyond
  • Axel Rauschmayer, Exploring ES6
  • Douglas Crockford, JavaScript: The Good Parts
  • Martin Fowler, Refactoring: Improving the Design of Existing Code

Historical Context

  • Node.js (2009): Brought CommonJS to prominence
  • RequireJS (2010): AMD for browsers
  • Browserify (2011): CommonJS in browsers
  • Webpack (2012): Universal module bundler
  • ES6 (2015): Native module standard
  • Vite (2020): Lightning-fast ESM-first build tool

πŸŽ“ Atelier Reflection: What You’ve Learned

By completing this lesson, you’ve mastered:

  1. βœ… Historical Evolution - From global chaos to ES6 modules
  2. βœ… Module Syntax - import, export, default, named, dynamic
  3. βœ… Architectural Patterns - IIFE, CommonJS, AMD, ES6
  4. βœ… Best Practices - SRP, explicit dependencies, barrel files
  5. βœ… Hands-On Skills - Built a complete modular application
  6. βœ… Professional Mindset - Understanding trade-offs and evolution

β€œYou are now equipped to build scalable, maintainable web applications. Use this power wisely β€”organize your code as if the next person to maintain it is a homicidal maniac who knows where you live.” β€” Anonymous (but wise) Developer


πŸš€ Next Steps

  1. Refactor an existing project to use ES6 modules
  2. Build a component library with proper module structure
  3. Study open-source projects to see modules in action (React, Vue, etc.)
  4. Explore build tools (Vite, Webpack, Rollup) for module optimization
  5. Apply to Tailwind lessons - Review S2-S5 with new understanding

Remember: Modular thinking is professional thinking. Every file you create is a commitment to future maintainability.


πŸ“ Commit Your Learning

git add .
git commit -m "feat: Complete JS Modules workshop

- Understand historical evolution from IIFE to ES6
- Master import/export syntax (default, named, dynamic)
- Build modular application with components/utils/views
- Apply best practices: SRP, barrel files, JSDoc
- Ready to build scalable web applications

Learning progress: Beginner β†’ Professional architecture mindset"

🎨 Atelier Philosophy: β€œWe don’t teach codeβ€” we teach thinking. Modules are not syntax, they are architecture. Architecture is not structure, it is culture. Culture is how we work together across time and space. Welcome to the community of vernacular web masters.”

β€” Prof. RubΓ©n Vega BalbΓ‘s, PhD