Design the Grocery Catalog & Search
- Frame the catalog as reconciliation: messy retailer feeds collapse into one product graph with a per-store overlay
- Keep price and stock out of the inverted index and join them at query time
- Defend entity resolution, store-scoped retrieval and ranking under senior follow-ups
Understanding the problem
Design search for the Instacart app. A customer has already picked a store, say a nearby Safeway or Costco, and types a query like milk or organic eggs. Return a ranked, paginated page of products that store actually carries, priced for that store, with likely-in-stock items surfaced first, over a catalog of millions of products spread across tens of thousands of stores.
Instacart is a storefront layered over many separate retailers. When a customer opens the app they pick a specific store. From then on, everything they browse and search has to reflect that store's assortment, prices and promotions, and whether an item is likely on the shelf right now. Behind that simple-looking search box is a catalog stitched together from thousands of retailer product feeds, each formatted differently and none of them fully trustworthy. Interviewers aim the system design round at Instacart's own product surfaces. The multi-retailer catalog with per-store availability is the most reported design, so it leads this guide.
Name the parties this touches. A product manager often co-runs the round and rewards a design that reasons across the marketplace, not just the diagram. The customer types the query and expects relevant, in-stock, correctly-priced results. The retailer supplies the product feed and its store-level prices, and it is a messy, partial source. The shopper who walks the aisles is downstream. Every out-of-stock item that search surfaces sends the shopper on a dead-end hunt and forces a substitution, so ranking has a real fulfillment cost. And the brand is the fourth party, because Instacart sells sponsored placement in search results, which pulls on ranking from the ad side. Hold all four in view as the design takes shape.
Functional requirements
Keep the scope on building the searchable catalog and answering a query against it for one chosen store. Given a store and a text query, return the products that store carries, ranked and paginated, each with that store's price and a stock signal. Understand messy queries, and rank so that relevant, likely-available items rise. The ingestion side is in scope because the catalog does not exist without it. Everything past the result page is a thread the interviewer can pull.
In scope
- Given a store and a text query, return a ranked, paginated list of products that store carries, each with the store's price and promotions
- Understand messy queries: spelling correction, synonyms (pop and soda), and brand-versus-category intent
- Rank by relevance blended with in-stock likelihood, product popularity, and sponsored placement
- Reconcile many retailer product feeds into one canonical catalog, matching duplicate listings to a single product
- Maintain a per-store overlay of price, promotions, and availability, and join it into results at query time
Below the line (name it, then skip)
- The availability prediction model itself (its own design; here we read a probability and boost on it)
- Cart, checkout, and payment settlement (search hands off a product id and a price; the charge is a separate design)
- Shopper batching, dispatch, and in-store routing (the fulfillment side, unrelated to building and searching the catalog)
- Product image storage and delivery (a standard blob store plus CDN; we reference a photo url, we do not serve the bytes)
- Personalized home carousels and recommendations (a recsys surface; here the customer has typed an explicit query)
Non-functional requirements
Reason each target from the product. Latency: typing a query and scrolling results should feel instant, so the budget to retrieve and rank a page sits in the low hundreds of milliseconds at the tail. Read-heavy is the defining skew. Far more people search and browse than the feeds update, so the design optimizes the query path hard and treats ingestion as a background pipeline. Freshness has two speeds. A new product name or a new photo can lag by hours and nobody notices. But a price or a stock change needs to reach search within minutes, because a customer who adds an item at one price and gets charged another is a real trust problem. Availability beats strict consistency for the read path, so search should keep serving even when a feed is late, showing a slightly stale price rather than an error. The one place correctness is not negotiable is that the price search shows has to be the price checkout honors.
Flag one relaxation now, because it shapes the whole design. The volatile fields, price and availability, change far more often than the stable catalog text, and you do not want a price change to trigger a re-index of a document. So the inverted index holds a product's slow-moving text and identity. Price and availability live in a separate overlay store that updates cheaply, and the two are joined at query time. Naming that split early, and pointing at where each kind of update lands, is a large part of what this prompt tests.
- Query: A store, then a term. The read key is a (store, query) pair. Search is always scoped to the one store the customer chose, so results reflect that store's assortment, price, and stock, never a global catalog.
- Read skew: Searches swamp feed updates. Query volume vastly outnumbers feed ingestion, so retrieval and ranking are optimized hard and reconciliation runs as a background pipeline off the read path.
- Freshness: Two speeds. Catalog text and photos can lag hours. Price and availability must land within minutes, since a stale price the customer acts on becomes a checkout mismatch.
- Consistency: Available now, correct at checkout. The read path stays up on slightly stale data, but the price shown has to reconcile with the price charged, so checkout re-reads the authoritative price.
The set-up
Core entities
Name the objects before drawing. Four carry the design, and the key split is between the canonical product, which is stable and shared, and the per-store overlay, which is volatile and enormous.
Product (canonical)
The deduplicated master record for a real-world item: name, brand, size, a GTIN or UPC barcode when it exists, images, and a category in the taxonomy. There is one canonical product no matter how many retailers sell it. This is what search matches text against and what the customer sees on the shelf. Millions of these exist across the whole network.
Feed item
A single retailer's listing of a product it sells, carrying that retailer's own internal id, its product-name string, its price, and a raw stock signal. Feeds arrive constantly and are messy, partial, and sometimes wrong. Every feed item has to be matched to a canonical Product before it is useful, which is the reconciliation job.
StoreProduct (overlay)
The (store, product) pair that carries this store's price, active promotions, and current availability. This is the high-cardinality object: roughly stores times assortment, on the order of a few billion rows. It holds only the per-store deltas, never a copy of the canonical attributes, so it stays cheap to write and easy to join in at query time.
Store
A physical location tied to one retailer and one region, carrying its assortment (which products it stocks) and the region's pricing rules. The store id is the scope key for every search: a query only ever retrieves and ranks over the products this store carries.
The split that organizes everything is stable identity versus volatile state. A canonical product's name, brand, size, and category barely change, so they belong in the search index, where a document is expensive to rewrite. A store's price and a product's availability change constantly, so they belong in the overlay store, keyed by (store, product), where an update is a cheap point write. On the read path a query retrieves candidate products from the index, then joins each one against the overlay for this store to attach the live price and stock. On the ingestion path a retailer feed is normalized and matched to a canonical product. The update is split so identity changes flow to the catalog and index while price and stock updates flow to the overlay. Keeping those two update streams apart is the reason search can be both fast and fresh.
API
The external surface is small: run a search scoped to a store, autocomplete a prefix, and open a product page. The interesting structure is internal. A search request expands into a retrieval against the index plus a batched overlay join for the chosen store. A retailer feed enters through an ingestion queue instead of writing the catalog inline.
The call to dwell on is getStoreProducts. In a naive design each search document carries the price and stock for every store that sells it. One price change rewrites a huge document, and searches read stale embedded prices. Here the index carries only stable identity, and getStoreProducts fetches the live price and availability for exactly the candidates a query returned, for exactly the chosen store, in one batched read. Treating the per-store overlay as a separate keyed store that search joins in at query time, rather than fields baked into the index, is the mental shift the rest of the design hangs on.
High-level design
Draw the smallest version that shows both paths, then go deep on the reconciliation and the store-scoped retrieval, because those are the parts that make this Instacart. The read path is a retrieve-then-join. A customer submits a query for a chosen store. The search service parses it and retrieves candidates from the index, restricted to that store's assortment. It joins each candidate against the per-store overlay for live price and stock, blends in sponsored items, then ranks and pages the result. The ingestion path runs alongside and off the request. A retailer feed lands on a queue. The ingestion pipeline normalizes each item, matches it to a canonical product, and splits the update so identity flows to the catalog and index while price and stock flow to the overlay.
Color marks the role. Blue is the two marketplace parties that touch this design directly, the customer searching a store and the retailer feeds supplying the data. Violet is the services, the search-and-rank service, the ingestion pipeline, and the ads service. Amber is the feed queue that buffers retailer uploads. Green is the stores, the search index, the canonical catalog, and the per-store price-and-availability overlay.
1) Understand the query and scope it to the store. A search request arrives with a store id and a raw query string like organik egs. The search service first cleans the query: correct the spelling to organic eggs, expand synonyms so soda also matches pop, and detect whether the intent is a brand, a specific product, or a category. It then fixes the retrieval scope to the chosen store's assortment, because a customer shopping one Safeway must never see a product only a different retailer carries. What is punted here is how the store's assortment is represented in the index cheaply, which the store-scoping deep dive answers. This step is small but load-bearing: get the scope wrong and every later stage ranks the wrong candidate set.
2) Retrieve candidates from the index. With a clean query and a store scope, the service queries the search index for products whose text matches, filtered to what this store carries. It gets back a few hundred candidate ids ranked by a first-pass text relevance score. No price or availability is read here, because the index deliberately holds only stable identity, so retrieval is a fast inverted-index lookup over slow-moving data. The candidate set is intentionally wider than a page: the real ordering comes after the overlay join and the full ranking, so retrieval favors recall and hands a generous slate to the ranker. What is deferred is attaching the live per-store price and stock, which the next step joins in.
3) Join the overlay and rank. The service takes the candidate ids and does one batched read against the availability and price overlay for this store, attaching each candidate's live price, active promotions, and availability signal. In parallel it asks the ads service for eligible sponsored items for the query and store. Now it has everything ranking needs. It scores each candidate by text relevance blended with in-stock likelihood, product popularity and a bounded slot for paid placement. Then it hydrates display fields from the catalog store or its cache and pages the result. The in-stock boost is doing quiet work here: it pushes items the shopper is likely to actually find toward the top, which is where the ranking touches the fulfillment side. What this step punts is keeping the overlay and index fresh, which the write path owns.
4) Ingest and reconcile on the write path. Meanwhile retailer data takes the other route. Retailer feeds land on the feed queue, and the ingestion pipeline consumes them. It normalizes each item and matches it to a canonical product. Then it splits the update: identity and text changes upsert the catalog store and re-index into the search index, while price and stock changes upsert the overlay. Because price and availability skip the index entirely, the high-frequency updates are cheap point writes and never cause a re-index storm. By the time a customer's next query runs, a price change from minutes ago is already reflected through the overlay join. The deep dives take this apart, starting with the matching that turns forty messy feed listings into one product.
Size the catalog and the query load
A few numbers set the shape of both paths. Start from the network. Instacart spans on the order of 80,000 stores across many retailers and regions. The canonical catalog runs to millions of unique products, but any one store only stocks a slice of that, roughly 40,000 to 50,000 items on its shelves. Multiply store count by assortment and the per-store overlay is the big object: about 80,000 times 45,000, which lands near a few billion (store, product) rows. That is the number that decides the overlay's storage, because it is far larger than the catalog itself. It is also why the overlay holds only per-store deltas, never a copy of each product's attributes, since duplicating catalog fields a few billion times would be enormous waste.
The query side is heavy but shaped by the read skew. Take on the order of a few hundred million searches and autocompletes a day. That averages a few thousand queries a second and peaks several times higher on weekend and evening rushes, call it up to roughly 20,000 QPS. Each query is one index retrieval plus one batched overlay read, so internal fan-out stays small and bounded. That only holds if store-scoped retrieval stays a single index lookup instead of a scan. The search index itself is sized by the catalog, not the overlay: millions of documents carrying text and identity fields, which is on the order of tens of gigabytes, comfortably shardable. The expensive part is keeping retrieval store-scoped and the overlay fresh, so the numbers push the design toward stable-text-in-the-index and volatile-state-in-the-overlay.
- Stores: ~80,000. Physical locations across many retailers and regions. The store id is the scope key for every query, so search is always over one store's assortment, never the global catalog.
- Products: Millions. Unique canonical SKUs network-wide. A single store stocks only ~40K to 50K of them, which is the slice any one search retrieves over.
- Overlay rows: ~A few billion. Store times assortment, about 80K times ~45K. The high-cardinality object; it holds only per-store price, promo, and stock deltas, never copied catalog fields.
- Search load: ~Hundreds of M / day. Averages a few thousand QPS, peaking near ~20K in rush hours. Each query is one index retrieval plus one batched overlay read.
Technology choices
Every box maps to a real category. Name the popular pick, say why that kind of tool fits, and be ready for the failure follow-up, because that is where the round goes.
- The search index is OpenSearch or Elasticsearch, an inverted index over the canonical catalog holding only stable text and identity fields. A search engine fits because the query path is text match plus filtering plus first-pass relevance scoring at high volume, which a relational store cannot do at this latency. The design choice worth defending is what is left out: price and availability never enter the index, so the high-frequency updates do not cause re-indexing. Plan for a shard or node loss, which replica shards cover with a promoted replica. Because the index is a derived copy of the catalog, a corrupted shard can also be rebuilt by re-indexing from the catalog store rather than restored from a fragile backup.
- The availability and price overlay is a wide-column or key-value store like Cassandra or DynamoDB, partitioned by store id with product id as the clustering key. That layout fits the two access patterns exactly: read one store's whole assortment, or read a specific page of products at one store, both as a single partition-local lookup. It also absorbs the constant stream of price and stock point writes cheaply, which a relational primary would struggle with at a few billion rows. It prizes availability and write throughput over strict consistency, which matches an overlay allowed to trail reality by a minute. A downed node is covered by replicas under a replication factor above one, and a redelivered feed update is harmless because an upsert rewrites the row to the current value rather than incrementing it.
- The catalog store is a relational primary such as PostgreSQL holding canonical products and the retailer-to-canonical mapping with provenance. A relational store fits because canonical products are structured records with relationships: a product belongs to a category and maps from many feed items. Reconciliation needs transactional merges plus the ability to trace and undo a bad merge. It is lower-volume than the overlay, so a single primary with read replicas is enough. If a primary node dies, a replica is promoted and writes fail over. Because search reads display fields from a cache and the index rather than the primary, a brief promotion window degrades ingestion, not customer queries.
- The feed queue is Kafka, carrying retailer feeds to the ingestion pipeline. A log fits because feed arrivals are bursty and heterogeneous, a full retailer sync being millions of items at once, and they must not block on the pipeline's pace. Separate topics let a steady stream of price deltas flow independently of bulk catalog syncs, so a nightly full sync never starves same-day price updates. If a consumer falls behind, the topic retains messages and the pipeline drains at its own rate. Offsets let a crashed worker resume without dropping or double-applying, and idempotent upserts make a replayed offset safe.
- The search service is a stateless service behind a load balancer, so it scales by adding instances and any instance can serve any store's query. It holds no per-session state; the assortment, the overlay, and the catalog fields all come from the stores. Query understanding (spelling, synonyms, category intent) runs inside it or as a sidecar model call, kept fast because it is on the hot path. Sponsored placement is fetched from the ads service per query and bounded to a few slots, so an ads outage degrades to purely organic results rather than failing the search.
Name the brand, defend the category. Three choices carry the design. Stable text goes into the search index and volatile state into the wide-column overlay. The relational catalog owns reconciliation and provenance. Kafka topics keep bulk syncs and price deltas from starving each other. The load balancer and the stateless service are standard components doing a familiar job. Keep the fast-changing price and stock out of the index and join them in at query time. Search can be a minute behind on price as long as it stays instant and available.
Potential deep dives
The signal lives here. An Instacart interviewer will push on several things. How do many messy feeds collapse into one product? How are a few billion per-store rows stored and served? How does retrieval stay scoped to one store without a per-store index? How does ranking blend relevance with availability and paid placement? And how do prices stay fresh and correct at checkout? Take them in that order.
1) Reconciling many retailer feeds into one product
The same box of Cheerios might be sold by forty retailers. Each sends a feed item with its own internal id and name string, one saying Cheerios 18oz and another saying General Mills Cheerios Cereal 18 OZ. If each becomes its own product, a search for cereal returns forty near-duplicate rows, ranking signal splits across them, and the customer is lost. The job is to collapse them into one canonical product. Walk it from the naive version up, because the right answer is layered and saying why is the point.
Approach
Take every feed item as its own catalog product. Ingestion is trivial: normalize the fields and write a row. No matching, no merging, the catalog is just the union of every retailer's listings.
Challenges
The catalog fills with duplicates. A single real product appears dozens of times, so search results become a wall of near-identical rows. The popularity and conversion signal that should lift one strong product gets split across its copies and sinks all of them. Worse, there is no shared identity, so the app cannot tell that the item a customer bought at one store is the same product at another, which breaks reorders and price comparison. This spends nothing on ingestion and pays for it forever on the read side.
Approach
Match on the GTIN or UPC barcode. Two feed items with the same barcode are the same canonical product, full stop. Group all feed items by barcode, pick one as the canonical record, and attach the rest as retailer mappings under it.
Challenges
This is the right backbone and it collapses the clean majority instantly, since a barcode is a global product identifier. The problem is the tail. Feeds routinely ship missing, wrong, or reused barcodes, private-label items may share a store-specific code, and weighted produce like loose bananas has no barcode at all. Barcode-only matching leaves that tail as unmatched duplicates, which is exactly the long tail of catalog quality that customers notice. You need a fallback for everything the barcode cannot resolve.
Approach
Layer the match. Resolve the barcode-clean majority deterministically. For the tail, block candidates by brand and category to shrink the comparison set, then score pairs with a fuzzy or learned match on normalized name, size, and image similarity. Auto-merge above a high confidence threshold, and route the uncertain middle to a human review queue. Store provenance, which feed items merged into each canonical product, so a wrong merge can be split back apart cleanly.
Challenges
The subtle risks run in both directions. A false merge shows the wrong image or the wrong size for a real product, which the customer sees, so the auto-merge threshold stays conservative. Ambiguous cases wait for review. A false split leaves duplicates, caught by periodic re-matching as feeds improve. Blocking keeps the fuzzy match affordable by never comparing a cereal against a detergent. Provenance is what makes the whole thing reversible, which is the property that lets you be aggressive on the easy matches and careful on the hard ones.
2) Storing a few billion per-store rows
The overlay is the largest object in the design. Do the arithmetic. About 80,000 stores times roughly 45,000 items each lands near a few billion (store, product) rows, and each row's price and availability changes far more often than anything in the catalog. The question is how to store and serve that without either wasting space on copied data or melting under the write rate.
Start with what the overlay must not do: it must not copy canonical attributes. A row holds only the per-store deltas, the price, the active promotions, and the availability signal, keyed by (store, product). The name, brand, and image live once in the catalog, so multiplying them across a few billion rows is exactly the waste to avoid. That keeps each row tiny, and tiny rows at high cardinality is precisely what a wide-column store is built for.
Partition by store id, cluster by product id. That layout serves both access patterns as one partition-local read. A category browse fetches a store's whole assortment; a search fetches the page of product ids it returned. Both land in one store's partition instead of fanning across the cluster. Writes are cheap point upserts, and because an upsert rewrites the row to the current price rather than incrementing anything, a feed delta that gets redelivered is harmless. A partition is bounded by a store's assortment, tens of thousands of rows, so no partition grows unbounded and hot stores can be handled with replication and caching rather than resharding. The one case to watch is a store with an unusually large assortment, a giant warehouse club, whose partition runs hotter, which is a caching and replica-count problem, not a data-model one.
3) Scoping retrieval to one store without a per-store index
Every search is for one store, and results must contain only products that store carries. The two naive fixes both fail at Instacart's scale. The interesting work is a scoping that stays a single fast index lookup. Walk the options.
Approach
Build a separate search index for each store, containing exactly its assortment. A query goes straight to that store's index, so scoping is automatic and results only ever contain carried products.
Challenges
80,000 indices is an operational nightmare. Most stores of the same retailer carry nearly the same assortment, so this copies the same products into thousands of near-identical indices. That wastes enormous space, and one change to a widely carried product means updating it in thousands of places. Index management, sharding, and rebuilds all multiply by store count. This makes scoping trivial by making everything else unmanageable.
Approach
Keep a single index, but with one document per (store, product) pair, each carrying the product text plus its store id. Filter by store id at query time and you get store-scoped results out of one index.
Challenges
Now the index has a few billion documents instead of millions, most of them identical product text repeated once per store. The index balloons into terabytes, retrieval slows under the bloat, and the same catalog change fans out into thousands of document rewrites. It trades the per-store-index problem for a per-store-document problem, which is the same duplication in a different shape.
Approach
Keep a single index at canonical-product granularity, millions of documents. Each document carries a compact representation of which retailer or stores carry it, so a query filters candidates to the chosen store's assortment during retrieval. Because most assortment is shared within a retailer, represent carriage at the retailer level with store-level exceptions rather than listing every store on every document. Availability then trims the rest at the overlay join, since a carried item can still be out of stock.
Challenges
The tension is keeping the carriage filter both accurate and cheap. Listing every store id on a popular product's document is expensive to store and update. A retailer's stores mostly share an assortment, so filter by retailer plus a small per-store exception set. Assortment does drift as stores add and drop items, so the ingestion pipeline updates carriage on the affected documents, which is a low-frequency change compared to price. The payoff is decisive: one index, millions of docs, and store scoping as a filter rather than a structural explosion, which is what keeps retrieval a single fast lookup.
4) Ranking: relevance, in-stock, and sponsored placement
Retrieval hands the ranker a few hundred candidates that match the text and are carried by the store. Ordering them is where the four sides of the marketplace all pull at once, so ranking is a blend of signals. The interviewer will want you to reason about it in the open.
Start with relevance. A first-pass text score, the standard BM25-style match from the index, gets the obviously on-topic products to the top. Query understanding from step one sharpens it, so a misspelling or synonym still matches and a category query like eggs does not lose to a brand named Egg. On top of that sits the business layer. In-stock likelihood, read from the overlay's availability signal, down-weights items the shopper is unlikely to find. An out-of-stock top result sends the shopper hunting and ends in a substitution the customer may dislike. Popularity and conversion, how often this product is bought after this query, pushes proven winners up. Light personalization, especially a customer's own past purchases, surfaces the milk they always buy. Each of these is a feature in a ranking model or a weighted term. Say you would learn the weights from engagement and fulfillment outcomes instead of hand-tuning them forever.
5) Keeping prices fresh and correct at checkout
Prices and availability change all day, and a customer who adds an item at one price only to be charged another loses trust fast. The question is how the volatile data stays fresh in search and, more importantly, stays correct when it actually matters, at checkout.
Freshness in search comes from the split the design has leaned on throughout. Price and stock deltas ride a Kafka topic separate from bulk catalog syncs, the ingestion pipeline applies them as point upserts to the overlay, and the next query picks them up through the join. No re-index is involved, so a price change is visible within minutes rather than waiting on a document rewrite. Full retailer syncs, which carry catalog text and the occasional large price reset, run on their own schedule and cadence so they never starve the steady stream of same-day deltas. The overlay is allowed to trail reality by a minute or two, which is invisible to a browsing customer.
Correctness at checkout is the part that cannot be eventually consistent. Search shows the overlay's price, which may be slightly stale, so treat that displayed price as a strong hint, not a contract. At checkout, and again when the shopper picks, re-read the authoritative price for the store. If it has moved, the customer sees the change before paying instead of on the receipt. Availability works the same way. The truest signal is the shopper standing in the aisle, so an item that looked in stock at search time can still trigger the out-of-stock and substitution loop at pick time. Framing price as fast-but-approximate for browsing and authoritative-at-checkout, and pointing at where each read happens, is how you show you understand which staleness is free and which is a trust violation.
What is expected at each level
How much you need to land scales with the level, and this round is where Instacart differentiates senior candidates, since the reported failure mode is passing coding and then stalling on architecture. The ladder uses L-codes with doubled titles, so confirm the L-number rather than the word. The bars below map to Engineer 2, Senior Engineer, and Staff Engineer.
Engineer 2 (L4)
Get the shape right and defend it. Frame the catalog as many retailer feeds reconciled into canonical products with a per-store overlay, draw the retrieve-then-join read path, and say why price and availability stay out of the index. Name that search is always scoped to one store, sketch query understanding at a high level, and show the ingestion pipeline running off a queue so the read path stays fast. The interviewer will drive the harder parts. The bar is a coherent skeleton, feeds to canonical products to index plus overlay to a store-scoped ranked query, that you stand behind with a nudge or two.
Senior Engineer (L5)
Depth carries it, and the two Instacart-specific problems are where you earn the level. Raise the reconciliation layering unprompted, barcode backbone plus a fuzzy or model fallback for the tail with human review and reversible provenance, and explain why a wrong merge is customer-visible. Own the index-versus-overlay split as a deliberate decision and show how store-scoped retrieval stays one index lookup rather than a per-store index or a per-store document. Talk about ranking as a blend of relevance, in-stock likelihood, and bounded sponsored placement, and connect the in-stock boost to the shopper and the substitution cost.
The bar is a near-complete design where you found the hard parts, the feed reconciliation and the store-scoping explosion, on your own, and reasoned about the customer, shopper, retailer, and brand as you went. Most senior candidates are sorted right here.
Staff Engineer (L7)
Lead across the whole system. Reason about the overlay's partitioning and the hot warehouse-club store. Weigh the price-freshness pipeline against the checkout re-read. Then show how search degrades instead of breaking when a feed is late, the ads service is down, or a shard drops. Weigh the assortment-filter representation against how often carriage drifts. Then draw the boundaries with the availability-prediction, checkout and shopper-batching designs, so you are not rebuilding the whole marketplace in one round. Speak to the four-sided trade-offs a PM in the room is grading. The bar is that you drive the room while the interviewer pushes back.
Key Takeaways
- ✓The catalog is a reconciliation problem before it is a search problem. Many messy retailer feeds collapse into one canonical product graph, keyed off the barcode with a fuzzy or model-based fallback for the tail and reversible provenance for every merge. Lead with that: it is why this is an Instacart design and not generic e-commerce search.
- ✓Split stable identity from volatile state. Keep product name, brand, size, and category in the inverted index, keep per-store price and availability in a wide-column overlay keyed by (store, product), and join them at query time. That split is the single decision the design turns on, and it is what lets search be fast, fresh, and per-store without re-indexing on price changes.
- ✓Scope every search to one store with a single product-granularity index and an assortment filter, not a per-store index (80,000 of them) or a per-store document (a few billion of them). Rank by relevance blended with in-stock likelihood, popularity, and a bounded slot for sponsored placement, so the customer, shopper, retailer, and brand are all served.
- ✓Price is fast-but-approximate for browsing and authoritative-at-checkout. Volatile deltas flow through the overlay within minutes and may trail slightly. Checkout and the shopper re-read the true price and stock, because a wrong price on the receipt breaks trust.
Instacart interview guideKnow what Instacart asks before you walk in.
30 coding · 5 system design · 14 behavioral questions
Get their questions first.
InterviewLegend is independent and not affiliated with or endorsed by any company it covers.