← Back to Blog
For: AI Engineers, ML Engineers, Platform Engineers, AI Systems Architects

Claude Code's Code Intelligence Usually Makes It Dumber

Semantic navigation is a net cost on most repos - you earn it only past a scale threshold, and even then you want the live form, not an index.

#claude-code#coding-agents#code-intelligence#lsp#context-engineering

An engineer I know wired a code-intelligence server - a semantic search tool backed by the Language Server Protocol (LSP) - into their Claude Code setup, expecting the agent to stop fumbling around a large codebase. It did the opposite. Sessions began hitting the context limit sooner. The agent sometimes ignored the new tool entirely and fell back to grep. On the repository where they most wanted the help, the agent got slower and no more accurate. They had added a tool built to make the agent smarter, and on their codebase it made the agent worse.

That is not a misconfiguration. On most repositories it is the default outcome, and the reason has a name.

The thesis: code intelligence is a net cost until you cross a scale threshold

Here is the claim this article owns. On most repositories, adding a code-intelligence tool to a coding agent is a net cost, not an upgrade. Every tool you wire in taxes the context window on every request, and below a certain scale that tax buys you nothing. You earn code intelligence only past a threshold - a large codebase where discovery is the expensive part of the work. And past that threshold, there are two forms you can add, and one of them quietly reintroduces the problems the agent was built to avoid. You want the live form - a language server queried on demand - not a materialized index.

Two mistakes follow from getting this wrong. Most teams reach for code intelligence too early, on repos where it only slows the agent down. Then, when they finally hit a codebase big enough to need it, they reach for the wrong form - they index the repo, and the index reintroduces staleness the agent never had.

Zoom out and the rule is not really about code intelligence. For any agent whose context window is the binding constraint, a tool is not free to have - only to use. Its description is standing rent against the window, charged every turn whether the tool fires or not. So a capability carries negative expected value until the rate you actually use it, times the precision it adds when you do, clears that rent. Adding a capability to a context-bound agent is a bet that you will use it enough to cover what it costs you just by existing - and most tools lose that bet. Code intelligence is simply the sharpest case: its rent is high because its tool schemas are large, and its payoff is zero until discovery dominates the work. The same test should gate every tool you bolt on - does this capability earn its standing rent, on this workload, today.

The consensus is that more capability is strictly better - that handing the agent a semantic-search tool or an indexed code graph can only help. For a majority of real repositories that is false, and it is false for a concrete, measurable reason.

The context tax: why every tool costs you on every request

Every tool an agent can call has a description, and that description sits in the context window for the entire session - whether the agent uses the tool once or never. This is the context tax. Wire in a semantic-navigation server and its tool schemas occupy working memory continuously, competing with your actual code for the model's attention. This is the same reason a bloated tool registry quietly degrades an agent: every tool description is standing rent. You do not pay this cost only when the agent searches. You pay it on every turn, as a standing deduction from the window - which is why the context window is infrastructure to manage, not a free resource.

There is a second, sharper effect on top of the raw token cost. Claude Code describes its own built-in tools at length, so the model has a strong prior toward using them. Bolt on an external navigation server and the agent may lean on grep anyway and never call the tool you added - you are paying the tax and getting none of the benefit. This is why teams report a semantic server making sessions hit the context limit faster, not slower, and why the agent that was supposed to get sharper instead gets duller.

On a small repository this is a pure loss. The agent could have found the file by reading three of them, so the navigation tool answers a question that was never expensive, while its schema eats the window the whole time. The tax only pays for itself once discovery genuinely dominates the work - a large codebase where the agent would otherwise burn its window reading its way to the right file. As a rough rule of thumb that break-even sits on the order of tens of thousands of lines, but the honest signal is not a line count. It is the moment read-the-repo discovery starts eating your context budget. Below that point, the correct amount of code-intelligence machinery is none.

Why Claude Code ships with no index in the first place

This bias toward less machinery is not a quirk of my advice. It is the design of the tool. Claude Code maintains no vector index of your repository. It navigates the way an engineer does: it runs grep, reads a file, follows a reference to the next file, loading context just in time. The reasoning is documented. As reported from a Hacker News thread and summarized by Vadim Kravcenko, Boris Cherny, the creator of Claude Code, said early versions used Retrieval-Augmented Generation (RAG) with a local vector database, and the team moved away from it because "agentic search generally works better. It is also simpler and doesn't have the same issues around security, privacy, staleness, and reliability."

