FSD Frontend System Design

Part III β€” The Speed Β· Lesson 7.2

Telemetry

In one lineCapture enough context to debug, sample enough to afford it, and scrub everything personal.

Where you've seen itA Sentry issue that reproduces a bug from the breadcrumb trail alone β€” route, three clicks, failed request, exception.

  • Time16 min
  • DiagramEvent pipeline from browser to dashboard, with the scrub step
  • Chapter7 Β· Logging & Monitoring

Diagram

diagram
THE PIPELINE β€” and the step people forget

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Browser │──►│ SCRUB  │──►│ SAMPLE │──►│ BATCH  │──►│ Backend β”‚
  β”‚ capture β”‚   β”‚  PII   β”‚   β”‚        β”‚   β”‚ + send β”‚   β”‚ + dash  β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    β–²                          β”‚
        never send: email, phone,       sendBeacon on
        card, address, auth token,      visibilitychange
        full URLs with tokens           (survives unload)
diagram
WHAT AN ERROR EVENT MUST CARRY

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ error     message + stack (minified β†’ mapped, 7.4)   β”‚
  β”‚ release   build id / git sha    ← "what shipped?"    β”‚
  β”‚ route     /checkout/payment     ← not the full URL   β”‚
  β”‚ user      hashed id only        ← never the email    β”‚
  β”‚ device    browser, OS, viewport, connection type     β”‚
  β”‚ breadcrumbs  last ~20 actions: clicks, routes,       β”‚
  β”‚              fetches, console warnings               β”‚
  β”‚ tags      feature flags, A/B variant (4.4), locale   β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

  Without breadcrumbs you get "it broke". With them you get "it broke
  after the third filter change while the token was refreshing".
diagram
SAMPLING β€” you cannot afford 100% of everything

  errors          100%     rare and high value
  traces          10%      enough for latency distribution
  session replay   1%      + 100% of sessions that errored
  custom logs      1–5%    + always keep the failing path

  Rule: sample the boring, keep all of the broken.

How it works

  1. Capture globally: window.onerror, unhandledrejection, failed fetch wrappers, and framework error boundaries. The promise handler is the one most often missing.
  2. Scrub before sending, not on the server β€” data you never transmit can never leak. Strip query strings, mask form values, hash user ids.
  3. Sample by category, keeping all errors and a slice of everything else.
  4. Batch and send with sendBeacon on visibilitychange, so events survive the user closing the tab.
  5. Fail silently. Telemetry that throws, blocks rendering, or retries aggressively is worse than no telemetry.
js
window.addEventListener('unhandledrejection', (e) => {
  report({
    type: 'unhandled_rejection',
    message: String(e.reason?.message ?? e.reason).slice(0, 500),
    stack: e.reason?.stack,
    route: location.pathname,          // path only β€” no query string
    release: __BUILD_ID__,
    userHash: hashedUserId,            // never the raw identifier
  });
});

Trade-offs

Capture moreCapture less
Faster root causeLower cost and compliance risk
Better breadcrumbsLess PII exposure (3.8)
Bigger bills, noisier dataBlind spots during incidents

Own-the-boundary rule: telemetry SDKs are third-party scripts with full origin access (3.7). Load them async, pin versions, and make sure a vendor outage cannot break your app.

Interview angle

"What do you log from the frontend, and what do you never log?"

  • Always: error and stack, release id, route, device and connection, breadcrumbs, and feature-flag or experiment tags.
  • Never: emails, phone numbers, payment data, auth tokens, or full URLs that may carry tokens in the query string.
  • Scrub on the client before transmission, since server-side scrubbing still means the data left the device.

Recap

  • Context is what makes an error actionable β€” breadcrumbs and release id above all.
  • Scrub on the client, sample by category, send with sendBeacon.
  • Keep 100% of errors; sample everything that's merely interesting.