Back to the blog
HELIX / ENGINEERING GRAPHS

GraphRAG for codebases: how to answer engineering questions with relationships

A practical retrieval architecture that combines knowledge-graph traversal, code evidence, and language models for better codebase answers.

GraphRAGKnowledge GraphsLLMCodebase Understanding
A question pulse travels through a selected subgraph of code, services, people, and tests before becoming a grounded answer.

Ask a codebase chatbot, “Do we use Stripe?” and it may return every file containing the word stripe. Ask, “What does the API look like?” and it may summarize one central module while missing routes, consumers, authentication, persistence, and failure paths.

The problem is not always the language model. It is the retrieval architecture.

Engineering questions are relational. GraphRAG gives the model a connected subgraph instead of a bag of similar text.

Vector retrieval and graph traversal solve different problems

Vector search is good at finding semantically similar passages. It can locate documentation about payments even when the word “Stripe” is absent. It struggles when the answer depends on joining several records.

Graph traversal is good at following explicit structure:

text
Checkout API
  → CALLS → Billing Adapter
  → USES → Stripe SDK
  → CONFIGURED_BY → STRIPE_SECRET_KEY
  → CHANGED_IN → PR #42
  → OWNED_BY → Commerce Platform

The strongest system uses both. Semantic retrieval resolves language and finds candidate evidence. The graph establishes paths and provenance.

Classify intent before traversing

Different questions need different relationships.

Question intentPrioritize
UsageUSES, IMPORTS, CALLS, CONFIGURED_BY
ArchitectureCONTAINS, EXPOSES, DEPENDS_ON, PERSISTS_TO
HistoryCHANGED_IN, INTRODUCED_BY, LED_TO, SUPERSEDED_BY
OwnershipOWNED_BY, REVIEWED_BY, HAS_EXPERTISE_IN
RiskAFFECTS, COVERED_BY, INCIDENT_IN, DEPLOYED_TO

This prevents a common failure: answering a usage question with a structural CONTAINS edge. A repository containing a file does not prove that a technology is used.

Resolve entities with aliases and context

“Billing,” “payments,” and “Stripe” may refer to a domain, service, provider, package, or environment variable. Resolve candidates using names, aliases, file paths, descriptions, and the active repository scope.

Do not silently choose when two candidates are equally plausible. Ask a short clarifying question or present the alternatives.

Build a bounded, explainable subgraph

The full graph is too noisy for a prompt. Construct a subgraph around the resolved entities:

  1. select relationship types from the question intent;
  2. traverse one or two hops first;
  3. expand only when the path adds decision value;
  4. rank direct observations above inferred edges;
  5. penalize stale and duplicate evidence;
  6. preserve path direction and source references.

The language model receives structured facts, not raw database rows:

json
{
  "claim": "Checkout uses Stripe for payment intents",
  "path": ["Checkout", "CALLS", "Billing Adapter", "USES", "Stripe SDK"],
  "evidence": ["src/billing/stripe.ts:18", "package.json:42"],
  "confidence": 1.0
}

Let the model explain, not invent

The model should synthesize the supported paths into natural language, distinguish facts from inferences, state uncertainty, and suggest useful follow-up questions.

It should not manufacture missing relationships or make a generic match sound causal. If the graph supports only “the repository contains a Stripe migration,” the answer should say exactly that and explain what evidence would establish active use.

Make conversation cumulative

Engineering questions are rarely isolated.

User: Do we use Stripe? Helix: Yes. Checkout calls the Billing Adapter, which uses the Stripe SDK for payment intents and webhook verification. User: What breaks if we replace it?

The second question depends on the entities and claims established in the first. Preserve a bounded conversation state with resolved entities, selected repository, prior claims, and evidence IDs. Do not resend an unbounded transcript.

Evaluate answers at the claim level

Generic “helpfulness” scores hide retrieval failures. Evaluate each claim:

  • is it entailed by the supplied paths?
  • is the relationship type appropriate to the question?
  • can the user open the cited evidence?
  • did the answer disclose inference and uncertainty?
  • did it omit a higher-value path already present in the graph?

These evaluations feed a learning loop. Corrections improve entity aliases, ranking, traversal policies, and source quality.

The interface should feel simple

The architecture can be complex. The experience should not be.

Lead with a direct conversational answer. Show a small evidence count. Let the user expand one evidence surface to inspect paths, files, pull requests, and decisions. Offer follow-ups grounded in the current subgraph.

GraphRAG works when the graph disappears into a better explanation—while remaining available whenever someone asks, “How do you know?”