Typeahead & Autocomplete
debt(d9/e5/b5/t9)
Closest to 'silent in production until users hit it' (d9). The misconception — using LIKE '%partial%' — works perfectly in development with small datasets. No compiler, linter, or standard SAST tool flags a LIKE pattern as performance-dangerous. The failure only becomes visible in production when table size grows and query times balloon, directly matching the d9 anchor.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix requires coordinated changes across layers: frontend debouncing logic, backend query rewrite (LIKE 'term%'), adding a database index to the relevant column, and wiring up Redis caching for popular prefixes. This is more than a one-line swap (e1/e3) but does not require cross-cutting architectural rework (e7+).
Closest to 'persistent productivity tax' (b5). The choice of autocomplete implementation strategy (indexed prefix queries, debouncing, caching) affects the search endpoint, frontend keypress handling, and caching layer. It is not a single localised component (b3) but also does not reshape the entire system (b7). Any future change to the search feature must account for these constraints, making it a persistent but bounded tax.
Closest to 'catastrophic trap — the obvious way is always wrong' (t9). The misconception field explicitly states this: LIKE '%partial%' is the intuitive, obvious implementation and is always wrong at scale. It works in development (masking the problem) and fails silently in production, directly matching the t9 anchor of 'the obvious way is always wrong.' The leading wildcard disabling indexes is a well-documented gotcha that nonetheless catches a large majority of developers implementing autocomplete for the first time.
Also Known As
TL;DR
Explanation
Autocomplete has stricter performance requirements than regular search — suggestions must appear within 50–100ms of each keystroke or users stop typing and wait, destroying the experience. Implementation approaches vary by scale: for small datasets (<10,000 items), a PHP endpoint filtering in-memory or using LIKE 'prefix%' queries is adequate. For larger datasets, a dedicated prefix index (MySQL prefix search with an indexed column, PostgreSQL pg_trgm with a GIN index, or Elasticsearch completion suggester) is required. Typo tolerance adds significant complexity — Elasticsearch's completion suggester supports fuzziness; for simple PHP implementations, trigram similarity indexes (pg_trgm) handle one or two character errors. Debouncing requests on the frontend (wait 150–200ms after the last keystroke before querying) reduces server load and prevents out-of-order response races.
Common Misconception
Why It Matters
Common Mistakes
- Using LIKE '%term%' instead of LIKE 'term%' — the leading wildcard disables all indexes.
- Not debouncing keystrokes — firing a request on every keypress creates a thundering herd of requests and race conditions when responses arrive out of order.
- Not caching popular prefix responses — the same two or three character prefixes generate the majority of autocomplete queries; cache them aggressively.
- Returning too many suggestions — 5–8 suggestions is the usable maximum; returning 20 forces scrolling and degrades the experience.
Code Examples
// Full table scan — unusable at scale, SQL injection risk
$results = $pdo->query(
"SELECT name FROM products WHERE name LIKE '%{$q}%' LIMIT 10"
)->fetchAll();
// No index used, leading wildcard kills performance
// Prefix match — uses index, safe
$stmt = $pdo->prepare(
'SELECT id, name FROM products
WHERE name LIKE :prefix
ORDER BY popularity DESC
LIMIT 8'
);
$stmt->execute([':prefix' => $q . '%']); // note: prefix%, not %prefix%
$results = $stmt->fetchAll();
// With Redis cache for hot prefixes
$cacheKey = 'autocomplete:' . strtolower(substr($q, 0, 3));
$cached = $redis->get($cacheKey);
if ($cached) return json_decode($cached);
// ... query and cache with 60s TTL