A codebase has several structures at once

Consider a relatively simple question:

Where is authentication implemented?

A lexical search might find files containing auth, login or token. An embedding search might find semantically related code even where those exact words do not appear. A symbol index can identify implementations of a particular interface. A call graph can reveal which components ultimately reach an authentication service. Git history might show that a seemingly unrelated middleware file changes whenever authentication logic changes. And an architectural document could tell us that authentication is intentionally split between an edge service and an identity service.

All of these answers describe the same repository. None is the repository.

This is why current research on repository retrieval is becoming more interesting than a simple graph-versus-RAG argument. Recent benchmarks of coding-agent context retrieval show that different retrieval families perform better on different repository questions. Compact repository maps, meanwhile, hold up far better than you would expect once the context budget gets tight.

Which is reason enough to distrust any architecture that claims one representation solves repository understanding.

Where graphs genuinely help

There are nevertheless problems for which graphs are unusually natural.

Suppose an interface changes. The interesting question is no longer:

Which files mention this interface?

It becomes:

What depends on it?

That is relational. A function calls another function. A module imports another module. A class implements an interface. A test exercises a component. A package participates in a build target.

These relationships form paths, and paths are precisely what graphs represent well.

Several systems exploit this directly, and repository-level graph representations measurably improve dependency-heavy tasks. They also make clear that a graph is not a switch that suddenly solves repository reasoning.

A particularly defensible approach is to build deterministic graphs from evidence such as imports, build targets, tests and static structure. These edges represent facts that can often be reproduced mechanically.

But the situation changes once we move from code graphs to knowledge graphs.

The seductive part of semantic graphs

A structural graph can tell an agent that validateContact calls normalizeAddress. But perhaps the real question is:

Do we already have email validation somewhere in this repository?

The relevant function could be called checkContactFormat. No lexical match is necessary. There may not even be an obvious call path from the code currently being edited.

This motivates adding a semantic layer: concepts, domain entities and system capabilities.

That is close to the architecture we are experimenting with in ForgeAI. The current design separates structural repository information from domain-level concepts and higher-level capabilities, so the system can ask what the software already knows how to do rather than only which symbols exist.

But there is a cost hidden inside that abstraction. Someone — or some model — has to decide that checkContactFormat belongs to the concept email validation.

Once we allow inferred semantic edges, our graph stops being a representation of facts and becomes a mixture of facts and hypotheses. That may still be extremely useful. But the distinction should not disappear.

Why embeddings are hard to replace

Vector retrieval solves a different problem, and solves it well. Embeddings are bad at telling us with certainty that function A calls function B. But they are useful when we do not know the vocabulary used by the repository.

A developer asks about "billing retry logic". The code calls it recoverFailedCharge. Semantic retrieval can bridge that gap without requiring someone to have previously encoded an IMPLEMENTS -> billing_retry edge.

This makes embeddings attractive for discovery. Graphs become attractive once relationships matter. Lexical search remains attractive when identifiers provide strong clues. And plain filesystem exploration remains useful when the agent needs to understand local implementation details.

The most credible direction is therefore not graph versus embeddings. It is hybrid retrieval.

There is another problem: graphs go stale

Every derived representation introduces synchronization. Rename a symbol. Move a class. Change a dependency. Split a service. Modify runtime configuration.

Some graphs can be updated deterministically and cheaply. Others require semantic reinterpretation. The more intelligence we encode into the representation, the more expensive maintaining it may become.

This creates an architectural tradeoff that is easy to underestimate. You can spend computation before the agent starts working, continuously maintaining a rich repository model. Or you can spend computation during the task, allowing the agent to explore the raw repository.

Neither is free. A system working on millions of lines of code may benefit enormously from precomputation. A small repository may not.

The likely answer is adaptive retrieval

The most interesting current direction is to avoid forcing the agent to choose one retrieval paradigm at all.

When the agent asks "what calls this?", use structural edges. When it asks "where have we solved something similar?", use semantic or procedural retrieval. When it asks "what breaks if I change this?", combine dependency structure, tests and possibly history. When it asks "does this capability already exist?", use a mixture of semantic retrieval, domain knowledge and structural evidence.

The engineering problem becomes deciding which evidence source should answer which question.

The real repository map

The metaphor of a "map of the codebase" is useful, but maps themselves provide the counterexample. There is no best map of Earth. A road map is excellent for driving and terrible for understanding rainfall. A geological map shows relationships that a political map deliberately ignores. A subway map distorts physical geography to make connectivity easier to reason about.

Codebases are the same. A call graph, an embedding index, a symbol table, Git history and a domain model are different maps of the same territory.

So I no longer think the most interesting question is whether coding agents need knowledge graphs. They probably need graphs for some things. What they need more is the ability to know when a graph is the right map to use.