Learn · Around the Algorithm
budding
Latency Is a Feature
The reader feels time to the first stable, useful answer—not the duration of the last background task.
“Response time to what?”
— Robert B. Miller, Response Time in Man-Computer Conversational Transactions
This is the twenty-first chapter in a book about search from first principles. You will decompose time to first useful result, compare fixed debounce with cancel-and-restart, derive a bounded cadence-aware scheduler, reuse survivor sets only under proved monotone refinement, and keep partiality in the answer value. You will preserve selection by semantic identity, anchor the typing field, shield IME composition, and separate direct manipulation from mixed-initiative help. The next chapter assigns every key to one visible owner.
Completion time is not perceived latency
Suppose two systems both settle after 400 ms.
System A clears the list, shows a spinner, and replaces everything at 400 ms. System B filters an already loaded index in 12 ms, preserves the selected identity, labels slower enrichment as pending, and reconciles it at 400 ms.
Their completion times are equal. Their interaction is not. Define
Slow enrichment belongs to the reader's wait only when no earlier published state is both truthful and useful. The first result must not be fabricated: a stale result carries its generation and freshness, and a partial result carries its settlement marker.
A fixed debounce taxes every reader
A trailing debounce of milliseconds waits for silence before starting work. It reduces redundant computation during bursts, but it adds up to latency to every query, including the first character, a slow single key, and an input whose work was already cheap.
Cancel-and-restart begins immediately. It adds no scheduling delay but may repeat work and cannot assume cancellation stops a worker or network request. The generation gate in Chapter 23 remains the publication rule.
Neither policy dominates universally. Let be admitted cost for query and let typed prefixes arrive at times . Immediate restart can waste at most the work performed before each supersession, bounded by each pass's slice and query lease. Fixed debounce wastes no started work during a burst but adds its timer to the last input before every pass.
The better first rule is simpler:
- the first settled input runs immediately;
- cheap bounded local work runs immediately;
- expensive interruptible work receives a finite slice; and
- timing chooses when to start optional work, never which generation may publish.
Adapt to this field, not an imagined person
Maintain a small bounded window of recent inter-key intervals for the active field. Use a robust quantile rather than a mean that one long pause can drag arbitrarily upward. Clamp the estimate inside product-tested minimum and maximum bounds.
If recent intervals are , a simple policy is
This describes recent cadence in one field. It does not label the person a “slow typist,” infer disability, persist across sessions, or join analytics. The window disappears when the interaction ends.
Sparse input never earns a longer delay. Paste, replacement, dictation-like input, and IME composition do not provide ordinary inter-key evidence. They publish immediately and reset or preserve cadence according to the declared policy.
Dhakal and colleagues observed substantial variation across 136 million keystrokes. That population evidence justifies refusing one universal typist; it does not supply a product constant. Measure the field's own workload and retain direct user control.
Algorithm — bounded cadence-aware scheduling
SCHEDULE-QUERY(EVENT, SESSION)
Input: attributed input EVENT and field-local SESSION
Output: immediate transition plus optional scheduled work
if EVENT is composition update
return PRESERVE-DRAFT-WITHOUT-SEARCH(SESSION)
if EVENT is first input or paste or replacement or composition commit
RESET-CADENCE-IF-NEEDED(SESSION, EVENT)
return START-BOUNDED-WORK-NOW(SESSION)
UPDATE-RECENT-INTERVALS(SESSION, EVENT.time)
delay ← CLAMP(SESSION.minimum, RECENT-QUANTILE(SESSION), SESSION.maximum)
PUBLISH-LOCAL-RESULTS-NOW(SESSION)
return SCHEDULE-OPTIONAL-WORK(SESSION.generation, delay)The scheduled effect carries destination, owner, and generation. When it fires, it revalidates all three. Time does not confer ownership.
Extension is a refinement
For exact substring, prefix, and ordinary subsequence matching, appending a character can only remove candidates:
Therefore the survivors of are a valid input universe for . Filtering them avoids rescanning candidates already proved impossible. If survivor counts are , extending work becomes proportional to the previous rather than the whole corpus each time.
The proof has boundaries:
- backspace widens the relation;
- a middle edit is not an extension;
- changing mode or normalization policy can widen it;
- typo thresholds may make an extension admit a candidate previously too far away; and
- a prior answer produced over a restricted or budget-exhausted universe is not complete evidence about the full corpus.
Cache reuse therefore checks a refinement witness, query-generation ancestry, corpus identity, analyzer identity, and complete settlement. A cache that only stores rows cannot prove any of those facts.
Every partial value prevents promotion
Three failures now share one cure:
- a truncated index artifact cannot be adopted as whole;
- a budget-limited answer cannot be called complete; and
- a restricted candidate-set answer cannot become a full-corpus survivor cache.
Partiality lives in the value because misuse occurs far from the call that created it. The search answer records searched universe, restriction predicate, settlement, corpus revision, and generation. A transformation preserves those fields or returns an explicitly narrower boundary.
This is more than type hygiene. The rows and scores of a restricted answer can look exactly like a complete one. Inspection cannot recover the missing universe.
Preserve semantic identity while results move
Focus, selection, and activation are separate. The typing field can retain DOM focus while an active-descendant identity marks one result. Reranking reconciles rows by stable semantic identity, not array index.
If selected identity survives, it remains selected even when its row moves. If disappears, the policy chooses one named outcome:
- retain latently until the query widens;
- choose a declared semantic neighbor; or
- clear selection.
“Keep row 3 selected” is not a policy; row 3 may now name another document. Scroll occurs on keyboard navigation intent, not because asynchronous insertion changed geometry. Pointer hover does not steal keyboard selection.
The first eligible result may become selected by an explicit initial-selection policy, but moving focus to a chip or changing a filter preserves input focus unless the workflow deliberately transfers it.
Hold the input still
When a search surface grows as results appear, the field containing the caret keeps its viewport position. A bottom-anchored sheet grows upward; a top-anchored surface grows downward. Semantic result order remains relevance-first even when visual layout reverses around an anchor.
Motion explains the change but never delays semantic commitment. A new resize retargets one in-flight transition from the observed visual state rather than stacking animations. Reduced motion projects the same terminal state immediately. Direct-manipulation filtering does not crossfade every keystroke; that shimmer would make responsiveness worse.
Virtualization bounds live row nodes to the viewport window and preserves semantic identity across recycling. Frame work follows measure, derive, mutate. One component reading layout after another has started mutation can still cause global forced layout, so the frame phase has one owner.
Direct manipulation is not mixed initiative
A pause may mean thinking, reading through assistive technology, using switch control, correcting composition, or waiting for the system. Silence is not consent for an interruption.
Adaptive scheduling may decide when already-authorized search work starts. It may not infer that the system should widen scope, rewrite the query, or take an action. Proactive assistance needs a distinct policy with task boundary, uncertainty, expected benefit, interruption cost, explanation, dismissal, authority, and an off switch. Horvitz's mixed-initiative framework belongs at that boundary, not inside a debounce heuristic.
Measure the feature
For cold, warm, and degraded paths, report:
- input-to-local-publication p50/p90/p99;
- time to first stable plausible result;
- completion and enrichment latency;
- work started, canceled, superseded, and actually stopped;
- stale publications attempted and admitted;
- survivor counts per prefix;
- field displacement in CSS pixels;
- DOM nodes, layout reads, and long tasks per keystroke; and
- focus, selection identity, and announcement continuity.
Pair latency with correctness under load. A fast implementation that publishes stale generations or drops IME text fails even if every percentile improves.
Negative results
| Temptation | Failure |
|---|---|
| one fixed trailing debounce | every input pays a delay chosen for someone else |
| infer a persistent typing class | local cadence becomes a sensitive human label |
| treat cancellation as ownership | late work can still publish |
| reuse any previous row set | partial or widening histories silently exclude candidates |
| preserve selected row number | reranking activates a different identity |
| clear content for a spinner | verified useful state disappears unnecessarily |
| animate each keystroke independently | transitions stack or shimmer |
| learn intent from silence | direct manipulation becomes unconsented interruption |
Lessons
- Optimize time to the first stable useful result, not only completion.
- Publish bounded local truth immediately and enrich by generation.
- A fixed debounce delays everyone; recent field-local cadence can schedule optional work without profiling a person.
- Extension reuse is licensed by monotone narrowing and complete prior evidence.
- Partiality travels in the value and prevents promotion to a total answer.
- Preserve result selection by semantic identity, not position.
- The typing field is a fixed point while the result region changes.
- IME composition, paste, sparse input, and assistive workflows alter timing policy.
- Silence schedules nothing beyond the already-authorized search task.
Practice
- Compare the added latency of 200 ms debounce with immediate bounded work for a one-character query followed by a long pause.
- Prove candidate monotonicity for substring extension and give a fuzzy-match policy where it fails.
- Design the complete reuse witness for an extending query.
- Specify a bounded recent-cadence estimator that forgets state on dismissal.
- Trace selection when its identity survives, disappears, then reappears after backspace.
- Audit a bottom-anchored search sheet at reduced motion: which layout invariant remains mandatory?
References
- Robert B. Miller. “Response Time in Man-Computer Conversational Transactions.” AFIPS Fall Joint Computer Conference, 1968.
- Vivek Dhakal, Anna Maria Feit, Per Ola Kristensson, and Antti Oulasvirta. “Observations on Typing from 136 Million Keystrokes.” CHI, 2018.
- W3C Web Accessibility Initiative. “Understanding Guideline 2.2: Enough Time.” WCAG 2.2 supporting guidance.
- Eric Horvitz. “Principles of Mixed-Initiative User Interfaces.” CHI, 1999.