<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: luqueee</title>
    <description>The latest articles on DEV Community by luqueee (@luqueee__).</description>
    <link>https://dev.to/luqueee__</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3756662%2F6da73e99-22b4-4270-bd76-b2afde11afd1.jpeg</url>
      <title>DEV Community: luqueee</title>
      <link>https://dev.to/luqueee__</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/luqueee__"/>
    <language>en</language>
    <item>
      <title>I benchmarked a code graph against grep across 37 repositories</title>
      <dc:creator>luqueee</dc:creator>
      <pubDate>Fri, 28 Aug 2026 14:43:00 +0000</pubDate>
      <link>https://dev.to/luqueee__/i-benchmarked-a-code-graph-against-grep-across-37-repositories-1n26</link>
      <guid>https://dev.to/luqueee__/i-benchmarked-a-code-graph-against-grep-across-37-repositories-1n26</guid>
      <description>&lt;h1&gt;
  
  
  I benchmarked a code graph against grep across 37 repositories
&lt;/h1&gt;

&lt;p&gt;I've been working on Kivgraph, a local open-source code graph for coding agents.&lt;/p&gt;

&lt;p&gt;There are already plenty of code search and code graph tools around, so I wasn't very interested in proving that "graphs are better than grep".&lt;/p&gt;

&lt;p&gt;grep is extremely good when you know what you're looking for.&lt;/p&gt;

&lt;p&gt;What I wanted to test was something narrower:&lt;/p&gt;

&lt;p&gt;Can a resolved code graph answer structural questions with the same accuracy while making an agent read significantly less code?&lt;/p&gt;

&lt;h2&gt;
  
  
  The benchmark
&lt;/h2&gt;

&lt;p&gt;I put together 29 questions across 37 repositories written in Go, TypeScript, Rust, Python and Dart.&lt;/p&gt;

&lt;p&gt;The questions had hand-written ground truth and covered things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;who calls this symbol?&lt;/li&gt;
&lt;li&gt;what can reach this within two hops?&lt;/li&gt;
&lt;li&gt;what would break if I changed this?&lt;/li&gt;
&lt;li&gt;which other repository consumes it?&lt;/li&gt;
&lt;li&gt;where is this declared?&lt;/li&gt;
&lt;li&gt;show me the relevant source&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The baseline was intentionally boring: grep for the relevant identifiers, then read the matching files.&lt;/p&gt;

&lt;p&gt;The results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Kivgraph&lt;/th&gt;
&lt;th&gt;grep + reading&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Exact answers&lt;/td&gt;
&lt;td&gt;28/29&lt;/td&gt;
&lt;td&gt;28/29&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Returned tokens&lt;/td&gt;
&lt;td&gt;35,961&lt;/td&gt;
&lt;td&gt;267,980&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Median context reduction&lt;/td&gt;
&lt;td&gt;~5.95x&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The accuracy was basically identical.&lt;/p&gt;

&lt;p&gt;The context usage wasn't.&lt;/p&gt;

&lt;p&gt;Kivgraph returned around 36k tokens in total versus roughly 268k for grep + reading.&lt;/p&gt;

&lt;p&gt;But grep still won on 5 of the 29 questions, usually when the identifier was rare and already known.&lt;/p&gt;

&lt;p&gt;That result actually changed how I think about the tool.&lt;/p&gt;

&lt;p&gt;A graph shouldn't replace grep.&lt;/p&gt;

&lt;p&gt;If you already know the name of the thing you're looking for, grep is often exactly the right tool.&lt;/p&gt;

&lt;p&gt;The graph becomes useful when the question is structural.&lt;/p&gt;

&lt;h2&gt;
  
  
  A name isn't an edge
&lt;/h2&gt;

&lt;p&gt;One of the things I wanted to avoid was building relationships by matching identifiers.&lt;/p&gt;

&lt;p&gt;Two unrelated methods called &lt;code&gt;Handle&lt;/code&gt; shouldn't become connected just because they happen to share a name.&lt;/p&gt;

&lt;p&gt;For Go, TypeScript and Rust, Kivgraph resolves relationships using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;go/types&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;the TypeScript type checker&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rust-analyzer&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dart uses the Dart Analysis Server.&lt;/p&gt;

&lt;p&gt;Python is intentionally more conservative. When Kivgraph can't prove a relationship using a semantic analyzer, the built-in fallback can report it as &lt;code&gt;CANDIDATE&lt;/code&gt; rather than presenting it as an &lt;code&gt;EXACT&lt;/code&gt; relationship.&lt;/p&gt;

&lt;p&gt;That distinction matters when an agent is asking questions such as:&lt;/p&gt;

&lt;p&gt;"Who actually calls this?"&lt;/p&gt;

&lt;p&gt;or:&lt;/p&gt;

&lt;p&gt;"What could break if I change this symbol?"&lt;/p&gt;

