Bundle analyzer → what's in the bundle, dup deps, heavy lib to swap
PerformanceObserver → raw entries (the API under RUM)
web-vitals (Google/Chrome) → the metric, computed to match CrUX. onLCP/onINP/onCLS + web-vitals/attribution for the why
RUM vendors to name
Free / Google field → CrUX (PSI, CrUX API, BigQuery, CrUX Dashboard) — aggregated, 28-day, not per-session
Perf-native RUM → SpeedCurve (LUX), Akamai mPulse, DebugBear, Calibre, Raygun
APM suites w/ RUM → Datadog, New Relic, Dynatrace, Sentry, Grafana Faro
Roll-your-own → web-vitals→sendBeacon→data lake (or GA4 / Cloudflare Web Analytics / Elastic APM)
Lead one-liners (memorize)
The loop “RUM to find it, lab to explain it. Field says which metric & which users; the flame chart says why. Fix the root cause, verify in the field.”
Systemic “Fix the class of issue, not the page — then a CI budget so it can't recur. Guessing from my laptop is how teams waste a sprint.”
web-vitals “I ship Google's web-vitals, not a hand-rolled observer — so my field numbers match the CrUX numbers I'm graded on, and attribution names the slow element.”