Vector search service architecture.
The failure mode here is not an error. It is recall dropping slowly as the index grows, with nothing in your metrics saying so.
The components
Every row below is read from the graph that produced the diagram above, so the two cannot disagree.
| Component | Tier | Why it is there |
|---|---|---|
| Client | Client | Supporting component |
| Search API | Application | Supporting component |
| Embedding service | Application | Same model at index and query time or recall collapses |
| Hybrid retriever | Application | Dense vectors plus BM25, fused |
| Filtered search | Application | Pre-filter or post-filter changes recall a lot |
| Reranker | Application | Supporting component |
| Index builder | Application | Rebuilds and swaps atomically |
| Qdrant | Data | HNSW, recall degrades quietly as it grows |
| Elasticsearch | Data | Supporting component |
| Postgres | Data | Supporting component |
| Object storage | Data | Supporting component |
| Prometheus | Infrastructure | Track recall on a fixed query set, not just latency |
Design decisions worth arguing about
A diagram shows what was chosen. It does not show what it cost, and that is usually the part that matters in a review or an interview.
The same embedding model at index and query time
Vectors are only comparable within the model that produced them, so changing the embedding model means re-embedding the entire corpus before a single query is served. Treating the model version as part of the index identity, and refusing to query an index built with a different one, turns a silent recall collapse into a loud error. Teams learn this once.
Hybrid retrieval, because dense vectors miss exact terms
Embeddings are good at meaning and poor at specifics: part numbers, error codes, rare proper nouns. Keyword search is the reverse. Running both and fusing costs a second index to maintain and a fusion weight to tune, and it removes a whole class of failure where a user searches for a literal string that is in the corpus and gets nothing.
Pre-filter or post-filter changes recall more than people expect
Filtering after the vector search is simple and can return far fewer results than requested, because the nearest neighbours were all filtered out. Filtering during search preserves recall and requires index support for it. The naive implementation looks correct in testing on a small corpus and degrades badly with selective filters at scale.
Rebuild and swap atomically
Mutating a live index means locking or serving inconsistent results mid-update. Building a new one, validating it against a fixed query set, and swapping the pointer makes updates instant and lets you refuse to ship an index whose recall regressed. It costs double the memory during the swap, which is the price of never serving a half-built index.
How it changes with scale
Query volume drives the reranker, which is the expensive stage and the first thing to cache. Corpus size drives the vector index, and approximate nearest neighbour search degrades gracefully in latency while degrading recall invisibly, which is the asymmetry that matters. Filter cardinality is the third axis and the one most often forgotten in capacity planning.
Where it breaks first
Silent recall degradation. Nothing errors, latency looks normal, and the right document stops being returned as the index grows or drifts away from the queries people ask. Because there is no exception and no alert, this is found through user complaints months later, which is the argument for evaluating recall on a fixed labelled query set on every index build rather than only at launch.
Draw this yourself
Open the Diagram tab and describe the system. The agent emits a semantic graph rather than coordinates, so you can edit the components and the layout re-solves instead of drifting.
When the shape is right, Implement in code turns the canvas into a markdown specification, every component, every relationship and the notes, and starts a real turn in the Code tab with it.
Questions about this design
More templates
- URL Shortener System DesignThe canonical read-heavy system: roughly a hundred reads for every write, and a redirect that has to be fast enough that nobody notices it happened.
- Chat Application System DesignLong-lived connections change everything: the hard part is not storing messages, it is knowing which of your servers is holding the socket you need to write to.
- Payment System DesignThe only system on this list where being approximately right is indistinguishable from being wrong, and where the provider, not you, holds the truth.
- RAG Pipeline ArchitectureAlmost every RAG system that disappoints is failing at retrieval, not generation, and the architecture is what decides whether you can tell.