Hold on to that list - security, privacy, staleness, reliability - because it returns. A stored index has to embed your code and keep it somewhere, which is the security and privacy cost. It is a snapshot taken at one moment, which is the staleness cost. And a vector index in particular returns fuzzy nearest-neighbor matches that are often almost-right in a way that is worse than a clean miss, which is the reliability cost. The team reported that agentic search outperformed the indexed approach by a wide margin in their testing, and found the size of the gap surprising. This is a concrete instance of a broader call I map out separately - when to reach for RAG, tools, or neither.

So the starting point for any code-intelligence decision on Claude Code is a tool that already bet against the index, on purpose. That does not mean you never add anything. It means the burden of proof sits on the addition. The context tax is why: capability is not free, and on most repos it does not clear its own cost.

When you have earned code intelligence: the two forms

Say your repo is past the threshold. Discovery genuinely dominates, the codebase is large, and the agent is burning its window reading its way around. Now the tax is worth paying. The question becomes which form of code intelligence you add, and this is where the second mistake happens.

There are two forms, and the database world already has names for them. A materialized view is a stored, precomputed answer, derived from the source at some earlier moment and refreshed on a schedule. A derived view is computed when you query it, so nothing is stored and there is nothing to refresh. Indexing your repo - vector embeddings or a precomputed code graph - is the materialized form. A language server queried on demand is the derived form. They feel interchangeable when you are shopping for "code intelligence." They are not.

The materialized form carries a defect the database world has known about for decades: a materialized view is only as fresh as its last refresh, even when the refresh is incremental. Between refreshes it drifts from the source. For a human running one lookup that is a minor annoyance. For an agent making an edit based on the answer, it is a correctness bug. I watched exactly this happen: a team embedded their repo into a vector store, and a week later the agent shipped a change that called a helper with a signature that had been refactored three commits earlier. The agent asked its index where the code lived, and the index answered confidently with a location that no longer existed. The embedding was from last Tuesday. The code was from that morning.

The derived form has no last-refresh, because it has no refresh at all. That is the form you want, and it has a name.

The Live Oracle: LSP as the derived form of code intelligence

The derived form is a language server - the same LSP engine your editor uses for go-to-definition and find-references. I call it the Live Oracle: you ask it a symbol question, it computes the answer against the current code, and it keeps no persistent snapshot you have to keep in sync. Ask it where PaymentValidator is defined and it resolves against the files as they are now. Ask it what references find_by_id and it walks the real symbol graph on disk, not an embedding of it.

The Live Oracle answers the four costs on Cherny's list, but be precise about how, because the naive version of this claim is wrong:

  • Staleness: there is no scheduled refresh to fall behind. The only staleness is a bounded reprocessing window right after an edit (covered below), not a snapshot that silently ages for days.
  • Security and privacy: no code leaves the machine and no durable embedded copy exists to be stolen or leaked. The server does hold the parsed project in local memory, so "stores nothing" would overstate it - the honest win is that there is no persistent artifact and no third-party embedding service.
  • Reliability on exact lookups: for a symbol it can resolve, the answer is an exact resolution, not a nearest-neighbor guess.

On Claude Code there are two ways to install a Live Oracle, differing mainly in who runs the language server. The first is Anthropic's first-party path. Install the language-server binary yourself, then add the matching plugin (the official marketplace is built in, so there is no marketplace to add first):

code
# 1. install the language server binary (must be on your PATH)npm install -g typescript-language-server typescript# 2. add the first-party plugin and reload/plugin install typescript-lsp@claude-plugins-official/reload-plugins

The second is an open-source LSP-backed MCP server such as Serena, which exposes find_symbol and find_referencing_symbols across dozens of languages. It wires up as a one-time Model Context Protocol (MCP) config entry:

code
// .mcp.json - LSP-backed symbol navigation as an MCP server{  "mcpServers": {    "serena": {      "command": "uvx",      // pin a ref in production rather than tracking HEAD      "args": ["--from", "git+https://github.com/oraios/serena",               "serena", "start-mcp-server",               "--context=claude-code", "--project-from-cwd"]    }  }}

Both still pay the context tax - they are tools with descriptions in the window. That is exactly why you install one only after your repo has crossed the threshold where the precision earns its cost.

Why the Live Oracle still needs a warm server: liveness is not statelessness

The Live Oracle is fresh, but it is neither free nor stateless under the hood, and pretending otherwise is where most write-ups go wrong. A language server builds its understanding incrementally. It parses imports, resolves types, and builds a call graph, and that takes time. The freshness comes from holding no persistent snapshot across sessions. It does not come from holding no state at all. Within a session the server is very much stateful, and it can lag: right after a large edit there is a real window where a reference query returns a partial answer, until the server finishes reprocessing.

