Frontend observability and field performance
Measure real visits, split them by who had them, and give every report its release and its trace.
Predict first
Before you read: predict
ExamHall's exam page scores 98 in Lighthouse on the team's CI laptop, with an LCP of 0.9 s. On exam day its RUM beacons hold 400 LCP values, each tagged with device class and country: 300 from desktops in Germany, all 1.2 s; 60 from phones in Germany, all 1.8 s; 40 from Chromebooks in Egypt, 28 of them 2.0 s and 12 of them 4.4 s. The team's dashboard reads each p75 as the value at rank ⌈0.75 × n⌉ of the sorted values.
What p75 LCP does the Chromebooks-in-Egypt segment get?
The 30-second version
Lighthouse loads your page once, on one device and network: good for debugging, not a verdict. In the field, the page sends home each real visit's Web Vitals, errors and trace ids, tagged by who had them. Read p75 per segment, keep each build's source map, carry traceparent into the API, and page on the share of sessions with errors.
Measure real visits, split them by who had them, and give every report its release and its trace.
How it works
Step 1 of 12
In the kitchen
- the rain chamber: one tent, one downpour, no wind, the same every time → a Lighthouse run (step 1)
Analogy: A tent maker's rain chamber and its warranty claims (outdoor equipment)
A tent maker tests each new model in a rain chamber, then sells it to campers everywhere, who send claims when a tent fails them.
| At the tent maker | In the frontend |
|---|---|
| the rain chamber: one tent, one downpour, no wind, the same every time | a Lighthouse run (step 1) |
| claims from campers in every weather and on every kind of ground | field data (step 2) |
| claims sorted by region and pitch: three in ten coastal tents leak while the national share looks small | p75 per segment (step 6) |
| the batch code stamped in a tent, read with that season's batch ledger; last season's ledger names another line | a minified frame and its build's map (step 8) |
| the order number on the camper's receipt, also on the factory's work ticket | traceparent from the browser's call to the API's span (steps 10–11) |
| the quality manager called when claims per hundred tents sold rise, not when the summer rush lifts the count | alerting on a rate (step 12) |
In real terms: the lab tells you whether a change made the page slower on one machine; only beacons from real visits, split by segment and release, tell you who is suffering and since when.
Breaks down when: claims come weeks later and only from unhappy campers. A RUM beacon comes from every visit, happy or not, within seconds.
Examples, run for real
4.1 ExamHall EXH-3440: one p75 for the whole exam page
Candidates at one school said ECON-201's exam page froze at the 09:00 start, while Lighthouse in CI gave 98. The RUM beacons carried only the page, so the dashboard showed one INP p75 for every candidate (EXH-3440). MR !3740 tags each beacon with device class, country, exam id and release, and splits the tile:
MR !3740 tags each beacon with device class, country, exam id and release
before MR !3740 (beacons tagged with the page only): exam page · 800 beacons · INP p75 152 ms (good)
after MR !3740, split by device class and exam: desktop · ECON-201 · 500 beacons · INP p75 135 ms (good)
after MR !3740, split by device class and exam: phone · ECON-201 · 200 beacons · INP p75 172 ms (good)
after MR !3740, split by device class and exam: school chromebook · ECON-201 · 100 beacons · INP p75 624 ms (poor)
The pooled p75 sat in the good band because school Chromebooks were 1 in 8 of the beacons. Split by device class and exam, their segment's p75 is poor (step 6). The tags are what made the split possible (step 5).
4.2 Streaming MR !3741: a player error with its own map
A streaming app's player threw "no rendition fits" for some viewers. The error tracker had only the minified frame. MR !3741 makes the build upload player.min.js.map under its release, 10.4, and leaves no comment in the bundle:
error report, release 10.4: "no rendition fits" at player.min.js:1:154
resolved with the map uploaded under release 10.4: abr.ts line 11, in pickRendition
that line: if (fits.length === 0) throw new Error('no rendition fits');
The frame on its own names a column in one long line. Resolved with the same build's map, it names the function and line that threw (step 8).
Picture it

