Shoppers keep clicking search results for things they cannot buy - out of stock, discontinued, or not available where they are. How do you fix that without wrecking relevance?
Separate the three cases first, because they need different treatment. Availability is a per-request fact rather than an index field, so filter at query time from a fast availability source and reserve suppression for what is genuinely delisted.
What the interviewer is scoring
- Does the candidate distinguish temporarily out of stock, permanently delisted, and unavailable to this shopper
- Whether availability is treated as a per-request evaluation rather than a flag baked into the document
- That the volume of stock events is confronted, with propagation on threshold crossings instead of every decrement
- Whether demoting unbuyable items is recognised as starving them of the behavioural signal they need to recover
- Does the answer name what the shopper sees in place of the result, rather than only removing it
Answer
Three different problems arrive wearing the same badge
The complaint is one sentence and it contains three unrelated failures, so the first move is to separate them. Getting this wrong is why teams ship a blanket "hide unavailable products" rule and then discover they have deleted a quarter of their range from search.
A line that is temporarily out of stock is still a product you sell. It has a price, a page, reviews, organic search equity, and quite possibly stock arriving on Thursday. Removing it from results loses the sale you could have made with a back-in-stock notification, and if the shopper searched for it by name and got nothing they conclude you do not stock it at all.
A line that is discontinued or delisted is not a product you sell. It should leave the sellable index, and the interesting question becomes what happens to its URL and its accumulated organic traffic, which is a separate decision from what happens in site search.
A line that is unavailable to this shopper is available to somebody else. It is out of stock in their region, not stocked by their nominated store, not eligible for the delivery method they have chosen, restricted by age or by shipping rules, or not part of their trade or membership assortment. This is the case that defeats naive designs, because there is no single answer to "is this buyable" that can be written into the document.
Availability is a per-request fact, not an index field
That last case forces the architecture. If availability depends on the shopper's region, chosen store, delivery method and assortment, then a boolean in the index is answering a question nobody asked. The workable shape is a split: put in the document the coarse, slowly-changing facts — the product is live, it is sellable in these markets, it belongs to these assortments — and evaluate the volatile, shopper-specific part at query time as a filter or a rescoring step against a fast availability source keyed by product and location.
The cost of that is real and worth stating out loud, because an interviewer is checking whether you know it. You are adding a lookup to the hot query path, and it has to be fast enough not to blow the latency budget for a results page, which usually means an in-memory or near-cache structure rather than a call to the inventory service per result. It also has to fail well: if the availability source is unreachable, showing results and letting the product page correct the shopper is far better than showing nothing.
The middle option, indexing per-region availability flags, works when the number of regions is small and becomes untenable when the unit is a store. At store granularity the flag matrix per product is large and changes constantly, so query-time evaluation stops being a compromise and becomes the only sensible design.
Why the index is stale, and why you should not fix it by indexing harder
Stock movements are the highest-volume events in a retail estate, because every unit sold and every unit received is one. Pushing each of them into a search index as a document update is the reflex that produces a permanently lagging index and an ingest pipeline that consumes more capacity than queries do. Worse, in indexes where an update rewrites the whole document, high-churn attributes on popular products cause repeated rewrites of exactly the documents you most need to be serving.
The fix is to stop treating the exact number as something search needs. Search needs the buyability transition, not the count: propagate an event when a product crosses from available to unavailable or back, or crosses a low-stock threshold, and let the exact quantity live in the availability source that the query-time filter reads. That reduces a firehose to a trickle, and the trickle is small enough to keep genuinely fresh.
Alongside that, the two-cadence pattern is worth naming: incremental updates for the volatile facts and a periodic full rebuild into a new index with an atomic alias swap for correctness, since incremental pipelines drift and a rebuild is the only thing that reliably repairs it. The rebuild cadence is a cost decision. The alias swap is what makes it safe.
The demotion that quietly becomes permanent
Here is the failure mode that separates a strong answer, and it is a feedback loop rather than a bug. Suppose you handle unbuyable items by down-boosting them in ranking, which sounds like the gentle option. Ranking on this site, like most, is partly learned from behaviour: clicks, add-to-basket rate, conversion. A demoted product receives fewer impressions, so it earns fewer clicks and no conversions, so its behavioural features decay towards those of a bad result. When the stock arrives on Thursday and the demotion is lifted, the product returns to a ranking position computed from the evidence collected while it was hidden, and it does not recover. You have taught your model that a temporarily unavailable product is a poor result.
The defences are specific. Compute behavioural signals over windows in which the product was actually available and buyable, so periods of suppression do not count as negative evidence. Keep the availability adjustment as an explicit, inspectable multiplier applied at serve time rather than something baked into learned features. And when a product returns, give it enough exposure to re-earn signal instead of waiting for it to climb, which is the same exploration problem as a newly-listed product and can use the same mechanism.
What the shopper sees instead
Removing a result is not a design; replacing it is. For a temporarily unavailable line the useful behaviours are to keep it visible but clearly labelled with its state and an expected date if you have one, offer notify-me, and surface the in-stock substitute next to it. For a genuinely delisted line, take it out of search and decide the URL separately: redirect to the closest live equivalent or the parent category where one exists, and keep serving a page that explains the situation and offers alternatives where it does not. Returning nothing for a query the shopper spelled correctly is the outcome that most reliably sends them elsewhere.
The judgement call is a query where the best matches are all unavailable. Filtering hard leaves an empty page, which reads as "we do not sell this". Relaxing the filter shows accurate results you cannot fulfil. The defensible middle is to show the matches with their state made obvious, ranked so anything buyable is above anything that is not, and to say plainly at the top that availability is limited.
Measure the thing the shopper experienced
The metric to instrument is the rate at which a search result click lands on something that cannot be added to the basket, split by the three cases. It is the only figure that describes the actual complaint, it can be computed from data you already emit, and it moves the moment the pipeline degrades. Pair it with the lag between a stock transition and its visibility in search, measured as a high percentile rather than an average, because the average is dominated by the quiet hours and the damage happens during peaks.
The three cases are not one problem. Delist what you no longer sell, evaluate shopper-specific availability at query time, and keep out-of-stock lines visible and honestly labelled - because a hidden product earns no signal, and a product with no signal never comes back.
Likely follow-ups
- A line is out of stock in the shopper's region and available for delivery from another. What do you show?
- How would you keep availability filtering from becoming the slowest part of your query?
- The product is discontinued but the page ranks well in organic search. What do you do with the URL?
- What metric would tell you tomorrow that your index has gone stale, before anyone complains?
Related questions
- You have changed the ranking. How do you decide whether search got better, rather than just different?hardAlso on search-relevance6 min
- A maintenance script deletes rows it should not have touched, and nobody notices for six hours. Walk me through the recovery.hardSame kind of round: scenario6 min
- A customer ports their number away to another operator. What has to happen on your side, and what usually goes wrong?hardSame kind of round: scenario6 min
- A customer's API token turns up in a public repository. What do you do in the next hour, and what in your token design decides how bad this is?hardSame kind of round: scenario6 min
- A dependency that normally answers in 80ms starts taking eight seconds. What in your service reacts, and in what order?hardSame kind of round: scenario6 min
- A dividend is declared and half the client's holding is out on loan while part of the rest is pledged as collateral. Who receives the income, who votes, and what does your system show the client?hardSame kind of round: scenario5 min
- An upstream feed resets ten thousand of your prices to a penny and orders start arriving. What should have stopped it, and what do you do with the orders that got through?hardSame kind of round: scenario6 min
- A small percentage of your transaction reports are rejected every day and the team resubmits them the next morning. Is that acceptable?hardSame kind of round: scenario6 min