Koragraph

Graph vs grep vs embeddings

Text search finds strings that look alike and vector search finds text that means something similar, but only a resolved graph can answer what is actually connected to what, which is the question code work turns on.

There are three different ways to find code. Text search finds strings of characters that look alike. Vector search finds text that means something similar. A graph finds what is actually connected to what. All three are useful, but they answer three different questions, and most of the real work of understanding a codebase turns on the third one.

Three questions that sound the same and are not

Suppose you are standing in front of a large, unfamiliar codebase with a change to make, and you need to find your way around. You might ask any of three questions that feel similar on the surface. Where does this exact name appear? What code is about this idea? What actually depends on this thing? They sound like three phrasings of the same request. They are not. Each is best answered by a different tool, and using the wrong tool for a question is the quiet cause of a great deal of wasted time and a fair number of bugs.

The three tools are text search, usually called grep after the classic command line program; vector search, usually called embeddings after the technique underneath it; and graph traversal, which walks a map of the code’s real connections. The honest way to understand them is not to crown a winner. It is to see clearly what each one is measuring, because that is what decides which question it can truthfully answer.

Grep: matching the letters

Text search looks through your files for a run of characters that matches what you typed. In its plainest form you give it a word and it hands back every line that contains those exact letters in that exact order. A richer form lets you describe a pattern rather than a fixed word, using a small notation called a regular expression, so you can ask for things like any line that starts with the word import, or any word that ends in the letters Handler. But the thing being matched is always the same: characters, on the page, as written.

What grep is good at is precisely this literalness, and it should not be undersold. It is fast, it is exact, and it is completely predictable. If you know the exact name of a function and you want every place that name is written, grep gives you all of them and nothing else, in a moment, with no guessing. When you have just renamed something and want to be sure no stray mention of the old name is left, grep is the correct tool and no clever alternative improves on it. Exactness is a real virtue, and for questions that are truly about the text, it is exactly the virtue you want.

Its limits are the flip side of the same coin. Grep sees letters and nothing beneath them, so it cannot tell apart two different things that happen to share a name, and it cannot connect two related things that happen to be named differently. Search for a common word like process and you will get a method on one class, an unrelated variable somewhere else, that same word inside a comment, and a fragment of it buried in the middle of a longer word, all mixed together, because to grep they are the same characters. It also cannot follow anything. It has no idea that one function calls another, because a call is not a matter of the two names sitting near each other in the text. Grep answers where do these letters appear, and only that.

Embeddings: matching the meaning

Vector search was built to get past the literalness of text search. The idea rests on a technique called an embedding. A model reads a piece of text and turns it into a long list of numbers, and it is trained so that texts with similar meaning end up with similar lists. You can picture each list as a point in space, so that two passages about the same idea land near each other even if they share not a single word, and two passages about different ideas land far apart even if they reuse the same words. Searching, then, is a matter of turning your question into such a point and finding the stored points nearest to it.

This is genuinely powerful for the question it fits. Ask, in plain language, where is the code that retries a failed network request, and vector search can surface a function that does exactly that even though it never uses the words retry or failed, because it captured the meaning rather than the spelling. When you do not know what something is called, or when the idea you are chasing has many possible names, this ability to search by meaning is exactly what you want, and grep cannot do it at all. Vector search shines when the question is fuzzy and the vocabulary is uncertain.

But its strength is also the source of its weakness. Similarity is not the same as connection, and it is not exactness either. Vector search gives you the code that is most like your question, which is a matter of degree and of guesswork, and it can be confidently wrong. It may return two functions that read almost identically and are used in completely unrelated corners of the system, because they resemble each other in meaning while having nothing to do with each other in fact. It has no notion of whether one thing calls, imports, or breaks another. It answers what code is about this idea, which is a real and useful question, but it is not the question of what is connected to what.

Grep matches the letters. Embeddings match the meaning. Neither of them can tell you what actually calls, imports, or would break the thing in front of you, because that is not a fact about letters or about meaning.

The graph: following the connections

The third tool is different in kind, not just in quality. A code graph is a map of the code’s real relationships, built ahead of time by reading the code and working out its actual structure. Its nodes are the real declarations, the functions and classes and files, and its edges are the real connections between them: this function calls that one, this file imports that one, this class inherits from that one, this service sends a request to that one. Once that map exists, you do not search it by matching characters or by measuring similarity. You walk it, following edges from one node to its true neighbors.

