enola/Compare

Why Enola, and when not Enola

Several projects turn a repository into a graph. They optimize for different things.

This page names them, says plainly where each one is the better pick, and gives you a question to ask all of us that is more useful than "how many languages do you support".

The short version

All of them answer one question. Enola answers a second one.

Every tool here answers what does this codebase look like. Enola also answers what did this change just do to it — which is why it pins a baseline, and why it has an exit code.

That is the whole difference, and it is a real trade rather than a free win. Pinning a baseline means Enola computes a snapshot on demand instead of maintaining a continuously-updated index, so it is not the tool to reach for if you want a graph that is never stale. It also means a snapshot has to be byte-reproducible, or a diff between two of them is noise rather than signal.

The projects below are well built, actively developed, and solving adjacent problems on purpose. Nothing here is a takedown.

Four open-source code graphs

What each one optimizes for, and when to pick it instead.

Written in Go, TypeScript with a Rust kernel, Python, and C respectively. All four parse with tree-sitter, persist locally, and expose the result over MCP. That is roughly where the agreement ends.

ProjectOptimizes forPick it instead of Enola if…
enola The graph plus a before/after verdict. check exits 1, so it can gate a build.
CodeGraph Returning the matching source in the payload, so the agent never opens a file. An always-live, incrementally-synced SQLite store. You want retrieval rather than a verdict, or you need an index that updates continuously as files change rather than a snapshot you take.
graphify Code alongside PDFs, images and video transcripts in one knowledge base, with community detection over the result. Your architecture knowledge lives in documents as much as in source, or you want the NetworkX algorithm library over your graph.
codebase-memory-mcp Kubernetes manifests and infrastructure-as-code next to source, a Cypher query engine, and the widest parse coverage of the four — in C, on in-memory SQLite. You want to query the graph in Cypher, index infrastructure as a first-class citizen, or need a language none of the others parse at all.

These are four different products, and only the middle layer is genuinely comparable. We measured that middle layer — storage shape, memory layout, traversal cost — by holding the extracted graph constant and modelling all four storage engines uniformly, including our own. Every result, including the axes where Enola loses, is in Four code graphs, four storage engines. Those measurements were taken on 6 August 2026 and every one of these projects is under active development, so treat the numbers as a dated snapshot and the reasoning as the part that ages better.

The question worth asking

"How many languages" is the wrong question.

Handling a language in a code graph is a ladder, and each rung is much more work than the one below it. A grammar buys you rung one. Essentially all the value for an agent is on rungs four and five — and the rung below the ladder is the one that decides most of the question.

  1. Don't open it.

    The file is not a language the tool handles, so it is not read at all. Nothing in it exists: no symbol, no edge, no finding.

  2. Parse it.

    A tree-sitter grammar exists, so you get a syntax tree instead of text.

  3. Extract definitions.

    You get symbols: functions, classes, methods.

  4. Extract calls and imports.

    You get edges, but only the ones written literally in the file.

  5. Resolve references across files.

    Now greet() in one file points at the declaration in another, and you have an actual graph rather than a pile of names.

  6. Resolve them with type information.

    Now user.profile.display_name() resolves through an import, a generic and an inheritance chain to the right method three modules away, the way an IDE's "go to definition" would.

Advertised language counts sit on different rungs, which is why they cannot be compared to each other:

ProjectAdvertisedWhich rung that number isMeasured per language?
codebase-memory-mcp158 languagesrungs 1–3 for all 158, rung 5 for 10no
CodeGraphfull extraction and cross-file resolutionrung 4, claimed uniformlyyes, 22 languages
graphify~40 languagesrung 3, rung 4 generically by nameno
enola11 code languagesrung 413 language tags across 38 repositories

Rung zero is why a checker built for one language is a different kind of thing entirely. It can be rung five for that language and rung zero for the other half of the same repository — and rung zero is silent. Discourse is a Rails backend beside an Ember frontend, 66,497 Ruby facts against 69,562 TypeScript ones, and the two halves talk over HTTP rather than imports. A Rails-only check grades the Ruby half correctly and reports nothing at all about the larger one, which reads exactly like a clean bill of health.

None of those claims is false. They are all true under a definition their authors state somewhere, and counting non-programming formats is a habit all four of us have — Enola's own supported-language list includes Terraform, Ansible, OpenAPI, GraphQL and Protobuf alongside Go and Rust. So ask two questions instead of one: which rung, for the languages I actually use? And is that measured or asserted? A tool that resolves your language by type is worth more to you than one that parses 158 and name-matches yours, and a tool that publishes 73.8% for a language is telling you more than one that publishes nothing.

The comparison most people are actually making

"We already have CI. What does this add?"

For most teams the real alternative is not another code graph. It is the checks already running on every pull request.

ToolTells you
Git diffWhich lines changed.
TestsWhether the behaviour you tested still works.
LinterWhether local rules were violated, file by file.
Code reviewWhatever a human notices, after the work is finished.
enola checkWhat the change did to the structure of the system.

A dependency cycle spans files, breaks no test, and is easy for a reviewer to miss. Coding agents can write more code than you can carefully review, and that gap is where structural damage accumulates — usually surfacing months later, when the package is too tangled to refactor.

The long versions

Both of these are on a personal blog, and both include the parts that do not flatter us.

Both are written by Enola's author, which is exactly why the method in the first one holds the input data constant and models every storage layer the same way, including Enola's own.

The fastest way to compare is to run it.

One read-only command, no baseline, no config, nothing written to disk: enola --explain /path/to/your/repo