Tree Shaking (Dead-Code Elimination)
Tree shaking removes code that a bundler can prove is unused from a production bundle. It works especially well with statically analyzable ES modules and can reduce JavaScript bundle size.
Tree shaking is a build-time optimization used by JavaScript bundlers such as Rollup, webpack, and others. The bundler analyzes the module dependency graph and determines which exports and code paths are actually needed by the application.
ES modules are particularly suitable because `import` and `export` relationships are statically visible. If a module exports several functions but the application imports only one, a bundler may omit the unused exports from the production output.
Tree shaking is not the same as simply deleting every function that appears unused in source text. The bundler must consider module usage, side effects, dynamic behavior, and configuration. Code that might produce observable effects when evaluated can need to remain even when its exported value is not consumed.
Production optimization normally combines tree shaking with other transformations such as minification and dead-code elimination. The exact result depends on the bundler, configuration, module format, and whether dependencies correctly describe their side effects.
Key points
- Primarily relies on statically analyzable module relationships such as ES `import` and `export`.
- Unused exports can be removed from the production bundle.
- Side effects can prevent apparently unused code from being safely removed.
- Tree shaking happens during the build; it is not a JavaScript runtime feature.
- Minification and tree shaking are related but perform different optimizations.
Interview questions
What is tree shaking?
Tree shaking is a build-time optimization that removes code a bundler can determine is not needed by the application. It commonly works with ES modules because their imports and exports are statically visible. The goal is to reduce the amount of JavaScript shipped to users. It is especially useful when a library exposes many exports but an application uses only a subset.
Why do ES modules support tree shaking well?
ES modules have statically structured `import` and `export` declarations. A bundler can inspect those relationships without executing arbitrary application code to discover dependencies. This lets it build a module graph and determine which exports are needed. Dynamic behavior can make this analysis less certain.
Why might apparently unused code remain in a production bundle?
The code may have side effects that are observable even when its exported value is unused. Module initialization, registrations, logging, polyfills, or other effects can make removing it unsafe. Bundlers therefore use side-effect information and conservative analysis. Package metadata and bundler configuration can also influence what is considered removable.
How would you investigate whether tree shaking is working?
I would build the application in production mode and inspect the generated bundle or bundle-analysis report. I would verify that an intentionally unused export from a tree-shakeable module does not contribute meaningful code to the output. If it remains, I would inspect module format, side effects, bundler configuration, and whether the dependency is being transformed in a tree-shakeable form. I would also distinguish tree-shaking issues from code-splitting or minification issues.