Production Optimization

Course 1 · Ch 8
Production Optimization — Tree Shaking, Code Splitting & Minification
Three genuinely different techniques, answering three genuinely different questions

build-tooling1-1 previewed minification as one of three separate jobs. This chapter delivers the full production-optimization picture — and closes the loop build-tooling1-2 opened when it explained exactly why ESM, not CommonJS, is what makes one of these techniques possible at all.

Tree Shaking — Removing Code That's Never Used

build-tooling1-2's own static-vs-dynamic distinction is the entire mechanism here. Because a bundler can determine with certainty which exports from an ESM module are actually imported anywhere in the reachable dependency graph, it can safely remove any exported code nothing ever actually imports — genuine dead-code elimination at the module level.

// utils.js exports 5 functions export function formatDate() { /* ... */ } export function formatPrice() { /* ... */ } export function slugify() { /* ... */ } export function debounce() { /* ... */ } export function throttle() { /* ... */ } // app.js only imports 2 import { formatPrice, slugify } from './utils.js'; // tree shaking: only formatPrice, slugify (and THEIR own dependencies) ship

This is exactly why build-tooling1-2 singled out CommonJS as unreliable for this purpose — a bundler can't be certain whether a possibly-conditional, possibly-computed require() call will actually execute, so removing seemingly-unused CommonJS code risks breaking real functionality a hidden runtime path genuinely needed.

Code Splitting — Not Loading Everything Upfront

A different, complementary optimization: rather than removing unused code, code splitting deliberately breaks the output into multiple separate chunks, loaded on demand instead of all at once. A rarely-visited settings page doesn't need to be downloaded by every user on their very first load — it can be its own chunk, fetched only when that route is actually reached.

// a dynamic import — the real signal telling a bundler "this is a valid split point" button.addEventListener('click', async () => { const { openSettingsPanel } = await import('./settings-panel.js'); openSettingsPanel(); });

This is the bundle-level version of exactly the principle web-vitals1-6 already covered for images and below-the-fold content: don't make a user download something they don't need yet.

Minification — Shrinking What Remains

build-tooling1-1 previewed this briefly; here's the full picture: renaming variables to shorter names, stripping whitespace and comments, and more aggressive transforms like eliminating genuinely unreachable branches (if (false) { ... }) or inlining small functions. Real tools: Terser — the long-standing standard JS minifier, itself written in JavaScript, a genuine case where a JS-native tool remains the established choice for a specific job rather than everything having moved to a compiled language — and esbuild's own built-in minifier, applying build-tooling1-6's own speed advantage directly to this step too.

Worth being explicit: minification is genuinely separate from tree shaking and code splitting, not just another word for "making the bundle smaller." It shrinks whatever code survives those two earlier decisions, regardless of when that code loads.

How These Three Fit Together

TechniqueQuestion it answersReal mechanism
Tree shakingIs this code used anywhere at all?Static ESM analysis (build-tooling1-2) removes genuinely unreachable exports
Code splittingIs this code needed right now?Dynamic import() creates separate, on-demand-loaded chunks
MinificationCan this remaining code be made smaller?Variable renaming, whitespace removal, dead-branch elimination
Tree shaking has real limits, even with ESM
A module can have side effects — code that runs just from being imported, unrelated to any specific named export (registering a global, patching a prototype). A bundler can't always safely tell whether removing an "unused" export would also silently remove a needed side effect, so it has to be conservative by default. Packages often explicitly mark "sideEffects": false in package.json specifically to tell the bundler it's safe to tree-shake aggressively — without that marker, even genuinely static ESM code may not get the full tree-shaking benefit this chapter otherwise promises.
One chapter left
build-tooling1-9's own capstone configures a real project's build pipeline end to end, combining every technique from this entire course.

Coding Challenges

Challenge 1

Given this chapter's own five-function utils.js example, where app.js only imports formatPrice and slugify, explain what tree shaking does here, and explain why this is only reliably possible because the import is ESM rather than CommonJS.

📄 View solution
Challenge 2

Tree shaking and code splitting are often both loosely described as "reducing bundle size," but this chapter frames them as answering genuinely different questions. Using this chapter's own three-technique table, explain the real difference between the two.

📄 View solution
Challenge 3

Explain the "sideEffects: false" gotcha this chapter names: why can't a bundler always safely tree-shake even statically-analyzable ESM code, and what does this package.json field do to help?

📄 View solution

Chapter 8 Quick Reference

  • Tree shaking — removes exports nothing ever imports, relying entirely on ESM's static analyzability (build-tooling1-2); unreliable with CommonJS
  • Code splitting — defers loading code until it's actually needed, via dynamic import(); the bundle-level version of web-vitals1-6's own lazy-loading principle
  • Minification — shrinks whatever code remains (Terser, or esbuild's own faster built-in minifier), independent of when that code loads
  • Three genuinely different questions: used at all? / needed right now? / can it be smaller? — not three names for the same optimization
  • Module side effects can block full tree-shaking even with ESM — "sideEffects": false in package.json tells a bundler it's safe to be aggressive
  • Next chapter: Capstone — Configuring a Real Project's Build Pipeline