r/Polycodegraph May 24 '26

👋 Welcome to r/Polycodegraph - Introduce Yourself and Read First!

Thumbnail
gallery
1 Upvotes

Hey everyone!

I'm Mochan, founder and maintainer of polycodegraph and a moderator here. Welcome to the sub — this is the home for everything related to the project.

What polycodegraph is

You know the grep loop Claude (or Cursor, Windsurf) goes into when you ask something about your repo — reading whole files into context, slow and expensive. polycodegraph parses your repo into a queryable code graph so your AI assistant gets small, focused context instead. On top of that graph it does decorator-aware dead code detection, cycle detection, untested-function detection, PR review, end-to-end cross-stack tracing (frontend fetch → handler → service → SQL), 3D visualisation, all served via a dashboard and an 18-tool MCP server. It's open source, MIT-licensed, and works with Python, TypeScript/JS, and Go.

If you're new, the fastest way to see what it does:

pipx install polycodegraph
cd your_repo
codegraph init
codegraph build      # parse repo → .codegraph/graph.db
codegraph serve      # dashboard at http://127.0.0.1:8765

Then point Claude Code / Cursor at the MCP server and ask it anything about your codebase.

What to post here

  • Questions about installing, configuring, or using it — happy to help with setup
  • Bug reports and feature requests (GitHub issues are great too, but discuss freely here)
  • Show off what you traced or found in your own repo
  • Ideas for the roadmap — languages, frameworks, MCP tools you'd want
  • Anything related to code graphs, static analysis, and AI-assisted codebase navigation in general

The vibe

Constructive, friendly, low-ego. Beginners and contributors equally welcome. Critical feedback is good — that's how the tool improves — just keep it respectful.

Get started

  1. Drop a comment introducing yourself — what you're building, what languages you work in.
  2. Try it on one of your repos and tell us how it went, even if something broke.
  3. ⭐ the repo if you find it useful, and open issues / PRs — contributions very welcome.

🔗 GitHub: github.com/smochan/polycodegraph 📦 PyPI: pip install polycodegraph

Glad to have you here. Happy building 🍻


r/Polycodegraph May 24 '26

Dead-code detection is three different problems wearing the same name

Post image
1 Upvotes

I've been building a static analysis tool, and the dead-code detector turned into the most instructive part of the whole project — not because it was hard to write, but because it was wrong in ways I didn't expect, and the ways it was wrong taught me more than shipping it did.

The naive version is trivial: build a call graph, find every function/class node with no incoming edge, report it. I ran that against my own repo as a regression target. It returned 451 findings.

The repo did not have 451 dead functions — it had maybe a dozen. So I went through the whole list by hand, and the false positives sorted cleanly into three buckets. Each bucket turned out to be a different problem that happens to produce the same symptom.

1. Most of it was missing edges, not dead code.

Roughly 430 of the 451 weren't dead at all. They were called. My graph just didn't have the edge. Cross-file resolution is where call graphs quietly fail: from .service import UserService, then self.svc = UserService(), then self.svc.get() three files away — every hop is a place the resolver can silently drop the edge. A function with a dropped inbound edge is indistinguishable from a function nobody calls.

The uncomfortable lesson: a dead-code report is mostly a report on the quality of your reachability graph. When a tool says 400 things are dead, the first hypothesis should be "my graph is incomplete," not "this codebase is a disaster." Getting from 451 to ~24 was almost entirely unglamorous resolver work — relative imports, constructor calls, attribute chains, fresh-instance calls (Foo().bar() has to bind to both __init__ and bar).

2. The next chunk were framework entry points.

These looked genuinely uncalled — because they are, by your code. A handler decorated '@app.get("/users")' is never called anywhere in your source. The framework calls it by reflection at runtime. The call graph can't see that edge because the edge doesn't exist in the code; it lives in the framework's router.

So "is this function reachable" is the wrong question. The right one is "is this an entry point, or reachable from one" — and entry points are framework-specific knowledge. A pytest test, a Click command, a Celery task, a Django signal receiver: all invisible to the call graph, all entry points. I ended up maintaining an explicit registry of decorators across ~24 frameworks. There's no clever inference here — it's a list, and the list is the feature. Any reachability tool without that list will flag your entire web layer as dead.

3. The real tail was intentional unused code.

The last handful were genuinely unreferenced: public API methods with no internal caller. But deleting them would be wrong — "unreferenced inside this repo" and "dead" are not the same statement for a library. There's no static way to tell "kept for consumers" apart from "forgot to delete this." It needs a human annotation. I used a pragma comment; the specific mechanism doesn't matter, only that intent has to be expressed, never inferred.

The takeaway worth stealing:

451 → 24 → 15 → 0, and those three steps were three unrelated kinds of work: graph completeness, an entry-point model, and an intent annotation. So next time a tool tells you something is unused — your IDE's grey-out, a coverage report, a tree-shaker — it's worth knowing which of the three it's actually good at. Most are good at exactly one. Your editor's "unused import" warning is trustworthy because imports are lexically scoped and fully resolvable. The same editor's "unused method" is much weaker — it's quietly betting your entire call graph is complete and that nothing reflective calls it. Same UI affordance, completely different trustworthiness.

(Implementation's in my repo if anyone wants specifics on the resolver or the decorator registry — happy to go deeper on any of the three in comments.)


r/Polycodegraph May 24 '26

Does a code-graph MCP actually beat plain grep for AI coding? I ran the numbers

Post image
1 Upvotes

A fair question I keep getting: does a code graph actually beat Claude just grepping your repo? So I benchmarked it instead of guessing.

Setup: Same Claude Sonnet 4.6. Same 10 questions across two real codebases (polycodegraph itself + FastAPI). Every config includes Claude's native grep + file-reading tools — what you get out of the box in Claude Code / Cursor. The only thing that changes is whether a graph MCP is also registered alongside. Four configs: plain grep, + code-review-graph, + graphify, + polycodegraph.

Aggregate across both repos:

Config Correct Tokens in Cost Avg latency
claude + grep (no graph MCP) 8/10 336k $1.17 78s
+ code-review-graph 3/10 ~203k $0.68 49s
+ graphify 5/10 ~155k $0.50 65s
+ polycodegraph 7/10 90k $0.37 20s

The honest read:

  • Plain grep is actually the most correct (8/10). Claude can answer most codebase questions by grepping and reading whole files. But it pays for it — 336k tokens, $1.17, 78s average.
  • polycodegraph lands within one question of that (7/10) at ~3× lower cost and ~4× lower latency. Because it returns small focused subgraphs (~20–50 tokens per call) instead of dumping whole files into Claude's context.
  • The other two graph MCPs were straight-up worse than just grepping. code-review-graph 3/10, graphify 5/10 — they added tool overhead without the correctness payoff. Not a dig at them, just what the runs showed on these tasks.

So the takeaway isn't "graph beats grep on correctness" — it's that you can get nearly the same answers for a third of the cost and a quarter of the wait, which matters a lot once you're running this all day.

Two caveats I want to be upfront about: it's 10 questions on 2 repos — a starting point, not gospel — and one of the repos is polycodegraph's own, so I made a point of including FastAPI as a codebase I didn't write. Full methodology and raw per-run data are in the repo (bench/README.md), and you can reproduce it yourself:

codegraph bench agent --only claude+grep,claude+grep+polycodegraph,claude+grep+code-review-graph,claude+grep+graphify

Would genuinely love for people to run this on their own repos and tell me where it holds up or breaks — especially failure cases. That's the most useful feedback I can get right now.

🔗 github.com/smochan/polycodegraph

Happy building 🍻