All 32 checks with why-it-matters prose, severity, and cross-references to related audits.
A JavaScript bundle over 250KB gzipped on the initial route directly punishes mobile users and anyone on a slower connection. ISO 25010:2011 time-behaviour is the formal hook, but the real-world impact is measured in seconds: Google's research shows that each additional second of load time increases bounce rate by up to 20%. An oversized initial bundle means users see a blank or non-interactive page while megabytes of JavaScript parse and execute — before a single product, article, or dashboard widget appears.
Why this severity: Critical because an oversized initial bundle directly delays Time to Interactive, blocking all user activity until the browser finishes parsing and executing JavaScript — a failure visible to every first-time visitor.
performance-deep-dive.bundle-analysis.bundle-size-criticalSee full patternWhen tree-shaking is disabled or ineffective, every utility your bundler pulls in ships in full — including the 95% of lodash or date-fns that your code never calls. On a typical project that imports from 10–15 large libraries, inactive tree-shaking can add 200–400KB of dead code to the production bundle. ISO 25010:2011 resource-utilisation captures the storage and bandwidth waste; the user-facing cost is slower parse and execution time on every visit.
Why this severity: High because ineffective tree-shaking silently inflates the production bundle with unused code, adding hundreds of kilobytes that slow parse time for every visitor without any obvious error signal.
performance-deep-dive.bundle-analysis.tree-shaking-enabledSee full patternWithout route-level code splitting, your admin dashboard, settings page, and checkout flow all ship to users who visit only your landing page. A monolithic bundle forces every visitor to download and parse code for routes they will never reach in that session. ISO 25010:2011 time-behaviour flags the slowdown; the practical result is a Time to Interactive that scales with your total codebase size rather than the complexity of the current page.
Why this severity: High because a single monolithic bundle forces every user to download and parse code for all routes simultaneously, making Time to Interactive proportional to total app size rather than the page being visited.
performance-deep-dive.bundle-analysis.code-splitting-routesSee full patternCharting libraries (Recharts, Chart.js), code editors (Monaco), and rich-text editors routinely weigh 100–300KB gzipped each. Importing any of them statically means that weight lands in the initial bundle, inflating Time to Interactive for every page — including pages where the component never renders. ISO 25010:2011 time-behaviour captures the latency hit; the business impact is users abandoning a landing page because a chart used only on the analytics dashboard was eagerly loaded.
Why this severity: Medium because heavy UI components inflate the initial bundle and delay interactivity even on pages where those components are not displayed, though the impact is proportional to the library sizes involved.
performance-deep-dive.bundle-analysis.dynamic-imports-heavy-uiSee full patternWhen vendor and application code share the same bundle, every code change — even a single-line fix — invalidates the cached bundle for all users. Separating vendor chunks means React, lodash, and other stable libraries stay cached across deploys. Only the small application chunk changes, so returning users download a few kilobytes rather than hundreds. ISO 25010:2011 time-behaviour captures the repeated re-download cost; unconfigured chunk splitting turns every deploy into a full cache miss for your entire user base.
Why this severity: Medium because mixed vendor/app chunks force users to re-download stable third-party code on every deploy, eliminating long-lived cache benefits and increasing bandwidth costs unnecessarily.
performance-deep-dive.bundle-analysis.vendor-chunks-splitSee full patternTailwind CSS ships with thousands of utility classes by default. Without purging, the full Tailwind stylesheet is 3–4MB uncompressed (150–200KB gzipped). A project that actually uses 200 classes ships the other 99% as dead CSS, adding meaningful parse time and network cost on every page load. ISO 25010:2011 resource-utilisation targets this waste; the user-facing effect is a heavier initial render with no corresponding functionality.
Why this severity: Low because unused CSS adds network and parse overhead, but the user-visible delay is typically under 200ms and does not block interactivity unless the CSS bundle is especially large.
performance-deep-dive.bundle-analysis.css-purging-enabledSee full patternHashed assets are designed for aggressive caching — the filename changes only when the file content changes, so browsers can safely cache them forever. Without `immutable, max-age=31536000`, browsers re-validate these files on every visit, adding a round-trip for each asset even when the content is identical. Conversely, if HTML files are cached with a long `max-age`, users miss updates entirely until the cache expires. ISO 25010:2011 time-behaviour frames the latency; the business impact is repeat visitors waiting on unnecessary network round-trips.
Why this severity: Critical because misconfigured cache headers force re-validation of every asset on every visit, eliminating the performance gains that content-hashed filenames are specifically designed to provide.
performance-deep-dive.caching-cdn.cache-control-hashed-assetsSee full patternA service worker that caches resources without versioning accumulates stale content indefinitely. When you ship a bug fix or critical update, users with a running service worker continue receiving the broken cached version — potentially for weeks, until they manually clear their cache or the browser evicts it. The cache name must change on each deploy, and the `activate` event must delete old caches. ISO 25010:2011 time-behaviour flags cache effectiveness; the operational risk is users being stuck on outdated or broken application versions with no server-side way to force an update.
Why this severity: High because unversioned service worker caches leave users running stale application code after deploys, with no server-side mechanism to force a refresh without manual cache clearing.
performance-deep-dive.caching-cdn.service-worker-cache-versioningSee full patternEvery read-only API endpoint that responds with `no-cache` forces the browser to wait for a full network round-trip before rendering data — even for data that changes infrequently. `stale-while-revalidate` flips this: the browser serves the last cached response immediately (zero perceived latency), then fetches a fresh copy in the background. ISO 25010:2011 time-behaviour measures the round-trip cost; the user-visible gain is instant page paint for the common case, with fresh data arriving shortly after.
Why this severity: High because serving all API responses with `no-cache` forces users to wait for a full network round-trip on every data fetch, adding hundreds of milliseconds to perceived load time even when cached data would be adequate.
performance-deep-dive.caching-cdn.stale-while-revalidate-apisSee full patternA CDN cache hit ratio below 80% means most static asset requests are reaching your origin server rather than being served from an edge node. Each origin-served request adds 50–400ms of latency depending on user geography, and it multiplies your origin load. ISO 25010:2011 time-behaviour captures the latency cost; the operational consequence is higher infrastructure cost and slower asset delivery specifically for users furthest from your origin region.
Why this severity: Medium because a low cache hit ratio degrades performance for distant users and increases origin server load, but the impact is bounded to cacheable assets and does not affect correctness.
performance-deep-dive.caching-cdn.cdn-cache-hit-ratioSee full patternCWE-525 (Information Exposure Through Browser Caching) is the formal classification, but the concrete failure is simpler: if a user-specific API response is cached at the CDN edge without `Cache-Control: private`, the next user who hits the same edge node may receive another user's private data. This is a data-exposure bug masquerading as a caching misconfiguration. ISO 25010:2011 time-behaviour flags the caching intent; the security consequence is personally identifiable or account-sensitive data served cross-user from an edge cache.
Why this severity: Medium because edge-caching personalized data exposes user-specific information to other users, but exploitation requires the attacker to hit the same edge node shortly after the victim — reducing but not eliminating real-world risk.
performance-deep-dive.caching-cdn.edge-cache-personalizationSee full patternMemory leaks in browser applications compound silently over a user session. A `useEffect` that registers a resize listener without a cleanup function adds a listener on every re-render; after 50 renders, 50 listeners are firing simultaneously. ISO 25010:2011 resource-utilisation frames the waste; the user-visible outcome is an application that slows, stutters, or crashes after extended use — worst for users who keep tabs open for hours, such as internal tooling users or power users of dashboards.
Why this severity: High because undetected memory leaks degrade performance progressively throughout a user session, causing crashes or forced page reloads that interrupt workflows in ways that are difficult to trace without explicit profiling.
performance-deep-dive.runtime-memory.memory-leak-detectionSee full patternA GC pause of 250ms during a scroll or animation freezes the frame pipeline: no input events are processed, no pixels are painted. Users experience this as a sudden hard stutter that is qualitatively worse than slow load times. ISO 25010:2011 time-behaviour captures the latency; the GC pressure that produces long pauses is typically caused by allocating large arrays or objects inside render loops or scroll handlers — code patterns that generate garbage faster than incremental GC can collect it.
Why this severity: Medium because GC pauses over 50ms produce visible jank during animations and scrolling, but the impact is intermittent and depends on workload and device memory, making it a degraded-experience issue rather than a blocking failure.
performance-deep-dive.runtime-memory.gc-pauses-under-50msSee full patternEach DOM node has a memory and layout cost. A page with 3,000+ nodes triggers slower style recalculations, longer layout passes, and higher memory usage — all of which degrade scrolling smoothness and interaction responsiveness. ISO 25010:2011 resource-utilisation quantifies the overhead; the practical failure mode is a data table that renders 5,000 rows at once, consuming hundreds of megabytes of memory and making the page unusable on mid-range devices.
Why this severity: Medium because excessive DOM nodes slow layout recalculation and increase memory consumption, causing scroll jank and interaction lag that scales with the number of nodes beyond the 1,500 threshold.
performance-deep-dive.runtime-memory.dom-node-count-limitSee full patternA server that waits for all data fetches to complete before sending a single byte of HTML holds users on a blank screen. If your slowest API call takes 1.5 seconds, every user stares at white for at least that long — regardless of how fast your other data loads. Streaming SSR and Suspense boundaries let the browser start rendering and displaying content from the fast paths while slow paths are still in-flight. ISO 25010:2011 time-behaviour captures the wait; the user experience impact is measured in perceived load time, which drives engagement and retention.
Why this severity: Medium because blocking SSR multiplies the latency of your slowest data dependency across every page load, but the fix (adding Suspense boundaries) is low-risk and scoped to server components.
performance-deep-dive.runtime-memory.streaming-ssr-enabledSee full patternA request waterfall where API B cannot start until API A completes means your page load time is the sum of all sequential fetch durations, not the maximum. A five-hop waterfall with 200ms fetches adds 1 full second of latency that cannot be improved by any infrastructure optimization — it is a code architecture problem. ISO 25010:2011 time-behaviour formalizes the concern; the user-facing result is a page that appears slow even on fast connections because the CPU is idle while waiting for chained network calls to resolve.
Why this severity: Critical because sequential request chains make page load time additive rather than concurrent — each extra hop adds unavoidable latency that cannot be solved by faster servers or better caching.
performance-deep-dive.network-waterfall.request-waterfall-optimizedSee full patternLCP is a Core Web Vital that Google uses in its ranking algorithm. An LCP over 2.5 seconds triggers the "Poor" label in PageSpeed Insights and directly reduces organic search visibility. Beyond SEO, a slow LCP means users wait over 2.5 seconds before seeing the primary content of the page — a threshold above which mobile abandonment rates climb sharply. ISO 25010:2011 time-behaviour captures the metric; the business impact spans SEO rankings, ad quality scores, and first impressions.
Why this severity: Critical because LCP directly affects Google search rankings, and an LCP over 2.5 seconds is classified as 'Poor' by Core Web Vitals — failing this metric has measurable, ongoing SEO and conversion consequences.
performance-deep-dive.network-waterfall.lcp-optimizedSee full patternEvery navigation click triggers a full page-load waterfall: DNS lookup, TCP handshake, server response, asset parsing. Prefetching starts this waterfall before the user clicks — on hover or viewport entry — so that by the time the click registers, the next page's assets are already in cache. ISO 25010:2011 time-behaviour captures the latency savings; without prefetching, even a well-optimized site feels sluggish on navigations because each page transition starts from scratch.
Why this severity: High because absent prefetching forces every navigation to begin a full request waterfall at click time, adding hundreds of milliseconds of avoidable latency to each page transition even when the destination page is predictable.
performance-deep-dive.network-waterfall.prefetching-configuredSee full patternHTTP/2 Server Push and 103 Early Hints both address the same problem: the browser cannot start fetching critical assets (CSS, fonts, JS) until it has received and begun parsing the HTML response. Early Hints tells the browser what to fetch before the HTML is ready — shaving 50–200ms from FCP and LCP on cold loads. ISO 25010:2011 time-behaviour captures the gain; for users on high-latency connections (mobile networks), this optimization is especially valuable because each round-trip is expensive.
Why this severity: Low because server push and early hints are additive optimizations that improve FCP on cold loads, but their absence does not cause correctness failures and the improvement is typically under 200ms on fast connections.
performance-deep-dive.network-waterfall.http2-server-pushSee full patternWhen three components on a page independently call `fetch('/api/products')`, three separate network requests fire — even if the responses will be identical. On a dashboard that renders six widgets each fetching user data, that is six redundant requests hitting your database in parallel. ISO 25010:2011 resource-utilisation quantifies the waste; the user-visible cost is slower component render times and unnecessary origin load, both of which worsen under traffic.
Why this severity: Low because duplicate requests increase network and server load proportionally to component count, but the impact is bounded — each duplicate adds one extra request rather than causing cascading failures.
performance-deep-dive.network-waterfall.request-deduplicationSee full patternA synchronous third-party `<script>` tag in `<head>` blocks HTML parsing until the script is downloaded, parsed, and executed. A single slow analytics script — say, a 400ms load from a third-party CDN under load — delays your entire page render by 400ms. ISO 25010:2011 time-behaviour captures the blocking cost; the full impact is that your first-party performance work is held hostage to the availability and speed of external vendors you do not control.
Why this severity: Low because blocking third-party scripts delay render but rarely cause total page failures; the risk scales with the number of synchronous scripts and the reliability of the external CDNs serving them.
performance-deep-dive.network-waterfall.third-party-scripts-asyncSee full patternPerformance regressions introduced by a dependency update, an accidental eager import, or a new feature are invisible without automated measurement. By the time a user reports that the site "feels slow," the regression may have been live for weeks and has already impacted SEO rankings, conversion rates, and Core Web Vitals scores. ISO 25010:2011 time-behaviour captures the measurement requirement; Lighthouse CI enforces a performance floor in the same pipeline that would otherwise let the regression ship undetected.
Why this severity: High because without Lighthouse CI in the deployment pipeline, performance regressions merge and ship silently, compounding over time with no automated gate to catch degradation before it reaches production.
performance-deep-dive.regression-prevention.lighthouse-ci-configuredSee full patternDuplicate dependency versions — two installs of the same library at different patch versions — silently inflate `node_modules` and the final bundle. Projects with complex dependency trees can end up with lodash@4.17.19 and lodash@4.17.21 both installed, contributing duplicate code to the production bundle. ISO 25010:2011 resource-utilisation flags the storage and bandwidth waste; CWE-1104 (Use of Unmaintained Third-Party Components) applies when the older duplicate version has known vulnerabilities that the newer version has patched.
Why this severity: Low because duplicate dependencies waste disk space and add bundle weight, but do not cause correctness failures — the impact is proportional to the size and count of the duplicated packages.
performance-deep-dive.regression-prevention.no-duplicate-dependenciesSee full patternWithout an enforced performance budget, bundle size drifts upward with each feature addition. A new charting library here, a heavier date picker there — after six months, your initial bundle has grown from 200KB to 600KB with no single commit being obviously culpable. ISO 25010:2011 time-behaviour captures the gradual degradation; an enforced budget makes the growth visible immediately, when a single PR pushes past the threshold, rather than during a quarterly performance audit.
Why this severity: Low because performance budget enforcement is a preventive control — its absence does not immediately degrade performance, but allows gradual regression to accumulate undetected across multiple PRs.
performance-deep-dive.regression-prevention.performance-budget-enforcedSee full patternSynthetic monitoring (Lighthouse CI, automated browser tests) catches regressions in controlled conditions. RUM (Real User Monitoring) captures what actually happens across your user base — different devices, networks, geographies, and browser versions. ISO 25010:2011 time-behaviour requires measuring real performance; without RUM, you are optimizing against lab conditions that may not match production. Without synthetic monitoring, you lack a baseline to detect regressions before they affect users.
Why this severity: Low because monitoring is detective rather than preventive — its absence does not degrade performance directly, but means regressions go undetected until user complaints surface them, often after significant impact.
performance-deep-dive.regression-prevention.rum-synthetic-monitoringSee full patternA monitoring dashboard that collects LCP data but has no alert threshold for regression is a passive tool — it shows you that LCP increased from 1.8s to 3.2s only after someone manually checks the dashboard. ISO 25010:2011 time-behaviour requires measuring and acting on performance data; an alert on LCP regression > 200ms converts monitoring from a reporting artifact into an active safety net. Without alerts, performance regressions persist in production for days or weeks before anyone investigates.
Why this severity: Info because alerts are a meta-control on existing monitoring — their absence does not cause performance failures directly, but reduces the team's mean time to detect regressions from minutes to days.
performance-deep-dive.regression-prevention.regression-alerts-configuredSee full patternStale alert thresholds turn your regression pipeline into noise. When baselines drift — new features, added dependencies, heavier pages — thresholds set at launch either fire constantly and train the team to ignore them, or sit so loose that real regressions slip past unseen. Both outcomes erode the signal that observability tooling is supposed to provide, and users pay the cost in LCP, TBT, and abandoned sessions the team never hears about.
Why this severity: Info because stale thresholds degrade monitoring quality rather than directly breaking user-facing performance or availability.
performance-deep-dive.regression-prevention.alert-thresholds-reviewedSee full patternAt 60fps, each animation frame has a 16ms budget. Any JavaScript that runs synchronously inside a frame — including a `setInterval` callback or a DOM layout read followed by a write — can consume that budget entirely, causing the browser to skip the frame and produce visible stutter. ISO 25010:2011 time-behaviour captures the responsiveness requirement; the user-visible failure is an animation that stutters at irregular intervals, particularly during scroll or on lower-powered devices.
Why this severity: Info because frame-budget violations produce cosmetic jank rather than functional failures — they are worth fixing for polish and accessibility but do not prevent users from completing tasks.
performance-deep-dive.regression-prevention.animation-frame-budgetSee full patternLayout thrashing occurs when JavaScript alternates between DOM reads (which force the browser to recalculate layout) and DOM writes within the same synchronous block. A loop that reads `el.offsetHeight` then writes `el.style.height` for 50 elements triggers 50 layout recalculations instead of one. ISO 25010:2011 time-behaviour measures the cost; the user-visible outcome is continuous paint flashing and dropped frames during any interaction that touches DOM geometry — typically resizing, drag, and scroll handlers.
Why this severity: Info because layout thrashing produces cosmetic jank and is difficult to trigger in synthetic tests, making it a polish-level issue rather than a blocking defect — though it can be severe on devices with constrained GPU memory.
performance-deep-dive.regression-prevention.paint-profiling-cleanSee full patternWithout documented cache-busting strategy, every developer on the team is guessing: does the CDN auto-purge on deploy? Do users need to hard-refresh to get the latest build? Are the service worker caches versioned? ISO 25010:2011 time-behaviour requires that the system delivers correct and current assets; gaps in cache-busting understanding lead to incidents where users receive stale JavaScript after a critical bug fix, because no one was certain which caching layer was holding the old version.
Why this severity: Info because undocumented caching is a knowledge-management gap rather than an active bug — the system may function correctly, but the absence of documentation increases incident response time when stale-asset problems do occur.
performance-deep-dive.regression-prevention.cache-busting-documentedSee full patternAn asset named `app.js` with no content hash cannot be safely cached for more than a few minutes — the browser has no way to know whether the file changed since the last visit. Setting a long `max-age` on an unhashed asset means users receive stale code after a deploy; setting a short `max-age` means the asset is re-downloaded on every visit. Content hashing is the mechanism that makes aggressive 1-year caching safe: the filename itself is the cache key. ISO 25010:2011 time-behaviour links asset hashing to time-behaviour because it determines whether repeat visitors pay full download cost on every page load.
Why this severity: Info because missing content hashes force a tradeoff between stale delivery and cache efficiency — the impact is either wasted bandwidth or stale assets, both of which degrade experience but do not cause functional failures.
performance-deep-dive.regression-prevention.asset-content-hashSee full patternRendering 500 list items in the DOM simultaneously creates 500 DOM nodes with layout, paint, and event handling overhead — even for items the user cannot see. Scroll performance on a fully-rendered 10,000-row table degrades to 10–20fps on mid-range devices because every scroll event triggers layout recalculation for all 10,000 nodes. ISO 25010:2011 resource-utilisation captures the memory cost; the user-visible failure is a janky, unresponsive list that makes the application feel broken for users browsing large datasets.
Why this severity: Info because virtual scrolling is an optimization for large lists — its absence only matters when the list exceeds 100 items, and many applications avoid this scenario through pagination or search.
performance-deep-dive.regression-prevention.virtual-scrolling-listsSee full patternRun this audit in your AI coding tool (Claude Code, Cursor, Bolt, etc.) and submit results here for scoring and benchmarks.
Open Performance Deep Dive Audit