Autocomplete & Typeahead
debt(d7/e5/b3/t5)
Closest to 'only careful code review or runtime testing' (d7) — chrome-devtools network panel and laravel-debugbar can reveal excessive requests and slow LIKE queries, but no automated linter flags missing debounce or wrong index strategy; requires manual observation.
Closest to 'touches multiple files / significant refactor in one component' (e5) — quick_fix involves frontend debounce + AbortController plus backend full-text index migration; spans JS and PHP/DB layers, not a single-line swap.
Closest to 'localised tax' (b3) — autocomplete is typically one feature/component (search box + endpoint); applies_to is web-only and doesn't shape the whole system.
Closest to 'notable trap most devs eventually learn' (t5) — misconception about LIKE 'term%' working at scale is a documented gotcha; works fine in dev, fails under production load with larger tables.
Also Known As
TL;DR
Explanation
Autocomplete strategies: prefix matching (startsWith), edge n-gram tokenisation (index every prefix), and completion suggesters (Elasticsearch's specialised structure). Performance: debounce input (wait 200-300ms after last keystroke before querying), cache recent queries, limit result count (5-10), and use a search engine not LIKE 'term%' SQL for large datasets. UX considerations: keyboard navigation, screen reader announcements (aria-live), and not autocompleting too aggressively.
Diagram
flowchart LR
USER[User types ph] --> DEBOUNCE[Debounce 150ms]
DEBOUNCE --> CACHE{Browser cache<br/>for this prefix?}
CACHE -->|hit| SHOW[Show suggestions instantly]
CACHE -->|miss| API[GET /autocomplete?q=ph]
API --> TRIE2[Server-side Trie<br/>or search engine prefix]
TRIE2 --> RANK[Rank by frequency<br/>personalisation]
RANK --> RESP[Top 5-10 suggestions<br/>highlight matched prefix]
RESP --> CACHE
RESP --> SHOW
style SHOW fill:#238636,color:#fff
style RANK fill:#1f6feb,color:#fff
style DEBOUNCE fill:#d29922,color:#fff
Common Misconception
Why It Matters
Common Mistakes
- No debounce — a query fires on every keystroke, overwhelming the server for fast typists.
- Not caching autocomplete results — the same prefix is often typed repeatedly; cache aggressively.
- Showing too many suggestions — 10+ results require scrolling; 5-7 is the usability sweet spot.
- Not handling 'no results' gracefully — showing an empty dropdown is confusing; hide it or show a fallback.
Code Examples
// No debounce — fires on every keystroke:
document.getElementById('search').addEventListener('input', async (e) => {
const results = await fetch('/api/suggest?q=' + e.target.value);
// 'P' → 'PH' → 'PHP' → 3 requests in 100ms, all potentially in-flight
});
// Debounced + cached:
const cache = new Map();
let timer;
document.getElementById('search').addEventListener('input', (e) => {
clearTimeout(timer);
const q = e.target.value.trim();
if (q.length < 2) return hideSuggestions();
timer = setTimeout(async () => {
if (cache.has(q)) return showSuggestions(cache.get(q));
const results = await fetch('/api/suggest?q=' + encodeURIComponent(q)).then(r => r.json());
cache.set(q, results); // Cache for session
showSuggestions(results);
}, 250); // 250ms debounce
});