Try it
Answer first, then check. Nothing is saved.
Question 1 of 8
ExamHall's exam page reports INP with web-vitals' onINP to /rum. A CI job opens the exam page in headless Chromium with the RUM script, waits for it to load and moves on to the results page, as a lab run does. In the same hour, a candidate opens the page, types one answer and follows the link to the next question.
How many INP beacons reach /rum from each visit?
Question 2 of 8
A streaming app's web player at https://watch.stream.test calls POST https://api.stream.test/playback/session when a viewer presses Play. The page runs the OpenTelemetry web SDK with new FetchInstrumentation() and no options; the API allows traceparent in CORS and starts its span from the incoming headers. In the trace backend, the browser's fetch span and the API's span for one Play carry different trace ids.
Which one change gives them the same trace id?
Question 3 of 8
A box office's checkout fails for some buyers. The error tracker shows release 10.02's top frame boxoffice.min.js:1:211, message "seat hold expired". 10.02's source map was not kept, so the team resolves the frame with boxoffice.min.js.map from release 10.01. Release 10.02 added a 4-line normalisePromo to checkout.ts, above confirmOrder.
What does the lookup return?
Question 4 of 8
ExamHall's candidate app reports every client error with its session id. On exam day the error tracker saw: 08:50–08:59, 3,000 active sessions and 15 sessions with one error each a minute; from 09:00, 30,000 active sessions and 150 sessions with one error each a minute; 09:03–09:07, one candidate's browser extension also throwing 1,200 errors a minute in one session; from 09:12, release 10.02's autosave bug adding 1,500 sessions with one error each a minute. Each rule pages after 3 minutes in a row above its line.
Which rule pages for the release bug and for nothing else?
Question 5 of 8
The interviewer: "Tell me what you would put on ExamHall's frontend dashboard for exam day, and what would page you." You get sandbox/dashboard.json, today's set-up: beacons tagged with the page only, the mean LCP and the mean INP of the whole site, and a page on more than 100 errors a minute. In 15 minutes, rewrite it so the dashboard shows which candidates suffer and since which release, and the pager wakes you for a real fault and nothing else.
Then run the check on your file and say in one sentence per change why.
Question 6 of 8
A payments ledger's team runs Lighthouse in CI on every merge and collects RUM from real users. For the statement page, last month's CI run reported an LCP of 3.4 s, while the field p75 LCP was 2.1 s. A developer asks which of the two numbers is wrong.
Which answer holds?
Question 7 of 8
A chat app's web composer loads web-vitals' attribution build and reports INP with onINP((m) => navigator.sendBeacon('/rum', JSON.stringify(m))), with no options. A user clicks Send three times, and each click's handler runs about 200 ms of work; then she follows a link to another page.
How many INP beacons reach /rum, and when?
Question 8 of 8
A streaming app's player bundle player.min.js has no sourceMappingURL comment. The server sends it with the header SourceMap: /maps/10.4/player.js.map, and also keeps a copy of the map beside the bundle as player.min.js.map. A viewer opens Chromium's DevTools on the player.
Which source map URL does Chromium hand DevTools for the bundle?
Go deeper
Pro tips
- Listen for
unhandledrejectionas well aserror. A failedawait fetch()in an async handler rejects a promise; it never reacheswindow.onerror. When: your tracker shows no errors while users report a failing save (step 7). Verify (RAN, Chromium 153,tips.mjs):tip 1 error listener only : 0 report(s) tip 1 error + unhandledrejection : 1 report(s) · unhandledrejection: autosave 503 - Keep each beacon small.
navigator.sendBeaconreturnsfalseand sends nothing when the browser will not queue the payload; batch metrics into small bodies and check the return value. When: you add attribution, URLs and stacks to RUM reports (steps 3, 5). Verify (RAN, Chromium 153,tips.mjs):tip 2 sendBeacon('/rum', 1 KiB body) -> true tip 2 sendBeacon('/rum', 70 KiB body) -> false
Source
Valid as of 2026-10-09.
- web.dev, Why lab and field data can be different (and what to do about it) (the curriculum row's third source), https://web.dev/articles/lab-and-field-data-differences (
webdev-lab-field.txt). "Lab data is determined by loading a web page in a controlled environment with a predefined set of network and device conditions." and "The controlled environment of the lab is useful when debugging issues or testing features before deploying to production" (step 1); "it is not just one number, it's a distribution of numbers" and "it reflects the actual devices, network conditions, and geographic locations of your users" (step 2); "While this value is greater than the 1.8 seconds shown in the field data, it's still a valid LCP value for this page—it's one of many values that make up the full distribution of load experiences."; "Sites with well-optimized cache configurations and lots of repeat visitors may discover that their real-world LCP is much faster than their lab data indicates." and "when you control for these factors you are explicitly not representing the variance that you see in the real world across all types of networks, device capabilities, or geographic locations."; "88% of visits saw an LCP of 2.5 seconds or less (good)." and "4% of visits saw an LCP greater than 4 seconds (poor)." (the LCP bands in t1). - GoogleChrome, web-vitals README (the row's first and fifth sources), https://github.com/GoogleChrome/web-vitals (
web-vitals-readme.txt). "The sendToAnalytics() function uses the navigator.sendBeacon() method, which is widely available across browsers, and supports sending data as the page is being unloaded." (step 3); "callback is always called when the page's visibility state changes to hidden"; "INP is not reported if the user never interacts with the page."; "when importing from the attribution build, the metric objects will contain an additional attribution property." (step 5). - web.dev, Interaction to Next Paint (INP) (the row's fourth source), https://web.dev/articles/inp (
webdev-inp.txt). "a good threshold to measure is the 75th percentile of page loads recorded in the field, segmented across mobile and desktop devices" (step 6); "Good INP values are 200 milliseconds or less. Poor values are greater than 500 milliseconds.". - MDN, Source map (glossary; the row's sixth source), https://developer.mozilla.org/en-US/docs/Glossary/Source_map (
mdn-source-map.txt). "A source map is a JSON file format that maps between minified or transformed code received by the browser and its original unmodified form" (step 8); "Browsers detect a source map via the SourceMap HTTP header for a resource, or a sourceMappingURL annotation in the generated code.". - W3C, Trace Context (Recommendation; the row's second source), https://www.w3.org/TR/trace-context/ (
w3c-trace-context.txt). "in single page browser applications, it is typical to make cross-origin requests. If one of these code paths leads to traceparent and tracestate headers being sent by cross-origin calls that are restricted using Access-Control-Allow-Headers [FETCH], it may fail." (step 11); "Tracing vendors MUST NOT use traceparent and tracestate fields for any personally identifiable or otherwise sensitive information." (step 10).
Related lessons
Checked against the tools and docs as of 2026-10-10.