Skip to main content

Browser, Networking, and Runtime

(Understanding what actually happens after the user clicks)

Senior rule:

If you cannot trace the browser and network path, you cannot explain the bug.

Frameworks hide details until they leak. Senior engineers know enough browser and network behavior to diagnose leaks quickly.

Conceptual Model

When a user changes an Atlas report filter:

  1. input event enters the main thread
  2. React state updates
  3. URL state may change
  4. old request may still be in flight
  5. new request starts
  6. response parsing happens
  7. table and chart update
  8. layout, paint, and compositing produce pixels
  9. telemetry records success or failure

Every step can become the bottleneck.

Event Loop and Main Thread

JavaScript runs on the main thread, but browser work happens around it. Long JavaScript tasks delay input, rendering, timers, and hydration.

export function chunkWork<T>(
items: T[],
process: (item: T) => void,
chunkSize = 100
) {
let index = 0;

function runChunk() {
const end = Math.min(index + chunkSize, items.length);
while (index < end) process(items[index++]);
if (index < items.length) window.setTimeout(runChunk, 0);
}

runChunk();
}

This is not a universal pattern, but it shows the principle: yield to the browser when work is too large.

Request Waterfalls

Bad Atlas route:

// Slow: each component starts after its parent renders.
export default async function DashboardPage() {
const user = await getUser();
const permissions = await getPermissions(user.id);
const summary = await getSummary(permissions.accountId);
return <Dashboard summary={summary} />;
}

Better route:

export default async function DashboardPage() {
const userPromise = getUser();
const summaryPromise = getSummaryForCurrentSession();

const [user, summary] = await Promise.all([userPromise, summaryPromise]);

return <Dashboard user={user} summary={summary} />;
}

Parallelize independent work. Keep dependent work explicit.

Stale Request Handling

Rapid filter changes can cause stale results to overwrite fresh ones.

"use client";

import { useRef, useState } from "react";

type Report = { rows: Array<{ id: string; revenue: number }> };

export function useReportSearch() {
const sequence = useRef(0);
const [report, setReport] = useState<Report | null>(null);
const [isLoading, setIsLoading] = useState(false);

async function search(query: string) {
const requestId = ++sequence.current;
setIsLoading(true);

const response = await fetch(`/api/reports?query=${encodeURIComponent(query)}`);
const nextReport = (await response.json()) as Report;

if (requestId === sequence.current) {
setReport(nextReport);
setIsLoading(false);
}
}

return { report, isLoading, search };
}

Alternative: use AbortController when you own the request lifecycle.

const controller = new AbortController();

fetch("/api/reports", { signal: controller.signal });
controller.abort();

API State Design

Avoid a boolean-only loading model.

type AsyncState<T> =
| { status: "idle" }
| { status: "loading"; staleData?: T }
| { status: "success"; data: T }
| { status: "empty" }
| { status: "error"; message: string; staleData?: T };

This lets Atlas show stale data while refreshing instead of blanking the dashboard.

Device and Network Conditions

Senior engineers test under constraints:

  • slow CPU
  • high latency
  • packet loss or offline
  • small viewport
  • touch input
  • low memory
  • Safari/WebKit differences

The question is not "Does it work on my machine?" The question is "Which users pay the highest cost?"

Mini Case Study: Slow Merchant Report

Atlas merchants complained that a report sometimes took 8 seconds. The backend was blamed first. DevTools showed three serial requests: session, permissions, report metadata, then report data.

Fix:

  • move session and permission resolution server-side
  • fetch metadata and data in parallel where possible
  • cache report metadata
  • show stale report data during refresh
  • add telemetry for request waterfall depth

Outcome: the page still depended on the backend, but it stopped adding avoidable frontend latency.

Common Failure Modes

  • Serial API calls hidden across components.
  • Missing request cancellation or stale response guards.
  • Treating loading as a single boolean.
  • Blocking input with large synchronous transformations.
  • Testing only on fast desktop hardware.
  • Ignoring browser differences until a production complaint.

Review Checklist

  • Can you draw the request sequence?
  • Are independent requests parallelized?
  • Can stale responses overwrite fresh UI?
  • Are loading, stale, empty, and error states distinct?
  • Is CPU-heavy work chunked, deferred, or moved?
  • Was the flow tested on slow network and slow CPU?

Senior Deep Dive: Runtime Consequence Mapping

Senior frontend work begins when you can trace a feature through the browser runtime. A component is only the authoring shape. The runtime shape includes event handling, task scheduling, style calculation, layout, paint, network queues, cache behavior, hydration, memory, and stale async work.

Click-to-paint timeline

For any important interaction, map the path:

StepWhat can go wrongSenior control
Inputevent handler does too much sync workkeep handlers short, defer noncritical work
State updateone local change invalidates a large treelocalize state, use selectors, split boundaries
Data requestrequest starts late or duplicates anotherroute-level data ownership, dedupe, prefetch by intent
Async raceold response overwrites newer stateabort, sequence, ignore stale result
Renderexpensive subtree commits repeatedlyprofile, isolate, virtualize
Layout/paintDOM size or measurements force layoutreserve dimensions, avoid layout thrashing
Side effectsanalytics/storage/third-party work blocks userschedule after user-visible work

The weakness this corrects: junior and mid-level engineers debug what they see in React DevTools only. Senior engineers debug the browser's full pipeline.

Request waterfall diagnosis

A request waterfall is not just "many requests." It is dependency ordering. Ask:

  • Which request cannot start until HTML arrives?
  • Which request cannot start until JavaScript executes?
  • Which request waits for user state that could have been available earlier?
  • Which component discovers its own data instead of the route owning the dependency?
  • Which third-party origin adds DNS/TLS setup to the critical path?
  • Which request is duplicated by server render and client hydration?

Waterfalls often come from architecture, not network speed. Fixing them may require moving data dependencies up to the route, adding a BFF, preloading only high-confidence resources, or changing rendering strategy.

Async correctness model

Every asynchronous UI flow needs a state machine, even if it is not implemented as one:

StateRequired UI behavior
idleuser can start the action
pendinguser sees progress and duplicate submits are controlled
successUI reflects the authoritative result
emptyabsence of data is explained
partialavailable data remains useful
staleuser knows data may not be current
faileduser can recover, retry, or safely abandon
cancelledold work cannot overwrite newer intent

A senior engineer watches for impossible states: loading and success at once, old filters with new results, disabled forms with no recovery, or "saved" UI before the server accepted the change.

Memory and lifecycle discipline

Runtime mastery also includes cleanup:

  • abort fetches on navigation or superseded queries
  • unsubscribe from sockets, stores, observers, and timers
  • bound in-memory caches
  • avoid retaining large API responses in closures
  • clear sensitive client state on logout
  • test long-lived tabs, not only fresh reloads

Many production frontend bugs appear after twenty minutes of use, not on first load.

Exercises

Exercise 1 - Click Trace

Pick one user action and write every step from input event to painted result.

Exercise 2 - Waterfall Audit

Open a route in DevTools Network and identify:

  • serial requests
  • duplicate requests
  • cache misses
  • oversized payloads

Exercise 3 - Stale Response Drill

Create a rapid filter-change test. Verify old responses cannot overwrite new results.

Further Reading