Prompt and context
An ecommerce product page loads analytics, chat, advertising, and A/B-testing scripts. Mobile real-user p75 LCP is 3.8 seconds and INP is 280 milliseconds; third-party JavaScript transfers about 1.2 MB from 14 origins, while desktop lab tests look healthy. You must preserve necessary business capability while limiting third-party effects on the network, main thread, privacy, and page availability.
Clarify four boundaries first: which scripts must run before the first screen, which are needed only after interaction, whether scripts depend on one another’s order, whether data may be sent before consent, and whether a third-party outage may block checkout. Third-party code is part of the page’s execution environment; it cannot be treated as an ordinary static asset that another team owns forever.
This fits frontend, web-platform, and performance-engineering interviews. Public frontend interview material treats async, defer, script ordering, and performance trade-offs as recurring fundamentals; web.dev, MDN, and W3C material provide the technical basis for loading timing, long tasks, CSP, and source control. The center of the question is governance and proof, not memorizing a framework component.
What the interviewer is assessing
A weak answer says “add async everywhere” or “put scripts at the bottom of the page.” A strong answer inventories scripts by user value and critical-path impact, then chooses async, defer, modules, interaction-triggered loading, or an iframe facade based on dependency and failure boundaries.
The interviewer wants five signals: can the candidate separate download time from execution time; explain unordered async versus ordered defer; combine performance budgets, consent, CSP/SRI, and supply-chain risk; isolate a vendor failure without blocking checkout; and use RUM, long-task data, and a controlled comparison to prove the change.
A Lighthouse score alone is insufficient. Lab conditions do not represent low-end phones, slow networks, content blockers, or a vendor server that stalls; the answer should include real-user percentiles, script execution time, and business completion rate.
Clarifying questions to ask first
- Which scripts are truly needed for first paint or first interaction? Payment and essential fraud checks may be critical; chat, recommendations, and replay can usually wait for interaction or idle time.
- Do scripts depend on one another? Independent analytics can use
async; DOM- or order-dependent application code usually usesdefer; unknown dependencies must not be parallelized. - What is allowed before consent? Define purpose, region, and consent state before creating a script, cookie, network request, or anonymous measurement.
- What page data may a vendor access? Same-origin code can read page context; if a chat or ad only needs to render, prefer an iframe or server-side event with a smaller permission surface.
- What is the user experience on failure? Purchase, login, and navigation need first-party paths; a vendor timeout must not make a critical button or main thread wait forever.
- Can the vendor be replaced or self-hosted? Self-hosting may remove DNS and vendor-availability risk but adds update, integrity, license, and cache ownership; it is not automatically safer.
A 30-second answer framework
“I would build an inventory recording business value, dependencies, bytes, requests, main-thread time, data purpose, and failure impact, then classify each script as critical, deferred, or removable. Independent scripts use async; DOM- or order-dependent scripts use defer; interactive widgets use a facade or iframe. I would not create non-essential trackers before consent. CSP would allow only reviewed origins, and fixed assets could use SRI. Each vendor gets byte, execution-time, and error budgets. RUM would compare LCP, INP, long tasks, conversion, and script errors by device and consent state. I would gray out removal or deferral first, then use fault injection and rollback to prove the page still completes the core task.”
Step-by-step deep answer
1. Build the inventory and critical path
Record each script’s vendor, version, trigger, dependencies, request origins, transfer bytes, parse and execution time, long tasks, data read, owner, and removal condition. State value as a testable outcome such as “attribute checkout failures,” not “improve experience.” A script with no clear purpose, owner, or metric is a removal candidate.
Draw the dependency graph for first paint and first interaction. Keep only first-party code needed for the initial screen, login, cart, and payment on the critical path. Analytics, chat, recommendations, replay, and marketing tags can often wait until after paint or explicit interaction. Asynchronous download does not remove parse, compile, or main-thread execution cost.
2. Choose loading semantics from dependencies
A classic script without an attribute blocks HTML parsing. async downloads in parallel and executes as soon as it is ready, with no ordering guarantee; it fits independent analytics or ads. defer also downloads in parallel but runs after parsing in document order, which fits code that needs the DOM or another script. Module scripts are deferred by default, but their dependency graph still needs review.
Use a decision table:
| Method | Execution timing | Ordering | Good fit | Main risk |
|---|---|---|---|---|
| Plain classic script | Executes after download and blocks parsing | Document order | Rare synchronous bootstrap | Delays parsing and first paint |
async | Executes when download completes | No guarantee | Independent analytics, ads, simple widgets | Can interrupt parsing; races dependencies |
defer | Executes after parsing in order | Preserved | DOM- or startup-dependent code | Still delays DOMContentLoaded |
| Interaction-triggered | On user action or idle time | Loader-controlled | Chat, maps, video, replay | First open has extra latency |
| Iframe facade | Static shell first, isolated embed later | Cross-document boundary | Video, payment UI, complex widget | Communication and accessibility cost |
Do not mark dependent scripts all async. If plugin.js needs vendor.js, use ordered defer, module imports, or an explicit loader rather than hoping download timing works out.
3. Defer, trim, or replace functionality
Use user interaction, requestIdleCallback with a timeout fallback, or a post-paint queue for non-critical work. A chat button can be a first-party static entry that creates the widget only on click; a video can show a thumbnail and play control before embedding an iframe. This reduces first-screen network and main-thread work and avoids paying for features the user never uses.
Remove duplicate tag managers, analytics SDKs, and abandoned experiments. Ask vendors for reduced builds, page-specific bundles, compression, and caching. Self-hosting may reduce third-party DNS or availability risk, but it needs version updates, SRI, licensing, cache invalidation, and rollback; it does not eliminate execution cost.
4. Define privacy, security, and failure boundaries
Before consent is known, do not create non-essential tracking scripts or load them and merely disable sending later. Define whether consent withdrawal clears cookies, stops queues, and prevents subsequent requests. Restrict origins with CSP script-src and relevant connection directives; use SRI for fixed-version external resources. Audit dynamic loading, vendor-loaded dependencies, and strict-dynamic separately rather than trusting only the first domain.
Third-party code running same-origin has a wide permission surface. Put render-only components in an iframe and send the minimum fields through postMessage. Keep purchase, login, navigation, and error recovery first-party; end vendor timeouts with a timeout, circuit break, or placeholder instead of blocking the core flow.
5. Set budgets and monitor
Give each vendor and page type budgets for transfer bytes, request count, main-thread execution, long tasks, error rate, and contribution to LCP/INP. Exceeding a budget triggers review or automatic degradation, not a future cleanup ticket. Segment budgets by low-end device, slow network, and consent state because averages hide the tail.
In the lab, use DevTools Performance and Network panels, WebPageTest, and fault injection to observe parsing, execution, long tasks, and single-point failures. In production, use RUM to record source, resource timing, PerformanceObserver long tasks, LCP, INP, CLS, conversion, and errors. Compare device, browser, region, and page-template segments so traffic mix changes are not mistaken for an optimization.
6. Roll out, roll back, and govern vendor changes
Gray out one page template and a small share of mobile traffic first. Every script change carries a version, source, consent configuration, and rollback switch; vendor updates follow the same path. If a script times out or throws, skip it or degrade by default rather than retrying forever. Rollback restores the last reviewed manifest instead of pulling an unknown version from a vendor URL.
Verify the invariants: users can browse, add to cart, and purchase when a vendor fails; restricted data is not requested without consent; script budgets remain under limits; CSP reports show no new origin; and critical interaction INP does not regress. Keep explicit removal or replacement triggers for every vendor.
7. Make verification executable
Test slow DNS, vendor 5xx, a download interrupted halfway, a thrown exception, a main-thread long task, consent withdrawal, disabled third-party cookies, a content blocker, and an older browser. Assert more than visual rendering: purchase state, recovery, keyboard interaction, screen-reader announcements, and data-request boundaries.
Use a control group that changes only one loading strategy, such as synchronous to deferred, while holding content and traffic rules constant. Verify p75/p95 LCP, INP, long tasks, resource bytes, conversion, and vendor errors. If checkout improves but opening chat becomes slower, report the trade-off and tune the trigger rather than reporting one attractive score.
High-quality sample answer
“I would not start by adding async to all 14 scripts. I would inventory each script’s purpose, dependencies, requests, bytes, main-thread time, data use, owner, and failure impact, then classify it as critical, deferred, or removable. Payment and login stay on a first-party critical path; independent analytics can be asynchronous; DOM- or order-dependent code uses defer; chat, maps, and video load on interaction or behind an iframe facade.
Before consent I would not create non-essential trackers. CSP would allow reviewed sources and fixed assets could use SRI. A render-only widget goes in an iframe, while purchase and navigation retain a first-party fallback. Every vendor has byte, execution, long-task, and error budgets.
I would gray the change to a small mobile cohort. In the lab I would throttle the network, inspect Performance traces, and inject vendor failures. In production I would segment RUM by device, region, and consent state and compare LCP, INP, CLS, long tasks, resource timing, conversion, and errors. Every change is reversible and vendor updates are reviewed. If a vendor fails, core purchase still works; if data is sent before consent or guardrails exceed budget, expansion stops.”
Common mistakes
- Adding
asyncto every script → dependent scripts can run out of order and execution can still interrupt parsing → map dependencies; useasyncfor independent work anddeferor modules for ordered work. - Only moving tags to the body end → download and execution still compete for the main thread and interaction can regress → defer, trim, split, or trigger by function, then measure execution cost.
- Using only Lighthouse → lab conditions omit low-end devices, slow networks, and vendor stalls → compare real-user percentiles, long tasks, and business metrics.
- Disabling tracking after consent is denied → the script already ran and may have sent a request → check consent before creating the script or network request.
- Treating self-hosting as a complete security fix → updates, integrity, licensing, and execution cost remain → combine reviewed versions, SRI, CSP, budgets, and rollback.
- Retrying a failed vendor until it works → a vendor becomes a single point of failure for the critical page → use timeouts, a degraded placeholder, and a first-party core path.
Follow-ups and responses
Follow-up 1: Analytics must send a first-screen event immediately. Can it be critical?
First check whether it truly must be sent before first paint. If a page-view event can be batched after paint and loss is acceptable, defer it. If attribution or compliance requires earlier delivery, use an independent async script, a short timeout, and a small first-party queue; do not block rendering. Set the budget by comparing event delay with the business value of LCP and INP.
Follow-up 2: A vendor provides only a dynamic URL. How can you use SRI?
You cannot pin a reliable hash to content that changes continuously. Request versioned assets, build a self-hosted signed-release process, or isolate the feature in an iframe or server-side integration. CSP, access audits, and change monitoring reduce risk. Do not fake a hash or replace it with unsafe-inline.
Follow-up 3: A third-party script still creates long tasks with defer. What next?
defer changes timing, not parse or execution work. Use the Performance trace to identify the script and task, then ask the vendor to split it, trigger it on interaction, remove features, or move work to an iframe or server-side event. If it must run, schedule smaller chunks in idle time with a timeout and verify the tail with RUM.
Follow-up 4: Marketing wants five tags added at once. What do you say?
Require each tag to state its purpose, owner, expected decision, consent category, performance budget, and removal condition. Check for duplicate collection or an existing event that already answers the question. Measure in a sandbox and gray out only after the value is clear. A tag without measurable value or within-budget cost does not enter production.