A few months ago I was mid-refactor on a Python codebase and grep -r gave me 12 results. I changed the function, ran the tests, and broke 4 things grep hadn't caught. Classic.
That afternoon I started building N3MO.
What N3MO does
N3MO answers one question: "what breaks if I change this?"
It parses your repository's ASTs using Tree-sitter, stores every symbol and call edge in PostgreSQL, then walks the call graph using recursive CTEs to return the full transitive blast radius of any function, method, or class.
pip install n3mo
n3mo index
n3mo impact "authenticate_user" --graph
Output:
โ IMPACT ANALYSIS
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Target: authenticate_user
โ Direct Callers (3 symbols)
โธ login_endpoint api/auth.py:12
โธ refresh_token api/token.py:23
โธ validate_session middleware/auth.py:89
โ Ripple Effects (5 symbols)
โฐโโธ POST /login routes.py:67
โฐโโธ admin_login admin/views.py:34
โฐโโธ require_auth decorators.py:12
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Total impacted: 8 references โ depth โค 3
Why Postgres as the graph store
The obvious question: why not Neo4j or a dedicated graph DB?
A few reasons:
-
It's already in most stacks. Asking a team to spin up a new DB just for code intelligence is friction. Asking them to run
n3mo setup(which starts a Docker container) is much less. -
Recursive CTEs are powerful enough. PostgreSQL's
WITH RECURSIVEhandles arbitrary-depth graph traversal cleanly. For the query volumes involved (this is a dev tool, not a production API), it's fast enough. - The data is relational. Symbols have names, files, line numbers, types. Calls have callers and callees. This is a table, not a document or a property graph. The schema is roughly:
CREATE TABLE symbols (
id SERIAL PRIMARY KEY,
name TEXT,
file TEXT,
line INT,
type TEXT -- function, class, method
);
CREATE TABLE calls (
caller_id INT REFERENCES symbols(id),
callee_id INT REFERENCES symbols(id)
);
The blast radius query uses a recursive CTE to walk calls from a given symbol outward to arbitrary depth.
The Django benchmark
I needed a real-world test case โ not a toy repo. Django is public, large, and well-structured. Here's what indexing it looked like:
| Metric | Value |
|---|---|
| Files | 3,021 |
| Symbols | ~43,000 |
| Call edges | ~181,000 |
| Cold index time | ~11 minutes |
Impact query on dispatch
|
317 references, <2s |
The biggest optimization came from fixing the call name matching query. Initial version used:
WHERE call_name LIKE '%' || s.name
This was doing a full table scan on every symbol lookup. Replaced with:
WHERE SPLIT_PART(call_name, '.', -1) = s.name
Cut indexing time from ~23 minutes to ~11 minutes on Django.
Other things N3MO does
Beyond the CLI, there are a few integrations I built:
GitHub App webhook โ installs on a repo, runs on every PR, posts a markdown blast radius report as a PR comment. Useful for catching unintended impact before merge.
MCP server โ N3MO exposes a Model Context Protocol server so AI coding tools (Cursor, Claude Desktop, Windsurf) can query the codebase structure before suggesting refactors. The idea is that if an agent knows dispatch has 317 downstream callers, it won't casually rename it.
Interactive graph UI โ --graph flag launches a vis.js visualizer in your browser with a depth slider and node highlighting. Click a node to deep-link into your local IDE.
What's next
The tool is technically complete. The current focus is on getting real teams using it and finding the rough edges.
If you work on a large Python codebase and want to try it:
pip install n3mo
n3mo setup # starts Postgres in Docker
n3mo index # parse and store
n3mo impact "your_function" --graph
GitHub App and full source: github.com/RajX-dev/N3MO
AGPL-3.0. Free for open source and projects under 15k LOC.













