Semantic code search
Find code by what it means, not just what it says — how semantic search works over source code, where each pure approach fails, and what measured hybrid retrieval looks like.
What is semantic code search?
Semantic code search retrieves code by meaning rather than literal text. Ask "how does auth middleware work?" and it returns the middleware functions — even though the query never contains the word authMiddleware. Under the hood it embeds code chunks into vectors and ranks by similarity, so concept queries, paraphrases, and cross-language questions ("where do we hash passwords?") finally work.
It's the technology behind "ask your codebase questions" features, AI coding assistants' codebase grounding, and codebase-wide Q&A. But semantic search alone is not the finish line — it's half of the signal.
Where each pure approach fails
Three tools, three blind spots:
- Grep / ripgrep — matches literal characters.
grep -r "auth"floods you with every substring hit but can't answer "how does login validation work" at all. Perfect for exact strings, useless for concepts. - Pure vector search — matches meaning but not exact tokens. Searching for
AbortWithStatusJSONis a lexical lookup: nearest-neighbor vectors may rank visually similar functions above the true match. It also inherits the chunking problem — fixed line-count chunks cut functions mid-body, so the embedding describes a fragment. - Pure keyword search (BM25) — excellent at identifiers and exact terms, blind to paraphrase. "How does auth middleware work?" never names
authMiddleware, so the best-ranked result is unreachable.
Concept queries and identifier queries split the real-world query space roughly in half. Any single-signal search engine misses half of it.
Hybrid: the approach that covers both
Production-grade semantic code search fuses signals:
- AST-aware chunking — chunk along tree-sitter parse boundaries (functions, classes, methods), so every embedded chunk is a complete semantic unit. Class-level header chunks let methods defined far from their class still surface for class-shaped questions.
- Dense + BM25 fusion — rank vectors and keyword scores together via Reciprocal Rank Fusion. Paraphrases ride the vector channel; exact identifiers ride the keyword channel; one merged ranking benefits from both.
- Adaptive weighting — query shape decides the tilt: a
camelCaseidentifier query leans lexical; a natural-language question leans semantic. - Symbol expansion — a hit on
validateToken()pulls invalidateToken's definition, so results arrive complete instead of as dangling references.
Run it locally in two commands
npm install -g @dhaxor/open-context oce index && oce search "how does login validation work?"
OCE embeds with an in-process MiniLM model by default — no API key, no network — and keeps vectors plus keyword indexes in local SQLite. Remote providers like Voyage are a drop-in upgrade for higher ceiling; keyword-only mode covers environments where vector search is unavailable, and every surface tells you which mode you're in.
Don't trust it unmeasured
The uncomfortable question for any search tool: what's your recall@10? OCE scores committed gold sets in CI — hybrid recall@10 0.943, keyword-only 0.886 on its own codebase, with per-query-shape adaptive fusion — and publishes the method and numbers on the benchmarks page. The code RAG guide covers the full retrieval pipeline, and the MCP server guide shows how to serve the same search to Claude Code or Cursor.
Frequently asked questions
What is semantic code search?
Retrieval of code by meaning rather than literal text — concept and paraphrase queries return the right functions even when the query doesn't name them. Built on embeddings over AST-aware chunks, and best fused with keyword search for exact identifiers.
How is it different from grep?
Grep matches literal characters — perfect for exact strings, useless for paraphrase. Semantic search matches meaning — great for concepts, weaker on exact tokens. Hybrid search does both.
Why doesn't plain vector search work well on code?
Two reasons: embeddings miss exact identifiers (lexical lookups), and naive line-count chunking embeds severed fragments. AST-aware chunking plus hybrid fusion fixes both.
Does it need the cloud?
No. OCE runs embeddings in-process and stores everything in local SQLite — fully offline. Cloud embedding providers are an optional upgrade, not a requirement.
How do I evaluate a semantic code search tool?
Gold sets plus recall@k / MRR / nDCG in CI — see OCE's published numbers, and run the same harness on your own repo with oce eval.