JavaScript Modules: From Chaos to Modularity
URL: https://ruvebal.github.io/web-atelier-udit/lessons/en/js-modules/
π Table of Contents
- π― Learning Objectives
- π The Evolution: From Global Chaos to Modular Enlightenment
- Theory: Understanding ES6 Module Syntax
- ποΈ Atelier Methodology: Hands-On Module Workshop
- π§ͺ Testing Your Module System
- π― Practice Exercises: Build Your Modular Skills
- π Best Practices: The Module Manifesto
- π Real-World Impact: Why This Matters
- π Further Reading & References
- π Atelier Reflection: What Youβve Learned
- π Next Steps
- π Commit Your Learning
π― 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!
Method 2: Use Vite (Recommended)
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:
- Create
src/components/Notification.js - Default export:
Notification({ message, type }) - Named exports:
SuccessNotification(),ErrorNotification(),WarningNotification() - 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:
- Create
src/components/Form.js - Export
FormField(),FormButton(),Form() - Create
src/views/contact.jsthat uses these components - Add route to main.js
Exercise 5.3: Build a State Management Module
Requirements:
- Create
src/utils/store.js - 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
- MDN Web Docs: JavaScript Modules
- ES6 Spec: ECMAScript Modules Specification
- V8 Blog: JavaScript modules
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:
- β Historical Evolution - From global chaos to ES6 modules
- β Module Syntax - import, export, default, named, dynamic
- β Architectural Patterns - IIFE, CommonJS, AMD, ES6
- β Best Practices - SRP, explicit dependencies, barrel files
- β Hands-On Skills - Built a complete modular application
- β 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
- Refactor an existing project to use ES6 modules
- Build a component library with proper module structure
- Study open-source projects to see modules in action (React, Vue, etc.)
- Explore build tools (Vite, Webpack, Rollup) for module optimization
- 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