This is the only one of the three that can answer a question about connection with certainty rather than resemblance. Ask what calls this function and the graph does not guess and does not approximate. It returns exactly the set of functions that have a call edge pointing at this one, because that fact was resolved when the graph was built. Ask what would be affected if I change this and the graph can follow the edges outward to every node that depends on it, directly or at a distance. That reach outward through the connections has a name of its own, the blast radius, and it is a pure graph question, because it is nothing but a walk over edges.

The cost of the graph is that it must be built first, by a pass that parses the code and resolves every edge, and that it is only as good as that resolution. But once it exists, it answers the class of question the other two tools cannot touch, and it answers it with the same certainty grep brings to text, applied to relationships instead of letters.

A worked example, one question at a time

Make it concrete with a single function, called charge, that bills a customer, and walk three real questions through the three tools.

First: are there any leftover mentions of the old spelling before I finish this rename? This is a question about the text itself, so grep is the right answer. It finds every literal appearance of the string, in code and comments alike, quickly and completely. Vector search would be worse here, since it might helpfully offer up things that merely mean something similar, and a rename does not care about meaning. The graph would be worse too, since a comment mentioning the old name is not a connection it tracks. For this one, grep wins outright.

Second: I want to understand how billing works in general, but I do not know what any of the functions are called. This is a fuzzy question about an idea, and vector search is the right answer. Describe billing in plain words and it surfaces the functions that are about that, whatever their names, giving you a foothold. Grep is nearly useless here, because you cannot grep for a concept you cannot spell, and the graph, while it holds the billing code, gives you no way in without a node to start from. For this one, embeddings win.

Third: I am about to change charge and I need to know everything that will be affected. This is the question that decides whether the change is safe, and only the graph can answer it truthfully. It follows the call edges into charge to find every function that calls it, then follows the edges into those to find their callers, out to the full set of code that depends on this one function. Grep can find the places the exact name is typed, but it cannot follow a call it does not see and cannot reach a caller two hops away. Vector search can find code that resembles charging a customer, which is not at all the same as code that would break if this particular function changed. For this one, the graph wins, and the win is not marginal.

The point underneath all of it

The lesson of that example is a single sentence worth stating plainly. Dependence is a relationship, not a string and not a similarity. Whether one piece of code depends on another is not decided by whether their names look alike, and not by whether they read as though they are about the same thing. It is decided by whether an actual connection runs between them, a call, an import, an inheritance link, a request across a service boundary. A tool that measures letters or measures meaning is measuring the wrong thing for this question, no matter how well it measures it.

This is why so much of the frustration of working in a large codebase comes from reaching for the tool that is at hand rather than the tool that fits. The questions that matter most before a change, what depends on this, what will this break, what has to change with it, are all relationship questions, and the two tools most developers reach for by habit, text search and similarity search, are structurally unable to answer them. Not because they are poorly made, but because they were built to answer something else.

Use all three, on purpose

The right conclusion is not that the graph replaces the other two. It is that the three are for three different jobs, and a person who knows which is which reaches for the right one each time. Use grep when the question is truly about the text: an exact name, a literal pattern, a rename to double check. Use embeddings when the question is a fuzzy hunt for an idea whose vocabulary you do not know. Use the graph when the question is about connection: what calls this, what imports this, what breaks if this changes. The mistake is not preferring one. The mistake is using the same one for every question and quietly getting wrong answers to the questions it was never built for.

The three tools are not rivals. Grep for the letters, embeddings for the meaning, the graph for the connections. Skill is knowing which question you actually have.

Where this leads

Each of these three deserves its own closer look. The graph is the code knowledge graph, the resolved map of declarations and their real connections that this whole hub circles around. Vector search rests on the ideas of embeddings and similarity, worth understanding in their own right rather than dismissed. And the larger pattern of using a structured graph to feed an AI model the right pieces of a codebase has a name, GraphRAG for code, which is the natural next idea, since it is really the question of how the winner of that third question gets put to work.

Connected concepts

The code knowledge graphA code knowledge graph is a structured map of a codebase where every declaration is a node and every real relationship, calls, imports, inheritance, cross-service links, is an edge.Vector search and similarityVector search finds text by nearness in embedding space rather than by matching words, which is powerful for prose and quietly wrong for code, where the caller and the callee rarely share vocabulary.GraphRAG for codeGraphRAG for code is retrieval that walks a graph of the codebase to assemble context, following real edges out from a node instead of returning the chunks that scored highest for similarity.The context layerA context layer sits between your codebase and your agent and decides what the agent sees for a given question, which is the job Koragraph exists to do and the point every other concept here builds toward.

Where this sits

Back to the full graphThe short glossary