1. Question
A globally accessed single-page app needs real-user performance data to compare experiences across devices, networks, and releases. Collect LCP, resource-load, and long-task data while limiting reports per page session. Design a PerformanceObserver collector and explain how it handles entries created before observer registration, buffer loss, cross-origin resources, and SPA route changes.
2. Constraints and clarifications
- Define the time scope of each metric: initial document, soft navigation, or a whole session.
- Set a sampling rate, per-type entry cap, batch size, and failure-retry policy so collection does not affect the page.
- Distinguish browser support differences from missing data; missing values need a reason and must not become zero.
- Define privacy boundaries for resource URLs, user identifiers, and query dimensions; do not report complete URLs or sensitive parameters directly.
3. Core concepts
PerformanceObserver receives entries for selected entry types; buffered: true can retrieve entries recorded before the observer was created. Resource, long-task, paint, and LCP types have buffer limits, and the callback can receive droppedEntriesCount to reveal entries discarded because a buffer was full. LCP is the render time of the largest image or text block during loading; after user input, later content should not be treated as the initial LCP. Without an appropriate Timing-Allow-Origin response header, timing data for cross-origin resources may be restricted or missing.
4. Reference implementation
startCollector():
session = createSessionId()
observe("largest-contentful-paint", {buffered: true})
observe("resource", {buffered: true})
observe("longtask", {buffered: true})
observe(type, options):
if type not in PerformanceObserver.supportedEntryTypes:
markUnsupported(type)
return
observer = new PerformanceObserver((list, _, dropped) =>
appendSanitized(list.getEntries())
if dropped > 0: markDropped(type, dropped)
flushWhenBatchIsReady()
)
observer.observe({type, ...options})
onSoftNavigation(route):
closePreviousView(route)
resetViewScopedMetrics()Register a separate observer for each entry type and use buffered to retrieve early records. The callback keeps only required fields, strips URL parameters, and sends batches. Each view stores observer-registration time, route, and release; a soft navigation closes the previous view and resets view-scoped metrics. Session-scoped resource or error counts continue with an explicit deduplication key.
5. Data quality and cost trade-offs
Higher sampling stabilizes quantiles but increases network and storage cost. Keep a higher sample for LCP and long tasks, while head-sampling resource entries or retaining only slow resources. A single resource buffer holds up to 250 entries by default, so the collector should consume it during the page lifecycle and record losses instead of endlessly increasing report frequency. Limit client retries after a batch failure and discard stale batches on the next visit so a local queue does not slow startup.
6. Verification and observability
- Create entries before observer registration to verify
buffered; use a resource-heavy page to verifydroppedEntriesCountalerts. - Test cross-origin resources with and without
Timing-Allow-Originand confirm missing-field reasons remain distinguishable. - Replay initial load, soft navigation, background recovery, and page hiding separately; verify LCP and resource entries are not attributed to multiple views.
- Monitor collector CPU, memory, reported bytes, batch-failure rate, entry-loss rate, and support rate.
7. Common mistakes
- Creating observers only after the
loadevent, permanently missing early LCP or resource entries. - Silently ignoring
droppedEntriesCountwhile treating an incomplete sample as a complete distribution. - Treating restricted cross-origin timing as true zero latency and corrupting performance quantiles.
- Creating a new observer on every SPA route without closing the old one, causing duplicate reports and memory leaks.
8. Interview scoring points
Uses the observer lifecycle correctly
The answer should cover buffered, supportedEntryTypes, observer shutdown, and soft-navigation boundaries to avoid early loss and duplicate attribution.
Handles buffer and cross-origin limits
The candidate should monitor droppedEntriesCount, know the resource-buffer cap, and use Timing-Allow-Origin to explain missing cross-origin fields.
Designs controlled sampling and reporting
The candidate should allocate sampling, entry caps, and batching by metric value and explain why retries do not slow the page.
Verifies data trustworthiness
The candidate should cover early entries, resource-heavy pages, cross-origin timing, soft navigation, and background recovery while measuring collector overhead and loss.