# FAQ: how code-review-graph compares This page says what code-review-graph is good at, and when it is not worth using. - [How is this different from LSP and language servers?](#how-is-this-different-from-lsp-and-language-servers) - [Isn't this just RAG?](#isnt-this-just-rag) - [Why not just grep?](#why-not-just-grep) - [When should I not use it?](#when-should-i-not-use-it) - [Does it phone home?](#does-it-phone-home) - [How do I verify it is working?](#how-do-i-verify-it-is-working) - [How big a codebase justifies it?](#how-big-a-codebase-justifies-it) - [How does it handle monorepos, git worktrees, and multiple repos?](#how-does-it-handle-monorepos-git-worktrees-and-multiple-repos) --- ## How is this different from LSP and language servers? Both build a structural model of your code. They are built for different jobs. **What LSP does better.** A language server sits on a compiler front end, so its results are type-aware: go-to-definition through generics and overloads, find-references that understands scoping, diagnostics, completions and safe renames. For a complete reference list for one symbol in one language, use the language server. **What CRG does differently.** - One graph for the whole repository. Language servers run one process per language and rebuild or revalidate per session. CRG parses with Tree-sitter, stores nodes and edges in one SQLite file (`.code-review-graph/graph.db`), and answers queries across more than 35 languages plus notebooks from one process, including cross-language edges. - It persists. `update` re-parses only changed files and their dependents; hooks and `watch` keep it current between sessions. - Review-oriented relationships: `tests_for`, execution flows, communities and risk-scored change analysis, which LSP does not model. **Trade-off.** CRG's call resolution is AST-level and heuristic, not compiler-backed. Dynamic dispatch, metaprogramming and duck typing produce inferred edges, so every edge carries a confidence tier (`EXTRACTED`, `INFERRED`, `AMBIGUOUS` or `UNRESOLVED`). LSP is more precise per symbol; CRG is broader, persistent and cheaper to query across the repository. ## Isn't this just RAG? No. RAG splits code into text chunks, embeds them and retrieves by similarity. That answers "find code that talks about X". It cannot answer "who calls X": similarity between two functions says nothing about whether one calls the other. CRG stores edges parsed from the AST: calls, imports, inheritance, test coverage. "Who calls `login()`" is a graph lookup. Embeddings are optional. They are one input to hybrid search (FTS5 BM25 plus vectors) used to find a starting node; traversal then follows edges. The text embedded per node is its name, parent, signature, a capped docstring summary, directory and language, not the function body. The benchmark that shows the difference is multi-hop retrieval: natural-language query, anchor node, then one hop (`callers_of`, `tests_for`, ...). CRG scores 0.909 across 11 tasks on 6 repositories (see [REPRODUCING.md](REPRODUCING.md)). Similarity retrieval has no second hop. **Where RAG-style search is better:** conceptual questions over prose, comments and docs ("where is rate limiting discussed?"). CRG's own keyword ranking is a known weakness (see the limitations in the [README](../README.md#limitations)). ## Why not just grep? Claude Code deliberately ships without a code index. Agentic search (glob, grep, targeted reads) is as fresh as the working tree, has no staleness failure modes and needs no setup. For one-hop questions ("where is `parse_file` defined?") it works well and CRG will not beat it by much. The gap is multi-hop structural questions, where each hop costs another round of grep, read and reasoning: - Impact radius: "what could break if I change this file?" needs callers, dependents and their tests. One `get_impact_radius_tool` call returns all three. - Callers of callers: `traverse_graph_tool`, or repeated `query_graph_tool(pattern="callers_of")`, instead of grepping each intermediate name. Grep matches text, so overloaded or re-exported names give false hits the agent has to read to rule out. - Tests for: `query_graph_tool(pattern="tests_for")` maps code to tests through parsed edges and naming conventions; `detect_changes_tool` adds transitive coverage. A row found by name, or by a PHP call the graph could not pin to one class, carries `inferred_by` and is a guess, not coverage. Grep only finds tests that mention the name literally. - Affected flows: "which execution paths does this change touch?" has no grep equivalent. The graph also persists. Agentic search re-derives the same structure every session. One caveat on the numbers: the whole-corpus token-reduction figures (about 63x median, 35x to 358x range) compare a graph response with reading the whole corpus, not with a skilled agentic-grep session. Against an agent that greps and reads the top 3 files, the median is 6.65x (range 4.2x to 72.6x, across 18 questions on 6 repositories); see [REPRODUCING.md](REPRODUCING.md) for what each benchmark measures. For single-hop lookups in a small repository, grep is cheap and good. ## When should I not use it? - Repositories under a few hundred files. An agent can read what it needs directly; the graph's structural metadata is overhead a small repository does not repay. See [How big a codebase justifies it?](#how-big-a-codebase-justifies-it). - Trivial single-file changes. The review response carries impact-radius edges and source snippets, which can exceed a one-file diff. The formal `token_efficiency` benchmark reports ratios below 1.0 for small commits (see [REPRODUCING.md](REPRODUCING.md)). - One-off questions on a repository you will not revisit. The payoff comes from reuse across queries and sessions. - Flow detection on JavaScript and Go. Entry-point detection is reliable mainly for Python and PHP/Laravel patterns (see the README limitations). ## Does it phone home? No. There is no telemetry. The graph is a SQLite file in your repository, and build, review, search and the MCP server run locally. `serve --http` binds to `127.0.0.1` by default. The only network activity is opt-in: - Local embeddings (`pip install "code-review-graph[embeddings]"`) download the `all-MiniLM-L6-v2` model from Hugging Face on first use. Your code stays on the machine. - Cloud embeddings (OpenAI-compatible, Google Gemini, MiniMax, Voyage AI) send the embedded text (node names, signatures, docstring summaries and file paths) to the provider you configure through environment variables. CRG prints an egress warning on stderr until you set `CRG_ACCEPT_CLOUD_EMBEDDINGS=1`; the warning is skipped when the endpoint is localhost. The context history log (`CRG_CONTEXT_HISTORY=1`) is off by default and is a local file only. See [LEGAL.md](LEGAL.md). ## How do I verify it is working? 1. Check the setup: ```bash code-review-graph doctor ``` It changes nothing. It checks every copy on your `PATH`, the version each MCP entry and hook runs, and whether the graph is there and built at HEAD. It ends with `All good.`, or names each problem with a fix and exits 1. 2. Check the graph: ```bash code-review-graph status ``` It prints `Nodes`, `Edges` and `Files`. If you have not built yet, it prints `No graph found at ` instead. Zero nodes means the build did not run (`Last updated: never`) or found nothing to parse. 3. See the savings on a real change. Edit something, then: ```bash code-review-graph detect-changes --brief ``` This prints the risk summary, the top three things to review, and the Token Savings panel against the existing graph without re-parsing. Add `--verify` to compare the estimate with the `cl100k_base` tokenizer (`pip install tiktoken`). If the graph may be stale, `code-review-graph update --brief` re-parses changed files first and prints the same panel. 4. Check the MCP wiring. In Claude Code run `/mcp` and confirm `code-review-graph` is connected. Ask something structural ("what calls `parse_file`?") and watch the assistant call `query_graph_tool` instead of grepping. If any step fails, see [TROUBLESHOOTING.md](TROUBLESHOOTING.md). ## How big a codebase justifies it? - Below a few hundred files: marginal. The graph works, but an agent can hold most of the repository in context, and for trivial diffs the structural response can cost more tokens than it saves (see [When should I not use it?](#when-should-i-not-use-it)). - A few hundred files and up: the six evaluation repositories (express, fastapi, flask, gin, httpx and code-review-graph) show 35x to 358x reductions on whole-corpus questions, with the caveat above about the baseline. - Multi-thousand-file repositories and monorepos: the strongest case. No agent can read the corpus per question (FastAPI alone is about 950k tokens of source), and incremental updates keep the graph current. File count is only one axis. A 300-file repository you review daily benefits more than a 3,000-file repository you touch once. ## How does it handle monorepos, git worktrees, and multiple repos? **Monorepos.** One graph per repository root. Commands find the root by walking up to the nearest `.git`, and in git repositories only tracked files and untracked files git does not ignore are indexed, so gitignored build output is skipped. Use `.code-review-graphignore` to exclude other paths (`vendor/**`, generated code), or pass `--repo ` to point a command at a specific directory. Imports between npm, yarn and pnpm workspace packages resolve to the package's source files, so `importers_of` and the impact radius cross package boundaries. **Git worktrees.** Each worktree is its own root and gets its own `.code-review-graph/` matching its checkout. Do not share one database across worktrees at different commits. The pre-commit hook skips linked worktrees unless `CRG_HOOK_WORKTREES=1` is set, so a commit there does not build a second graph by accident. The edit hook updates the graph of the repository that holds the edited file, so a session in the main checkout that edits a worktree updates the worktree's graph, once you have built one there. To keep the database outside the working tree (ephemeral workspaces, network shares), pass `--data-dir ` to `build` or `update` (the path is saved in the registry, so later commands find it), or set `CRG_DATA_DIR`. For parallel PR reviews in one stdio MCP session, use the opt-in `code-review-graph serve --multi-worktree --repo ` mode. Each tool call must pass the linked worktree root in `repo_root`; the server routes it to a child MCP process dedicated to that worktree. The router authorises only the primary root and active linked worktrees from the same Git common directory, and starts children lazily. Do not use `CRG_DATA_DIR` with this mode; use one Registry `data_dir` per worktree when graph files must live outside the checkouts. **Multiple repos.** A registry at `~/.code-review-graph/registry.json` (or under `$CRG_HOME`) lets MCP clients search across projects: ```bash code-review-graph register ~/work/api --alias api # add a repo (optional alias) code-review-graph repos # list registered repos code-review-graph unregister api # remove by path or alias ``` Once registered, `list_repos_tool` and `cross_repo_search_tool` work across all of them. One HTTP server can also answer for all of them: `code-review-graph serve --http --multi-repo` serves every registered repository and loads the embedding model once. Each tool call must then pass `repo_root`, the absolute path of a registered repository; a call without one, or naming a repository that is not registered, is refused. To keep several graphs current, the daemon watches registered repositories as child processes: ```bash crg-daemon add ~/work/api --alias api crg-daemon start crg-daemon status ``` Also available as `code-review-graph daemon start|stop|status`. See [COMMANDS.md](COMMANDS.md) for the daemon reference. **A folder of sibling repos, or nested repos.** Build each repo you want a graph for, inside that repo. The edit hook that `install` writes for Claude Code, Qoder and CodeBuddy then updates the graph of the repo that holds each edited file, even when the session started in the outer folder. It never builds a graph for a repo that has none, unless the session started in that repo. MCP tool calls still act on the server's default repo unless they pass `repo_root`.