Engrain by ByteRush

QLT.01.05 Mid-level· 11 minCore — asked often

JavaScript and asset budgets: keeping a fast site fast

Analyze bundles, split and tree-shake code, drop polyfills by targeting Baseline browsers, compress images, subset fonts, tame third-party scripts with facades and async loading, and enforce performance budgets in CI.

Comes up in: Web fundamentals, Frontend system design

Learn#

Priya’s team made their store fast in March. By September it was slow again: no single change was big, but forty small ones added 400 KB of JavaScript.

A performance budget is a limit the team agrees on, such as “170 KB of compressed JavaScript on first load”, checked automatically on every pull request. Performance problems that never ship do not need fixing later:

[
  { "name": "first-load JavaScript", "path": "dist/assets/index-*.js", "limit": "170 kB" },
  { "name": "styles", "path": "dist/assets/*.css", "limit": "30 kB" }
]

That is a .size-limit.json file for the size-limit tool, which fails the build when a limit is exceeded.

When a budget fails, a bundle analyzer shows which modules fill the bundle, usually one or two large dependencies.

A third-party script, such as chat widgets, analytics, ads and A/B tests, runs on your main thread with code you do not control, and often costs more than your own app.

A budget stops a regression in reviewStep 1 of 4

State

  • budget: script 170 KB

Console

    empty

    The team sets a budget: 170 KB of compressed JavaScript for the first load of the home page.

    Read the steps as text
    1. The team sets a budget: 170 KB of compressed JavaScript for the first load of the home page.
    2. A pull request adds a date library with every locale. The build reports 236 KB of JavaScript.
    3. CI fails the build and links the bundle analyzer report, which shows the locales taking 70 KB.
    4. The author imports only the needed locale. 168 KB: the check passes and the regression never ships.

    Predict#

    Predict first

    A support chat widget adds 250 KB of JavaScript, but most users never open it. What is the best approach?

    Interview angle#

    Say it like this — about 30 seconds

    “I set budgets per page type, for compressed JavaScript, images, fonts and third-party code. CI enforces them, for example with size-limit or Lighthouse CI. When one fails, I run a bundle analyzer. Usual fixes are route-level code splitting, tree-shakable imports, replacing heavy libraries, and dropping polyfills by targeting Baseline browsers. For third-party scripts, I load them after the main content, use facades for chat widgets and video embeds, and remove ones nobody uses.”

    Stays on your device. Nothing is uploaded.
    Follow-up How do you handle the performance cost of third-party scripts?
    Audit and remove, load async or after interaction, use facades, self-host when allowed, and give them a budget.
    Follow-up How do you keep a large app's bundle size under control over time?
    Budgets in CI that fail the build, bundle analysis on each PR, and owners for big dependencies.
    Follow-up What stops tree shaking from working?
    CommonJS modules, side effects at import time, and importing a library’s whole index instead of single functions.

    Practice#

    Guided

    Write the check a CI step runs: totals per asset type, overall and third-party, against budgets.

    Done when

    • Each set budget is compared with the right total.
    • Violations report the actual size, the limit and the overage, in the given order.
    • Missing budgets are not checked.
    Your solution
    Ctrl/⌘ + Enter

    Remember#

    Try to answer each card before you open it. Then add them to your reviews.

    What is a performance budget?

    An agreed limit on size or timing, checked automatically on every change.

    What is a facade for a third-party widget?

    A lightweight placeholder that loads the real widget only when needed.

    What blocks tree shaking?

    CommonJS, import-time side effects, and whole-library imports.

    How do you drop most polyfills?

    Target Baseline browsers in your build, so modern features ship untranspiled.

    Go deeper

    Four layers. Stop at the one your interview needs; each ends with a quick check.

    The core ideaEvery round

    Budget JavaScript separately: every kilobyte must be downloaded, parsed and executed on the main thread, which matters most on cheap phones. Images cost bandwidth but not main-thread time.

    Check yourself: Why budget compressed JavaScript and not just total bytes?

    JavaScript costs CPU to parse and run, not just bandwidth.

    How it works underneathSenior rounds
    Tree shaking removes unused exports from ES modules (JS.10.01); mark packages side-effect-free and import named functions.
    Split by route and by heavy feature (RCT.11.02).
    Targeting Baseline browsers drops most polyfills and transpilation Source: web.dev: Baseline.
    Check yourself: What lets a bundler remove unused exports?

    Static ES module imports and code without side effects (tree shaking).

    Edge cases and trapsWhere people slip

    Images: AVIF or WebP, right sizes with srcset, compression. Fonts: subset to the scripts you need (Latin, Devanagari), use WOFF2, and limit weights.
    Partytown-style tools move some third-party scripts into a worker, with compatibility limits.

    Check yourself: How does font subsetting help?

    It removes glyphs you never use, making font files much smaller.

    In productionStaff-level answers

    Lighthouse CI can assert resource sizes and metrics on every pull request Source: Lighthouse CI: Configuration. Track field metrics too (QLT.01.06), since lab budgets cannot see real devices.

    Check yourself: Name a tool that enforces budgets in CI.

    Lighthouse CI (budgets or assertions), or bundle size checks such as size-limit.

    Finished the five steps? Mark it, and its cards join your reviews.

    Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.

    Interview questions this answers

    • How do you handle the performance cost of third-party scripts?
    • How do you keep a large app's bundle size under control over time?