This is why a naive per-call LSP bridge is a known anti-pattern. The bridge starts the server cold, asks one question, and tears it down, so the server answers from an empty index before it has parsed your project. The team behind the agent-lsp project documents exactly this trap, and their fix is a stateful runtime that indexes the workspace once and keeps it warm. The lesson is not "avoid LSP." It is that the Live Oracle must be kept warm. A long-lived server, pre-indexed once and reused, is the difference between instant precise navigation and a two-minute stall on the first lookup. Concretely, pre-index large repos and raise the tool timeout. The cold start on a large repo can run into minutes, and the server can hold several gigabytes while it keeps the graph resident. These are costs you plan for, not deal-breakers.

The distinction from an index is still real, and it is the crux. The warm server holds a live working set that tracks your edits through change notifications, with a lag measured in reprocessing latency. An index holds a snapshot that drifts on a refresh schedule you can forget to run. Bounded, self-healing lag is not the same failure as unbounded, scheduled staleness.

What the Live Oracle does not answer: exact resolution is not discovery

One more honesty check, because it is easy to oversell symbol navigation as the answer to everything. A language server answers exact questions: where is this symbol defined, what references this method. It does not answer conceptual questions: where is the code that handles refunds, which module owns retry logic. Those are discovery queries, and an exact symbol resolver cannot serve them - it can only jump to a name you already have.

That reframes the failure in the opening. The team's real error was not "indexing." It was using a fuzzy discovery tool for a job that needed exact resolution, and paying staleness for the mismatch. Discovery still belongs to the model reading the code with grep, or, at genuine cross-repo scale, to an index. The Live Oracle owns exact resolution, and only that. Keeping these query classes separate is what stops the whole comparison from collapsing into "which tool is best" - they answer different questions.

Freshness ownership: choosing between grep, LSP, and a code graph

CLAUDE CODE · CODE INTELLIGENCEFreshness OwnershipThree ways to give an agent code intelligence, sorted by who keeps it in sync1 · Model owns itPlain agentic searchgrep + Read - no extra tool2 · Server owns itThe Live Oracle: LSPderived live - no refresh3 · Graph owns itIndex e.g. Sourcegraphmaterialized - refreshedFresh, but lexicalgrep hits the wrong symbolDefault under the thresholdExact - keep warmCold-start on big reposPre-index + raise timeoutCross-repo reachStaleness + infra costExact graph, still snapshotTHE ORDERING RULENo index by default.Small repo: plain agentic search.Large + typed: add the Live Oracle.Many repos at once: accept the index.Weak or untyped code: expect grep fallback.Never embed the repo just to feel smarter.THE LIVE ORACLEPrecision without a snapshotAn LSP server is queried on demand.It computes against current code andkeeps no snapshot on a schedule.Derived, not materialized.Freshness is a property, not a chore.

Once you accept that code intelligence is earned, not default, the remaining choice is between three paths, and feature-count comparisons miss what separates them. The axis that picks your path is freshness ownership: who is responsible for keeping the semantic layer in sync with the code as it changes right now. There are exactly three answers, and each names a path.

  • The model owns freshness (plain agentic search). With no extra tool, Claude Code re-reads the code on demand with grep and Read. Freshness is perfect because it reads current files, but precision is lexical, not semantic - grep send still hits the mailer, the queue, and a comment. This is the right default below the threshold, where the context tax would buy nothing.
  • The language server owns freshness (the Live Oracle). The semantic layer is derived live, so keeping it fresh is not a task anyone runs - it is a property of how the answer is computed. This is the first-party plugin and the Serena paths. Cost: the context tax, a warm server per language, and cold-start on large repos.
  • A server-side graph owns freshness (the index). A hosted platform such as Sourcegraph precomputes a cross-repository code graph and refreshes it on a schedule. Be fair about what this is: a code graph is exact, not fuzzy - it is compiler-derived reference data, not nearest-neighbor embeddings. Its cost is not unreliability; it is staleness between refreshes, plus the security surface of a served index and the infrastructure to run it. What you buy for that cost is real: precise navigation across many repositories at once, at organization scale.

That third path is the honest case for a materialized index, and it is why this is a decision and not a sermon. If your unit of work spans dozens of repositories, and the agent must trace a call from one service into another, no per-repo language server can do it. A cross-repo code graph is built for exactly that, and its staleness is the price of the reach. The index is not wrong. It is wrong as a default for single-repo work, where the Live Oracle gives you exact resolution with no snapshot to age.

