← All posts

How we cut dashboard load time by 70%

A walk through the three changes that took our analytics dashboard from a 2.8s load to under 900ms - pre-aggregation, streaming queries, and shipping less JavaScript.

A speedometer-like abstract gradient suggesting speed

A slow analytics dashboard is its own kind of irony. Last quarter ours took 2.8 seconds to become interactive. Today it’s under 900ms. Three changes did most of the work.

1. Pre-aggregate at write time

We were computing rollups on read - every dashboard load re-scanned raw events. We moved that work to ingestion, maintaining hourly and daily rollups as events arrive:

INSERT INTO metrics_daily (day, metric, value)
SELECT date_trunc('day', ts), metric, count(*)
FROM events
WHERE ts >= now() - interval '1 hour'
GROUP BY 1, 2
ON CONFLICT (day, metric) DO UPDATE
SET value = metrics_daily.value + excluded.value;

Reads got dramatically cheaper because the heavy lifting already happened.

2. Stream results instead of blocking

The old dashboard waited for every query before rendering anything. Now the shell renders instantly and each chart streams in as its query resolves. Perceived performance improved even more than the raw numbers.

3. Ship less JavaScript

This was the biggest surprise. We were hydrating the entire page as one app. By splitting the interactive pieces into isolated components and leaving everything else as static HTML, we cut the JS bundle by more than half.

The fastest code is the code you never send to the browser.

The takeaway

None of these are exotic. Move work to write time, render progressively, and don’t ship JavaScript you don’t need. Most “slow” dashboards are slow for boring, fixable reasons.

← All posts