THN Interview Prep

Bundling & code-splitting economics

Bundling work answers one question: what code must be downloaded, parsed, compiled, and executed before the user can complete the task? A senior answer is not “split everything.” It is a measured plan that reduces critical-path JavaScript without creating slow follow-up navigations.

Core details

ConcernWhat it meansFailure mode
Initial bundleCode required for first render and first interactionGreat Lighthouse screenshot, bad real-device time-to-interactive
Route splitLoad route-specific code on navigationExtra round trip on a route users open immediately
Component splitLazy-load rare panels, editors, maps, chartsSpinner where the main task should be
Vendor splitShare stable dependencies across routesLarge common chunk becomes everyone’s tax
Tree-shakingRemove unused exports through ESM analysisBarrel imports or side effects retain whole packages
PrefetchSpeculatively fetch likely next code/dataWastes bandwidth and battery on low-intent paths

Analyzer workflow: capture production build stats, sort by parsed size and compressed size, group modules by feature owner, then ask whether each large module is needed before the first meaningful task. Chase the largest cohorts before tiny syntax-level changes.

Import boundaries: dynamic import() is useful when a dependency is genuinely conditional: admin-only screens, rich text editors, map SDKs, charting, search overlays, heavy date/time libraries, or payment flows after checkout intent.

import dynamic from "next/dynamic";

const RevenueChart = dynamic(() => import("./RevenueChart"), {
  loading: () => <ChartSkeleton />,
});

Tree-shaking requirements: use real ESM imports, avoid catch-all barrels for hot packages, and keep package.json sideEffects accurate. Setting sideEffects: false blindly can remove CSS registration, polyfills, telemetry setup, or custom element registration.

Prefetch policy: prefetch when intent is visible: hovered links, near-viewport links, predictable wizard steps, or idle time after the current task is stable. Disable or narrow prefetch for expensive routes, metered networks, or personalized data that may go stale before use.

Understanding

Splits move cost from initial load into later navigations. That improves outcomes only when deferred routes aren't on your activation critical path. Otherwise you lengthen time-to-task with extra round trips—you prove the trade against navigation profiles tied to KPIs—not gzip bragging alone.

Teams accumulate duplicate transitive dependencies (five date/format/icon stacks) disguised behind convenience imports; analyzers show clusters ripe for consolidation and internal shared utilities.

The hard part is that compression size is not the whole cost. JavaScript also costs parse, compile, execution, hydration, memory, and invalidation. A 60 KB library that runs expensive initialization may hurt more than a 120 KB module loaded after a clear user action.

Good splitting follows task boundaries, not file boundaries. A checkout route should not wait for the marketing carousel. A text editor should not ship to users who only read comments. A dashboard’s charting library may be acceptable if the dashboard is the task, but unacceptable if it blocks login or onboarding.

Practical examples

ScenarioGood moveRisk to check
Rare rich text editorLazy-load editor after “Edit” intentPreserve focus and avoid layout jump when editor arrives
Large icon libraryImport named icons from the package entry recommended by the libraryBarrel import may retain the full icon set
Heavy date libraryUse platform Intl or a smaller scoped helperLocale/timezone correctness still matters
Micro-frontend shellShare stable runtime dependencies intentionallyVersion skew can duplicate React or design-system runtime
Above-the-fold chartKeep in initial route if it is the product taskSkeleton must reserve dimensions to avoid CLS

Senior understanding

SignalWhat staff-level answers include
Design-system drifttoken/CSS pipeline unintentionally duplicated runtime blobs
Regressions in CIoptional route-level artifact budgets or smoke perf harness—honest about flakiness trade
Micro-frontend edgesshared vendor dedupe story + version skew governance

Call out tail latency on low-end devices. Thin medians can hide expensive cold starts during real navigation sequences, so compare field RUM with a repeatable lab trace before making the optimization permanent.

Staff operating model: set budgets by route group, fail builds only for stable signals, allow explicit budget waivers with an owner and expiry, and review dependency additions as architecture changes rather than “just npm install.”

Failure modes

  • Splitting a component that appears immediately after hydration, causing a waterfall.
  • Prefetching every route and making mobile users pay for pages they never open.
  • Shipping duplicate design-system packages because multiple teams pin different versions.
  • Marking a package side-effect-free when it registers CSS, globals, custom elements, or polyfills.
  • Measuring only gzip while ignoring parse/compile/execute and hydration cost.

Interview drill

Question: "A route became slower after adding charts and a rich editor. How do you reduce JS cost?"

Model answer structure:

  1. Start from production build stats and field symptoms, not package-name intuition.
  2. Group large modules by route and feature owner, including duplicate transitive dependencies.
  3. Ask whether each module is needed for the first meaningful task or can wait behind user intent.
  4. Split rare/editor/map/chart flows, preserve layout with skeletons, and tune prefetch by intent and network cost.
  5. Guard route budgets with owners, expiry for waivers, and field monitoring for parse/execute regressions.

Follow-ups to expect:

  • "Why is gzip size not enough?"
  • "How do barrel imports defeat tree-shaking?"
  • "When can code splitting make navigation worse?"

Diagram

Loading diagram…

See also

Mark this page when you finish learning it.

Spotted something unclear or wrong on this page?

On this page