[Deep Dive #19] Fiber Priority Hacks - discussion
We have a couple routes where typing feels bad even though the raw work isn't huge. It feels like a scheduling/priority issue more than a single slow function.
Do you actually use priority/scheduling techniques in app code (not just as a library detail), or do you treat it as off-limits? What signals do you render to prove a priority decision happened (lane, deferred surface, render budget)? And how do you keep these tweaks from turning into a pile of mysterious one-offs?
Comments (10)
Back to latestI avoid scheduling hacks until I can prove it's scheduling. Otherwise it's usually just identity churn or expensive derived work.
The only way we made this safe was signals + budgets.
If you're going to defer something, you need to prove which surface was deferred and why.
Related: Performance and Rendering Best Practices and Rendering Budgeting (Deep Dive #17).
We used a simple defer-heavy-panel contract and rendered it as a signal so it was testable:
tsx
const doc = useRouteDoc('search');
const deferHeavy = doc.read('deferHeavyPanel') === true;
doc.write('prioritySignal', { deferHeavy, reason: 'typing' });
return (
<>
<SearchBox />
<div data-priority={deferHeavy ? 'defer-heavy' : 'normal'} data-reason="typing" />
{deferHeavy ? <SkeletonPanel /> : <HeavyResultsPanel />}
</>
);Counterpoint: replacing real content with skeletons while typing can feel like the app is broken.
We preferred keeping the panel visible and only reducing derived work (store evidence, avoid recompute) before we started deferring surfaces.
Agree. The skeleton example is mostly to illustrate the contract + signal idea.
In our case the issue is more that a sidebar recomputes expensive derivations on every keypress.
Then I'd fix derivations first. Scheduling should be the last tool, not the first.
The only priority knob I trust is the one I can observe. If you can't render a signal for it, it's going to become folklore.
We treated priority hacks as route posture keys (on/off, reason). That way you can A/B in QA without shipping random toggles in component code.
tsx
const route = useRouteState({ lane: 'revenue', batch: 'manual', reason: 'typing' });
useBatchControl({ mode: route.read('batch'), lane: 'priority' });
return <div data-lane={route.read('lane')} data-batch={route.read('batch')} data-reason={route.read('reason')} />;We once fixed typing by deferring updates and accidentally made validation laggy and confusing.
Signals helped us catch it because we could see deferHeavy=true on screens where it shouldn't apply.
If the route doc has a renderBudgetMs key and the UI renders it, you can keep these changes disciplined instead of random.
My fear with scheduling hacks is hidden divergence. If two panels have different rules, signals are the only way to make it explainable.
The boring fix is usually the right fix: stable identities, stored derived evidence, fewer rerenders. Priority is the spice, not the meal.