field notes · engineering
Building, tearing down, and rebuilding a code graph for AI agents, and learning exactly which problems a graph should own.
By Samrat Biswas (Sam), Chief Research Analyst
23 July 2026 · 9 minute read
I did the responsible thing. When I set out to give my AI coding agents a real understanding of a codebase, I stood up the proper stack: Neo4j for the graph, Voyage for best-in-class code embeddings, the whole cathedral. If an agent was going to reason about my code, it would do it over a rich, queryable model of that code, not by fumbling around with text search like it was 2011.
A few weeks later I tore most of it down, and kept the part that earned its place. The agents got better, my bill got smaller, and the graph I still run does a completely different job than the one I started with.
Because that is the real lesson, and it is not "graphs are overkill." It is that I had pointed an excellent instrument at the wrong problem. Here is where a graph actually belongs behind an AI agent, with the receipts.
The reason everyone reaches for a graph is sound. Agents burn tokens re-reading files to answer questions they should be able to look up, and a graph turns "what calls this function" from a dozen file reads into a single hop. If your motivation is cutting context and token costs, you are not wrong that a graph can do it. Reported reductions on structural queries run well into the double and triple digits.
Here is the catch I learned the expensive way: that is an efficiency win, not a capability win. The studies that show a graph slashing token use do not show the agent solving more problems. The cleanest example in the literature is a graph-guided system that crushed embeddings at finding the right code, and still moved the end-to-end fix rate by about two and a half points. Cheaper, yes. Smarter, no. Keep those two apart and half the confusion in this space disappears.
The unlock, for me, was realizing I had been treating one word as one problem. "Understanding the codebase" is really three different jobs, and a graph is right for only some of them.
Where is this function, what is in this file.
What breaks, what is dead, what calls what, three hops deep.
Module boundaries, layering, what is coupled to what.
Most of the excitement, and nearly all of the disappointment, comes from not keeping these apart. Once you do, the right architecture stops being a debate and starts being a lookup table.
Here is the thing that should give any would-be graph builder pause. The teams with the most to gain from a rich code index looked at the first job, finding code, and walked away from precomputed indexes entirely.
In 2025, Anthropic pulled the vector index out of Claude Code and went back to live search. Their creator's stated reason was almost rude in its simplicity: agentic search just worked better, and it dodged the staleness, privacy, and reliability problems that come with an index. Amazon then put a number on it, finding that live keyword search reaches roughly 94 percent of what a vector database gives you, with nothing to keep fresh. Sourcegraph, who more or less invented this category, dropped embeddings and reframed the whole question as relevance versus coverage.
grep, which none of us put on our LinkedIn, keeps quietly beating the cathedral.
So for the most common job, the humble tool won. If your plan is to build a graph so the agent can find code, you are re-solving a problem the frontier already closed, and modern agents grep well.
I am not here to bury graphs. I still run one. I just stopped asking it to fetch context and started asking it the questions text search physically cannot answer, and there a graph is not merely useful, it is the only honest tool in the room.
A bounded reverse walk over the call graph, not a prompt. A deterministic set, every node tagged by distance, the same answer every time.
Unreachable, unexported, untested, unrouted. A whole-graph question. A model that is "pretty sure" is not something I delete code on.
Matching a provider in one service to callers in another is a join over canonical identifiers, a view per-repo text search does not have.
Taint and reachability are a graph's home turf, which is exactly why the security world has always drawn them as graphs.
The method matters as much as the list, and it is where most code graphs quietly betray you. My call edges come from a language server or a SCIP index, not tree-sitter alone, because tree-sitter cannot resolve dynamic dispatch and will happily hand you a confident, incomplete graph. The edges static analysis genuinely cannot see, I recover from co-change history: files that always move together are coupled whether or not a call edge exists between them, and I label those inferred rather than dressing them up as fact. Every edge carries its provenance and its confidence, so a blast radius can say "complete for what I can prove statically, partial here, and here is why." That answer is worth ten that shrug and call themselves exhaustive.
Then there is the subtler prize, the one AI most reliably misses: the shape of the system itself. Agents are good at the local and the literal and clumsy at the architectural, because you cannot grep for a design seam. The elegant part is that this layer is stable, it drifts monthly rather than per commit, which quietly dissolves the usual objection that a graph goes stale. There is even a beautiful thirty-year-old idea for keeping it honest, the software reflexion model: declare the architecture you meant to build, compare it against what the code actually does, and treat the divergence as the finding. The gap between the map in your head and the territory on disk is usually the most interesting thing in the building, and a graph is how you measure it.
Ask an agent what breaks and you get a fluent, confident answer, then a different one on the next run. Good enough to read code, not to delete it.
A bounded reverse traversal returns the same set every time, each node tagged by distance, each edge banded by how it is known.
The instrument is a commodity. The judgment is not. You choose which questions are graph-shaped, and which the agent should just grep, live.
If you do build, know where the bodies are buried.
A wrong answer wearing the costume of a precise one is more dangerous than an honestly fuzzy grep.
Here is the reframe that saved me. Neo4j and a vector store are not competitors; they do different jobs. A vector index does similarity, "find code that looks related to this." A graph does traversal, "what transitively calls this." So the real question is never "Neo4j or pgvector." It is "do I need a dedicated graph database at all, or can the Postgres I already run hold the graph too?"
For repo-scale work, Postgres holds it fine. Store edges as plain rows and walk them with a recursive query. Blast radius and reachability are bounded-depth traversals that SQL does deterministically and fast into the low millions of edges. Put your embeddings in the same database and you have both layers in one box: similarity for recall, the graph for precision, one backup, nothing new to feed.
This is exactly the walk I took. I started on Neo4j and Voyage, the heavy and the excellent. There was no dramatic teardown: over a couple of months I simply kept not reaching for the graph, until Postgres with pgvector and a small local embedding model had quietly become the whole stack. For what I am doing, I have not needed to go back. Neo4j earns its place eventually, at monorepo scale, or for gnarly variable-depth path queries, or when graph algorithms become the point rather than a side effect. Until one of those actually bites, a dedicated graph engine is a lot of ceremony for a question Postgres was already answering.
If there is one thing to take from my detour, it is that the impressive part was never standing up the graph. It was aiming it.
For everyday context I let the agent search, live, the way the frontier settled on, because that job is a commodity and a fresh grep beats a stale index every time. For the questions that are genuinely graph-shaped, what breaks, what is dead, what reaches what, how far the real architecture has drifted from the one I intended, I reach for a graph without a second thought, because nothing else answers them honestly. Most of the craft is simply refusing to make one tool do both jobs, and building the smallest graph that answers a real question before reaching for a heavier one.
So no, I did not talk myself out of graphs. I stopped using one as a mattress to pile all my context on, and started using it as the scalpel it is, for the few incisions text search cannot make. I built the cathedral first and learned, the expensive way, that my agents wanted a good map and a flashlight for the daily walk, and one very sharp, very specific instrument for the cuts that matter.
Owning the instrument was never the hard part. Knowing exactly where to point it is the whole job.
See RepoGraph, the instrument this essay describes →Further reading