&lt;p&gt;Text search can show occurrences. A resolved graph can tell you which occurrences represent actual relationships.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem before graph traversal
&lt;/h2&gt;

&lt;p&gt;While testing this, I kept running into another problem.&lt;/p&gt;

&lt;p&gt;Sometimes the coding agent knows exactly what it's looking for conceptually, but has no idea what the codebase calls it.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;"Where is the code that decides whether a failed request should be retried?"&lt;/p&gt;

&lt;p&gt;The implementation might use &lt;code&gt;retry&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Or it might call the same concept &lt;code&gt;requeue&lt;/code&gt;, &lt;code&gt;backoff&lt;/code&gt;, &lt;code&gt;reschedule&lt;/code&gt;, or something project-specific.&lt;/p&gt;

&lt;p&gt;That's what &lt;code&gt;find_by_intent&lt;/code&gt; is for.&lt;/p&gt;

&lt;p&gt;The agent asks the graph what the code does, Kivgraph ranks the symbols and files that are likely to implement it, and once it has an entry point the agent can switch to the resolved graph for callers, references, dependencies or blast radius.&lt;/p&gt;

&lt;p&gt;There are no embeddings and no model calls inside Kivgraph.&lt;/p&gt;

&lt;p&gt;The coding agent itself already has a pretty good idea of which implementation words might represent a concept, so it can optionally pass those as hints.&lt;/p&gt;

&lt;p&gt;I ran a separate 24-question benchmark where the question deliberately didn't contain an identifier from the answer file:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Correct&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;grep&lt;/td&gt;
&lt;td&gt;7/24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;intent only&lt;/td&gt;
&lt;td&gt;6/24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;intent + likely code vocabulary&lt;/td&gt;
&lt;td&gt;11/24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;intent + vocabulary + repository&lt;/td&gt;
&lt;td&gt;17/24&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The interesting part for me wasn't that intent search "beat grep".&lt;/p&gt;

&lt;p&gt;Plain intent didn't.&lt;/p&gt;

&lt;p&gt;What mattered was combining what the model knows about the problem with what Kivgraph knows about the codebase.&lt;/p&gt;

&lt;p&gt;The workflow I've ended up with is roughly:&lt;/p&gt;

&lt;p&gt;natural-language question → find a likely entry point → traverse resolved relationships&lt;/p&gt;

&lt;h2&gt;
  
  
  Cross-repository code
&lt;/h2&gt;

&lt;p&gt;Kivgraph indexes multiple repositories into the same graph.&lt;/p&gt;

&lt;p&gt;Every result includes the repository, path, qualified name and line range, so an agent can use the output of one query directly in another.&lt;/p&gt;

&lt;p&gt;That means questions like:&lt;/p&gt;

&lt;p&gt;"Which other repository consumes this?"&lt;/p&gt;

&lt;p&gt;don't require manually searching every repository in the workspace.&lt;/p&gt;

&lt;p&gt;This was one of the main reasons I started building it in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Kivgraph doesn't try to do
&lt;/h2&gt;

&lt;p&gt;It's not a replacement for grep.&lt;/p&gt;

&lt;p&gt;It's not a replacement for the language server.&lt;/p&gt;

&lt;p&gt;It's not another model sitting between your coding agent and the codebase.&lt;/p&gt;

&lt;p&gt;And it doesn't turn uncertain relationships into confident ones just to make the graph look more complete.&lt;/p&gt;

&lt;p&gt;The goal is mostly to give coding agents a smaller, structured view of the codebase when reading a pile of files would be wasteful.&lt;/p&gt;

&lt;p&gt;Everything runs locally and Kivgraph itself doesn't require an API key or external model.&lt;/p&gt;

&lt;p&gt;Current language support is Go, TypeScript, Rust, Python and Dart. I'm currently adding Java and C#.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmark caveat
&lt;/h2&gt;

&lt;p&gt;The benchmark harness, ground truth and captured responses are public.&lt;/p&gt;

&lt;p&gt;The corpus itself isn't.&lt;/p&gt;

&lt;p&gt;So the exact benchmark can't be independently reproduced without running the harness against another corpus.&lt;/p&gt;

&lt;p&gt;That's the biggest limitation of the numbers above, and one I'd rather state explicitly than hide behind the benchmark.&lt;/p&gt;

&lt;p&gt;If anyone wants to run it against another large codebase, I'd be very interested in seeing where the results fall apart.&lt;/p&gt;

&lt;p&gt;Kivgraph is open source:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Luqueee/kivgraph" rel="noopener noreferrer"&gt;https://github.com/Luqueee/kivgraph&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Docs and full benchmark:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://kivgraph.dev" rel="noopener noreferrer"&gt;https://kivgraph.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm also curious how other people are handling this with coding agents: where do you draw the line between grep/file reading and a persistent structural index?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>programming</category>
      <category>mcp</category>
    </item>
  </channel>
</rss>
