← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Autocomplete & Typeahead

Search PHP 5.0+ Intermediate
debt(d7/e5/b3/t5)
d7 Detectability Operational debt — how invisible misuse is to your safety net

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.

e5 Effort Remediation debt — work required to fix once spotted

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.

b3 Burden Structural debt — long-term weight of choosing wrong

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.

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

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.

About DEBT scoring →

Also Known As

typeahead search suggestions prefix search

TL;DR

Showing relevant suggestions as the user types — requiring fast prefix search, debouncing, and careful UX to be helpful rather than distracting.

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

Autocomplete can use LIKE 'term%' SQL — LIKE prefix searches work for small tables; at scale, edge n-gram indexes or dedicated completion structures are required for sub-10ms response.

Why It Matters

Autocomplete is the most user-visible search feature — slow or irrelevant suggestions destroy the search experience; proper implementation (debouncing + correct indexing) makes it instant.

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

✗ Vulnerable
// 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
});
✓ Fixed
// 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
});

Added 15 Mar 2026
Edited 22 Mar 2026
Views 64
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 1 ping S 2 pings S 1 ping M 1 ping T 1 ping W 0 pings T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 1 ping W 0 pings T 0 pings F 0 pings S 1 ping S 1 ping M 1 ping T 0 pings W
No pings yet today
SEMrush 1
Amazonbot 20 Google 7 Perplexity 7 Scrapy 6 Ahrefs 4 ChatGPT 3 SEMrush 3 Unknown AI 2 Claude 1 Majestic 1 Meta AI 1 PetalBot 1
crawler 53 crawler_json 3
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: Medium
⚡ Quick Fix
Debounce input by 300ms, use AbortController to cancel previous requests, and show a loading state — the PHP search endpoint should return results in <100ms using a full-text index
📦 Applies To
PHP 5.0+ web
🔗 Prerequisites
🔍 Detection Hints
Search request on every keypress without debounce; no abort of previous requests; LIKE %term% query without full-text index
Auto-detectable: ✗ No laravel-debugbar chrome-devtools
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: High ✗ Manual fix Fix: Medium Context: File Tests: Update


✓ schema.org compliant