mermaid
flowchart TD
    Start[Agent fumbles around a large codebase<br/>you are tempted to add code intelligence]

    Start --> Q0{Is discovery actually<br/>eating your context budget?}
    Q0 -->|No, small repo| Plain[Add nothing<br/>plain agentic search: grep + Read]
    Q0 -->|Yes, past the threshold| Q1{Work spans many<br/>repos at once?}

    Q1 -->|Yes, org scale| Graph[Server-side code graph<br/>e.g. Sourcegraph MCP]
    Q1 -->|No, one repo| Q2{Typed language with a<br/>solid language server?}

    Q2 -->|Yes| Oracle[The Live Oracle<br/>first-party LSP plugin or Serena]
    Q2 -->|Weak / dynamic LSP| Degrade[Live Oracle where it helps<br/>expect grep fallback in untyped code]

    Graph -.materialized: staleness<br/>as the price of reach.-> Cost1[Accept the index cost<br/>on purpose]
    Oracle -.derived: keep it warm.-> Cost2[Pre-index + raise timeout<br/>no snapshot on a schedule]

    classDef start fill:#FFD93D,color:#2C2C2A,stroke:#D4B02A;
    classDef decision fill:#7B68EE,color:#FFFFFF,stroke:#6858DE;
    classDef good fill:#6BCF7F,color:#2C2C2A,stroke:#4CAF64;
    classDef neutral fill:#4A90E2,color:#FFFFFF,stroke:#3A7BC8;
    classDef warn fill:#FFA07A,color:#2C2C2A,stroke:#E07850;

    class Start start;
    class Q0,Q1,Q2 decision;
    class Oracle,Cost2 good;
    class Plain,Graph,Degrade neutral;
    class Cost1 warn;

Where the Live Oracle degrades: it fails incomplete, not silent

The Live Oracle inherits one hard limit, and it is not the clean safety net it first looks like. Navigation is only as good as the language server underneath it. A typed language with a mature server - TypeScript, Go, Rust, Java, C# - gives exact resolution. A dynamically-typed corner with weak server support degrades toward grep, because the server cannot resolve what the language never declared. A repository that does not typecheck cleanly degrades the same way.

Be honest about the failure shape, because it is not "returns nothing." On dependency-injection wiring, reflection, or string-dispatched calls, find_referencing_symbols returns a set that looks complete but silently omits the runtime-wired callers the server cannot see. That is a confident, incomplete answer - the same failure class you were trying to escape, in a different disguise. It is why load-bearing indirection like a DI binding or a string-dispatched task still belongs in a hand-written map, a point I develop in the brownfield harness article. Know your repo's dynamic corners, and do not assume symbol navigation is uniform across a polyglot tree.

How to add code intelligence to Claude Code: the checklist

Decide in this order. The ordering is the advice, because the most common mistake is adding capability before you have earned it.

  1. Start with nothing. Plain agentic search - grep plus Read - is the correct default until discovery genuinely eats your context budget. On a small repo, adding any navigation tool pays the context tax for precision you did not need.
  2. Add code intelligence only past the threshold. The signal is discovery cost, not a line count - when the agent burns its window reading its way to the right file, the tax starts paying for itself. As a rough marker, that is on the order of tens of thousands of lines.
  3. Past the threshold, choose the derived form. For a large single repo, install a Live Oracle - the first-party LSP plugin for your language, or Serena as an MCP server. It gives exact resolution with no snapshot to age.
  4. Keep the oracle warm. Pre-index large repos and raise the tool timeout so the first lookup does not pay cold-start. A cold per-call LSP bridge is the anti-pattern.
  5. Know where it degrades. On untyped, non-compiling, or DI-heavy code the server returns confident-but-incomplete references. Keep those corners in a hand-written map.
  6. Reach for a materialized index only for cross-repo scale. If the work spans many repositories at once, a hosted code graph such as Sourcegraph is the right tool, and you accept its staleness, security, and infrastructure costs on purpose. Do not reach for it as a single-repo default, and never embed a single repo into a vector store to "make the agent smarter."

If you take one thing, take the general law and not just the code-intelligence case: a tool on a context-bound agent must earn its standing rent, and most do not. Code intelligence is the sharpest instance because its rent is high and its payoff is zero until discovery dominates. The consensus treats capability as a strict upgrade, so teams add it early and reach for the index when they scale. Invert both moves. Add nothing until discovery is the expensive part, and when it is, prefer the derived form - a language server queried on demand - over a materialized index. A materialized view is stored and drifts between refreshes. A derived view is computed now and never sits on a stale schedule. Reach for the index only when you need what only it can give, cross-repo reach, and pay its staleness on purpose. Everywhere else, the intelligence is in the model, the precision is in the language server, and neither one has to be an index. This is the coding-agent case of a wider argument I make across the Harness Engineering series: the leverage is in the harness you build around the model, not in the model alone.

References


AI Engineering

Follow for more technical deep dives on AI/ML systems, production engineering, and building real-world applications:


Comments