01 — Background
Search was our highest-intent moment. We were making it harder than it needed to be.
By 2024, Pathao Food served 500,000+ users across Bangladesh and Nepal. For most of them, the search bar was their primary navigation tool — not category tiles, not the homepage carousel. When someone was hungry and knew roughly what they wanted, they typed. That intent moment was the most valuable interaction in the entire product.
But the search experience at that point was entirely reactive. A user typed their full query, hit search, and got results. No guidance along the way. No hints about what was available. No nudge toward a popular variant of what they were looking for. You were on your own until you'd committed to a full search term.
For a platform operating across two countries with thousands of restaurants, varied menu naming conventions, and users typing in both English and Banglish, this meant a significant share of searches ended in either zero results, irrelevant results, or abandoned sessions — not because the food wasn't available, but because the connection between what the user typed and what was in our catalogue was never made.
The Friction
Full-commit search with no guidance
Users had to type a complete query before receiving any feedback. A slight misspelling, a Banglish variation, or an unfamiliar restaurant name meant zero results with no recovery path. The search bar offered no signal about what the platform actually contained.
The Opportunity
Real-time guidance at the highest-intent moment
Suggesting relevant options as the user types transforms search from a guessing game into a guided experience. The user gets faster to what they want. Drop-offs from failed searches decrease. And the platform gets to surface popular, high-converting options at exactly the right moment.
The Context
Part of a broader search renovation
This feature was designed alongside the Search Prioritisation of Paid Restaurants work. Together they formed the two sides of Pathao Food's search renovation: one improving the commercial layer, this one improving the user experience layer. Both depended on the same underlying data infrastructure.
02 — Problem
Three failure modes, all happening in the same search bar.
Before starting on solution design, I mapped out where search was actually losing users. There were three distinct failure modes, each hitting a different type of user for a different reason.
The core question
How do you guide users to the right result before they've even finished typing — without surfacing irrelevant noise and without making the experience feel cluttered? The suggestion list had to earn its place in the UI on every keystroke, or users would learn to ignore it.
Failure Mode 1
The committed typo
A user types "Kachi" instead of "Kacchi" and gets zero results. They don't know if the dish doesn't exist on the platform or if they just misspelled it. Most don't retry — they abandon. Real-time suggestions using fuzzy matching would catch this before it became a dead end.
Failure Mode 2
The ambiguous intent
A user types "cake" and gets 80 results across restaurants, dishes, and categories. They wanted a specific thing but searched too broadly. Suggestions that guide them toward "Chocolate Cake" or "Birthday Cake" — ranked by what the city is actually ordering — would turn a noisy result page into a directed one.
Failure Mode 3
The unknown catalogue
A user opens the app wanting something but isn't sure what to search for. With no suggestions, they're browsing blind. Real-time suggestions that surface popular cuisines and dishes as they begin typing create a discovery layer that turns partial intent into a concrete choice.
All three failure modes shared one root cause: the user had no visibility into the platform's catalogue while forming their query. The fix wasn't just faster search results — it was making the search bar itself informative from the first keystroke.
03 — Users
Two types of users the feature had to work for simultaneously.
When I scoped the user need, two distinct mental models emerged. Designing for one while ignoring the other would make the feature feel wrong for half the user base.
The Purposeful Searcher
Knows what they want, wants to get there fast
Already has a specific dish or restaurant in mind. For them, the suggestion list is a shortcut — they want to tap a suggestion at 3 characters rather than type the full 12-character restaurant name. Speed and accuracy matter most. A wrong suggestion is a distraction, not a discovery. The "exact typed word first" rule was designed specifically for this user: they can always tap their own partial query and proceed to results without waiting for a perfect suggestion.
The Exploratory Searcher
Knows a vague category, open to suggestions
Types "biry" because they're in the mood for biryani but haven't decided where from. For this user, the suggestion list is a menu of what's popular near them. Popularity-ranked suggestions are the feature. Surfacing "Kacchi Biryani" or "Chicken Biryani" ranked by what the city is ordering heavily guides them toward high-converting choices they'd have been happy with anyway, reducing their decision effort to a single tap.
The design implication: Both users are served by the same feature, but they use it in opposite ways. The purposeful searcher needs suggestions to be fast and non-intrusive. The exploratory searcher needs suggestions to be informative and ranked by quality. The solution — always showing the exact typed word first, then ranked suggestions below — satisfies both simultaneously without compromising either.
04 — My Approach
Four decisions that shaped everything downstream.
Before writing a single acceptance criterion, I made four foundational product decisions that constrained the solution space and ensured the feature would hold up under real usage conditions.
1
Three characters before anything appears
I chose a 3-character minimum threshold rather than 1 or 2 for two reasons. At 1–2 characters, the suggestion space is so broad that results are noise — every restaurant, dish, and cuisine in the catalogue potentially matches. At 3 characters, we have enough signal to return meaningful, specific suggestions. It also avoids the performance cost of firing a suggestion API on every single keypress, which on mobile networks in Bangladesh would have introduced visible latency.
2
Always show the exact typed word as the first result
Regardless of how good the suggestions below are, the user's own input always appears first. This means a user who knows exactly what they want can tap the first item and proceed without reading the list. It also means the suggestion list never "hijacks" intent — the user is always one tap away from searching exactly what they typed. This was a trust decision as much as a usability one.
3
Suggestions ranked by type: cuisine first, then dish, then restaurant
When a query could match across all three types, the order matters. I prioritised cuisine first because it has the broadest redirect value — a cuisine suggestion leads users to a curated collection of relevant restaurants, which is typically a better discovery experience than jumping directly to a specific restaurant. Dishes come second because they represent specific intent. Restaurants come last because name-matching is already handled well by the existing search result ranking.
4
Phase the complexity: ship matching first, add popularity ranking second
Rather than blocking launch on the full popularity-scoring algorithm, I split the work into two phases. Phase 1 would ship prefix and fuzzy matching with a basic ranking order. Phase 2 would layer in the DS-backed popularity index. This let us get user value into production quickly while the data science team built the more complex scoring model. Each phase had a clear scope gate so the team knew exactly what constituted "done" at each stage.
05 — Suggestion Design
What the user sees — and exactly what each suggestion does.
The suggestion list has a fixed structure. After the user types 3 or more characters, results appear and update in real time with each additional keystroke. Every item in the list has a defined type and a defined action when tapped. I specified both precisely to eliminate ambiguity for the engineering team and to ensure consistent behaviour across all input variations.
User types "bir" → suggestions appear
🔍
bir
Exact
bir → search results for "bir"
Always first
Cuisine
Biryani → cuisine results
High city popularity
Dish
Kacchi Biryani → item results
High order rate
Dish
Chicken Biryani → item results
High order rate
Restaurant
Biryani Express → restaurant page
Prefix match
Suggestion type priority order
Within the suggestion list, items are ordered by type first (cuisine before dish before restaurant), then by popularity within each type. Here's the full hierarchy I defined:
1
Exact Typed WordThe user's raw input, always shown first regardless of match quality. Tapping takes the user to search results for their exact query — default tab is Restaurant.
Always P1
2
Cuisine SuggestionsMatched by prefix or fuzzy on cuisine name. Ranked by city-level popularity. Tapping redirects to cuisine-filtered search results.
Prefix / Fuzzy
3
Dish SuggestionsMatched by name prefix or fuzzy. Dishes must be frequently ordered across the platform. Popularity ranked city-wise. Tapping opens item-tab search results.
City-wise pop.
4
Restaurant SuggestionsMatched by name prefix or fuzzy. Ranked first by prefix/fuzzy match quality, then by popularity. Tapping opens that restaurant's page directly.
Popularity
What happens when a user taps a suggestion
I was explicit in the spec about what "tapping" means for each suggestion type, because the destination is different depending on what was tapped — and getting this wrong would send users to the wrong place, undermining the whole value of the feature.
Tapping the exact typed word: Performs a search with the user's raw query. Lands on the Restaurant tab of the search results page by default. Equivalent to pressing the search button — just faster to reach.
Tapping a cuisine suggestion: Performs a search filtered to that cuisine, showing all restaurants and dishes that match. Broader intent, broader result set.
Tapping a dish suggestion: Performs a search on the Item tab, filtered to that dish name. Shows all restaurants offering that dish across the user's area. High intent, narrow result set.
Tapping a restaurant suggestion: Opens that restaurant's menu page directly. The user bypasses the search results page entirely and lands in the restaurant they selected. Fastest path for a user who knows exactly where they want to order.
06 — Ranking & Popularity Algorithm
Making the list smarter: from match-based to popularity-weighted.
Phase 1 suggestions were ordered by match type (prefix before fuzzy) and then alphabetically within each type. This was good enough for the initial launch but left value on the table: alphabetical ranking has nothing to do with what a user actually wants. "Aarong Biryani" ranking above "Kacchi Biryani" because of the alphabet is not useful to anyone.
Phase 2 replaced this with a city-wise popularity index that I specified in collaboration with our data science and backend teams. The index was designed to surface what users in a given city are actually engaging with — not globally, but locally, because food preferences and ordering patterns vary significantly between Dhaka, Chittagong, and Kathmandu.
Restaurant Popularity Signals
Four inputs to rank restaurant suggestions
Average Rating — proxy for consistent food quality
Total Order Count — reveals sustained real-world demand
Conversion Rate — % of restaurant page views leading to an order
Checkout Rate — % of users who add to cart and complete the order
Dish Popularity Signals
Two inputs to rank dish suggestions
Conversion Rate — how often a dish tile view leads to an order
Checkout Rate — from food tile click to successful checkout
Dishes rely on conversion signals rather than ratings because dish-level ratings are sparse and often unreliable. Conversion and checkout rates are objective, high-volume signals that directly measure user intent completion.
Why conversion rate, not just order count? Order count alone is biased toward older, more established restaurants that have had more time to accumulate orders. Conversion rate normalises for recency — a newer restaurant with a high conversion rate is genuinely compelling to users right now, and should rank above an older one with a higher raw order count but declining engagement. I pushed for conversion rate as a mandatory signal in the index precisely to avoid the "incumbency advantage" problem.
Key guardrails I built into the ranking design
The exact typed word is always P1, immovable: No matter how popular a suggestion is, the user's own input always appears first. The popularity ranking only applies within the typed-ahead suggestions below it, never displacing the exact-input result.
Suggestions re-rank in real time after 3 characters: Each additional character the user types refines the match pool, and the popularity index re-applies to the narrowed pool. A longer input narrows the field; popularity determines the order within that narrowed field.
Popularity data is city-specific, not global: The index is computed and refreshed per city, not as a platform-wide aggregate. "Popular" in Dhaka is not the same as "popular" in Kathmandu. A Dhaka user should see Dhaka's ordering preferences, not Bangladesh-plus-Nepal combined.
Fallback to match-based sorting if popularity data fails: If the popularity API returns an error or has no data for a given entity, the system gracefully falls back to Phase 1 match-based sorting. No broken suggestion lists, no crashes — just a slightly less curated experience until the data is available again.
07 — Edge Cases & Hard Decisions
The decisions that only reveal themselves when you think carefully about what can go wrong.
Edge case handling is where a lot of search features quietly break in production. I worked through these explicitly during spec-writing, because ambiguity here turns into engineering bugs or poor user experiences that are hard to trace back to their root cause.
Edge Case 1
Restaurant is out of the user's delivery radius
A restaurant suggestion might be a legitimate prefix match but outside the user's current delivery area. For Phase 1, discovery radius checks were deliberately excluded from the suggestion layer — adding radius filtering to every real-time suggestion query would have significantly increased latency and complexity. If an out-of-radius restaurant is tapped, the system shows results for similar restaurants based on the tapped restaurant's cuisine and dishes. The user gets a useful redirect, not a dead end.
Edge Case 2
Zero suggestions returned for a valid input
Sometimes a 3+ character input genuinely matches nothing in the catalogue — a new restaurant name, a regional dish variant, or a Banglish spelling the system hasn't seen. Rather than showing a blank suggestion list (which feels broken), the system shows two specific fallback options: "Show results for [input] in Restaurants" and "Show results for [input] in Items." The user always has an escape route that makes sense.
Edge Case 3
Case sensitivity and Banglish input
Input matching is fully case-insensitive — "Biryani," "biryani," and "BIRYANI" are treated identically. Banglish input (Bengali words typed in English letters) required specific handling in the fuzzy matching layer, since there's no single canonical Banglish spelling for most Bengali food terms. Fuzzy matching was scoped to absorb the most common variants without generating false positives for completely unrelated queries.
Why paid restaurant prioritisation was excluded from suggestions in Phase 1: The Search Prioritisation of Paid Restaurants feature (Signature and Select tiers) was being designed in parallel. I made the deliberate call to keep paid keyword ranking out of the suggestion layer for Phase 1. Auto-suggestion fires on partial, unresolved queries — applying commercial ranking to a 3-character input before we know what the user actually intends felt like the wrong place to introduce commercial bias. Phase 2 would revisit this with cleaner intent signals in place.
08 — Phasing Strategy
Ship user value fast. Add sophistication in the next sprint.
I split the feature into two clearly scoped phases. The decision wasn't just a delivery convenience — it reflected a genuine product sequencing choice: get the matching infrastructure into users' hands first, validate that suggestions were being used and were driving positive outcomes, then layer in the complexity of popularity ranking only after we'd confirmed the core mechanic worked.
| Dimension |
Phase 1 — Match First |
Phase 2 — Popularity Ranked |
| Suggestion trigger |
3+ characters, real-time as user types |
Same, plus re-ranking applied after match pool is assembled |
| Matching logic |
Prefix and fuzzy match on name |
Same prefix and fuzzy matching, unchanged |
| Ranking within type |
Match quality (prefix before fuzzy), then alphabetical |
City-wise popularity index (rating, order count, CVR, checkout rate) |
| Radius filtering |
Not applied - excluded for performance |
Not applied - decision deferred pending latency review |
| Paid keyword ranking |
Not applied - intentionally excluded |
Not applied - kept out of suggestion layer by design |
| Fallback on no results |
Two redirect options: Restaurant tab / Item tab |
Same, plus graceful fallback to Phase 1 if popularity API fails |
| Data dependency |
Search index only |
DS-managed popularity index, city-wise, periodically refreshed |
Why this phasing was the right call: The risk of shipping Phase 1 and 2 together was that a bug in the popularity scoring system could take down the entire suggestion feature. By decoupling them, Phase 1 could be independently stable. If Phase 2 broke, it failed gracefully back to Phase 1 behaviour. Users always had suggestions; the question was just how well-ranked they were.
09 — Go-to-Market
A feature that only works if users discover it naturally.
Auto-suggestion is a feature that should require zero onboarding. If a user has to be taught that suggestions appear when they type, the feature has already failed. My GTM approach reflected this: no tutorial, no tooltip, no splash screen. The feature earns its place by being genuinely useful the first time a user types 3 characters into the search bar.
01
Internal QA & Staging Validation
End-to-end testing of all suggestion types across the canonical edge cases: the typo scenario, the zero-results scenario, the out-of-radius restaurant tap, case sensitivity, and the minimum 3-character threshold. QA team validates all paths using the documented acceptance criteria before any production deployment.
02
Phase 1 Production Release
Match-based suggestions shipped to production. No feature flag needed — the feature is additive and has no impact on existing search result behaviour. Analytics events instrumented for suggestion appear rate, tap rate by type (cuisine/dish/restaurant), and search initiation source (suggestion tap vs manual search). Monitor for 2 weeks before Phase 2.
03
Phase 2 — Popularity Layer
DS delivers the city-wise popularity index. BE integrates the scoring API. QA validates fallback behaviour when the popularity API returns errors. A/B comparison of suggestion engagement rates between Phase 1 and Phase 2 cohorts validates that popularity ranking improves tap-through and downstream order conversion before full rollout.
10 — Metrics
What success looks like across the funnel.
I defined metrics at three layers: feature adoption (are users actually using the suggestions?), search quality (are suggestions reducing failed searches?), and business impact (are they converting to orders?). A feature that gets taps but doesn't reduce zero-result searches or improve conversion isn't working — users might be tapping suggestions and still not finding what they want.
Adoption — Primary
Suggestion tap rate
>30%
Target: more than 30% of search sessions that trigger suggestions end with a tap on a suggestion rather than manual search submission.
Quality — Primary
Zero-result search rate
↓ target
Suggestions guide users away from misspelled or ambiguous queries. A measurable drop in zero-result searches validates that the fuzzy matching is doing its job.
Conversion — Primary
Search-to-order CR for suggestion-initiated searches
vs baseline
Sessions where the user tapped a suggestion vs. sessions where they typed a full query manually. The hypothesis: suggestion-started sessions convert better because the intent is more precisely matched.
Phase 2 — Secondary
Tap rate on top-3 suggestions vs. lower positions
Concentration
A healthy popularity ranking concentrates taps on the top 1–3 suggestions. If taps are evenly distributed across all positions, it signals that the popularity ranking isn't adding signal above alphabetical ordering.
Quality — Guard Rail
Fallback rate (no-suggestion triggers)
Monitor
How often does a 3+ character input return zero suggestions and show the fallback redirect options? A high fallback rate indicates catalogue gaps or matching gaps that need DS or ops attention.
Search Behaviour
Avg. query length before suggestion tap
Trending ↓
If suggestions are working, users should be tapping sooner — at 3–5 characters rather than typing 8–12 character full queries. A declining average query length before tap is a proxy for reduced search friction.
11 — Risks
What could go wrong, and how I designed around it.
High
Latency on slow mobile networks makes suggestions feel broken
Auto-suggestions fire on every keystroke after 3 characters. On 3G networks common in parts of Bangladesh, a naive implementation would show stale suggestions lagging behind the user's typing. Mitigated by debouncing the API call (firing only when the user pauses typing for >150ms) and by the 3-character minimum which reduces the frequency of calls on short inputs.
High
Popularity index surfaces stale data
If the city-wise popularity index isn't refreshed frequently enough, the rankings would reflect last month's ordering patterns rather than current ones. Seasonal dishes (e.g. Iftar items) ranking highly outside Ramadan would create a bad experience. Mitigated by specifying a mandatory refresh cadence for the DS pipeline and by the graceful fallback to match-based sorting if the index is unavailable.
High
Fuzzy matching generates irrelevant results
Too-aggressive fuzzy matching would surface restaurants and dishes with no meaningful connection to the user's input. A search for "bir" could fuzzy-match to completely unrelated names. Mitigated by tuning the fuzzy match threshold carefully during QA, and by setting the 3-character minimum which provides enough signal to constrain the fuzzy match pool meaningfully.
Medium
Out-of-radius restaurant taps lead to dead ends
Users tapping an out-of-radius restaurant suggestion and landing on an "unavailable in your area" message would be a frustrating experience. Mitigated by the Phase 1 design decision: any out-of-radius tap redirects to similar restaurants by cuisine and dishes rather than throwing an error. Radius filtering will be revisited in a future phase when the performance cost can be properly assessed.
Medium
Suggestion list clutters the screen on mobile
A long suggestion list on a mobile screen could cover the keyboard and feel overwhelming. Mitigated by capping the list at a small number of visible items, designing the list to scroll within its container rather than pushing page content, and by the type-priority ordering which ensures the most relevant suggestions appear at the top without the user having to scroll far.
Low
Popularity index penalises new restaurants
New restaurants have zero order history and will rank at the bottom of suggestion lists indefinitely. This was a known and accepted trade-off for Phase 2. The fallback to match-based sorting helps here — a new restaurant with a good name prefix match will still appear, just ranked lower. A "new restaurant boost" for recently onboarded partners was flagged as a Phase 3 consideration.
12 — Lessons
What building this taught me about search UX and phased delivery.
01
The first result in a suggestion list is a trust contract, not a design choice
Showing the exact typed word first — always, without exception — was the single most important decision in the feature design. It communicates to the user: "we will never override your intent." Once I framed it that way, it stopped being a debate about whether a popular suggestion should sometimes rank above the user's input, and became a non-negotiable product principle. A suggestion list that occasionally hijacks your query is a suggestion list users will stop trusting.
02
Phasing by complexity, not by feature, gave us a better feedback loop
The temptation was to phase by splitting features: launch cuisine suggestions first, then dish, then restaurant. Instead I phased by complexity: launch all three types with simple ranking (Phase 1), then improve ranking quality for all types simultaneously (Phase 2). This meant every Phase 1 metric measured the feature at its full breadth, making Phase 2 impact cleanly attributable to the ranking improvement rather than confounded by new suggestion types arriving.
03
Fallback behaviour is a feature, not an afterthought
Specifying the fallback states — what happens when there are no matches, what happens when the popularity API fails, what happens when a tapped restaurant is out of radius — took about a quarter of the total specification time. It would have been easy to defer these to engineering judgment. But in a real-time search feature, the failure cases are precisely the moments when user trust is most at risk. A spec that describes only the happy path is a spec that hasn't been thought through.
04
City-level data is not a nice-to-have - it is the feature
When the DS team initially proposed a single global popularity index for simplicity, I pushed back on it. Food preferences in Chittagong are meaningfully different from Dhaka; Kathmandu is entirely different from both. A global index would surface Dhaka's ordering patterns to every user, making suggestions actively misleading for users in other cities. Relevance at the right geographic granularity is not a refinement to add later — it's what makes the feature trustworthy from day one.
05
Debouncing is a product decision, not just a technical optimisation
When latency concerns came up during technical planning, the engineering team's first suggestion was to debounce API calls to reduce server load. My contribution was reframing this: debouncing also makes the feature feel more intentional to the user. A suggestion list that flickers and updates on every single keystroke feels jittery and hard to read. A list that updates when the user pauses feels responsive and considered. The right debounce window is not determined by server load alone — it should be set by what produces the best-feeling user experience.