[Deep Dive #17] Rendering Budgeting - discussion
We have a few screens that "feel slow" in a way that's hard to pin down: not one expensive call, but a steady stream of small rerenders and derivations.
Do you set explicit rendering budgets per route/surface (rerenders, derived work, fetch count), or do you just optimize when it hurts? What signals do you render to make render cost explainable (render trail, reasons, surface counts)? How do you keep budgeting from becoming "optimize everything" instead of focusing on user-visible pain?
Comments (10)
Back to latestHow do you avoid budgeting turning into a perf police situation? People will just stop touching the code.
We budget per surface, not per component, and we only budget the stuff users feel (keystroke latency, navigation jitter).
The signals are the key: once you can see a render trail, you can make small fixes without blame.
Related: Performance and Rendering Best Practices and Performance Audit Trails (Deep Dive #6).
We keep a cheap render-trail signal per route and roll it up:
ts
type Trail = { surface: string; reason: string; at: number };
function pushTrail(doc: any, e: Trail) {
const prev = (doc.read('trail') as Trail[]) ?? [];
const next = [...prev.slice(-49), e];
doc.write('trail', next);
doc.write('trailAgg', aggregate(next));
}
Then the UI renders trailAgg in a tiny signals bar. It's not perfect, but it gives you a starting point.
Counterpoint: signals can become performance overhead if you're not careful.
We only enable full trails when a route posture key is set (like perf=profile), otherwise we keep it to counts only.
That seems right. I like the idea that profiling is a route posture, not a developer-only mode.
If you can share a URL that reproduces the profiling posture, debugging gets way faster.
Exactly. It also stops the "works on my machine" vibe because the posture is explicit.
We got the most win from budgeting forms. If typing feels bad, the whole app feels bad.
Our budgets are mostly "avoid identity churn".
A lot of React perf problems were just hooks returning unstable objects and triggering rerenders everywhere.
Budgeting also prevented us from optimizing the wrong thing.
If a trail shows 30 rerenders but users don't feel it, we don't burn time. If INP is bad and the trail points to one surface, we fix that first.
We made budgets part of route docs. If a route has a known expensive derivation, the doc should name it and signals should prove it isn't running on every keystroke.
The budget isn't "no rerenders". It's rerenders that are explainable.
If your signals can't tell you why something rerendered, you'll end up cargo-culting memoization. Trails prevent that.