Generated code is one way to end up responsible for code nobody remembers writing. Legacy
enterprise work is the older way, and it is worse: the code is a decade old, the domain
vocabulary is undocumented, the stack is unfamiliar, and you are on the third context
switch of the day. Here is the part of that load a derived map takes off you — and the part
it honestly does not.
Point by point
Six things that make the load, five that a graph can carry.
“There is a lot of business terminology that needs to be understood to grasp the logic.”
A glossary that fails a build when it goes stale
The standard advice is to keep a glossary in Notion or Obsidian. It works for about a
quarter. Nothing tells you the day it stops being true, so it decays into a document
people quietly stop trusting.
Elevator is that glossary with two differences: each term
carries a cr: pointer at the code that implements it, and --drift checks the pointer still resolves and that identifiers named in
the description still exist in the file they claim. --code-map catches
the classic enterprise smell — the same code claimed by two differently-named
concepts, which is how you discover two teams named one thing twice.
elevator --check--drift--code-mapMCP overview
“I'm obliged to navigate between the high-level picture of how everything works, down to
the low-level.”
Three altitudes, one vocabulary
This is the whole design. The domain floor is Elevator: Categories, Features, and the
verbs under them. The structural floor is the graph: what this folder contains, what
it calls, what depends on it. The symbol floor stays with your LSP, which is already
good at it.
The ride between floors is trace A B — the shortest dependency path with
the relationship kind of every hop. “Draw the path from the UI button to the database
change” is the right instinct; drawing it by hand is what makes it a two-hour job that
is wrong next month. nao emits it as Mermaid or DOT from the code as it is.
overviewmaptrace
“I have to make modifications and make sure they're doing as expected, on systems I
don't even really understand.”
A safety net you don't have to build first
The advice — write characterization tests around the scary code before touching it —
is correct and expensive. Before paying for it, it is worth knowing whether a net
already exists: tests_for answers which tests actually reach the entity,
directly or transitively.
Then impact gives the blast radius level by level, and assess_change diffs your working tree against a git ref — per-entity
complexity deltas, added and removed entities, new smells — so “is this change bigger
than it looks?” has an answer before review, not after.
Read it honestly: call resolution is 93.7% recall at
98.6% precision, so an empty impact result is strong
evidence, not proof. On a system you don't understand, that is still a far better
starting point than grep — and it tells you where it is unsure.
impacttests_forassess_change
“Legacy projects. So they are very complex.”
Complexity you can act on, instead of dread
All of it looks scary, which is the same as none of it looking scary. Crossing
complexity with git churn separates the two: code that is convoluted but hasn't been
touched in four years is somebody else's problem, and code that is convoluted and edited every sprint is where your bugs come from.
hotspots ranks that, quality names what is wrong there — God
Class, Dispatcher, Feature Envy, Shotgun Surgery — and the composite score turns
“this codebase is a mess” into a list of five files, in order.
hotspotsquality
“I'm constantly switching between those three projects.”
Partly. The reload, not the switch.
Nothing here fixes context switching — that is a scheduling problem, and blocking your
week by project is still the right answer. What a map removes is the structural half of reloading: which module this is, what calls it, where the
folder sits in the whole. That half you were reconstructing by reading, every time,
and it is one map call.
The other half — what you were mid-thought about when you got pulled away — is still a
note to your future self. No tool knows that.
mapoverviewcontext
“They are relying on Hybris technology, which I'm unfamiliar with.”
The one where the config is in the graph too
In SAP Commerce the logic is not only in Java. It is in .impex files, in
Spring XML, in local.properties — the places code search reconstructs
worst. “Follow the data, not the code” is exactly right, and it is also why most
code-intelligence tooling is blind on this stack: it indexes languages, and the
answers are in the data files.
nao ships a hand-rolled Impex parser — no tree-sitter grammar exists, SAP doesn't
publish one — so .impex files become entities in the same graph
as the Java and Groovy around them:
INSERT / UPDATE / INSERT_UPDATE / REMOVE headers become writes-to edges to the type they touch.
Reference columns become references edges, so you can see which impex depends on which catalog or type.
$macro definitions are substituted, rather than left as opaque text.
#% script lines become calls — including which ones sit behind an if: gate.
Groovy gets its own parser too: classes, methods, fields including @Field,
call sites, try/catch arms and closure bodies. A field initialised from applicationContext.getBean("…") is flagged with the bean name as an
attribute — that is a marker for a human to follow, not yet a resolved edge, and the
page would be lying if it said otherwise.
“You have not tested the workflow yourself” is the sharpest line in that whole
conversation, and nothing here fixes it. The graph is static — no runtime, no traces, no
user journey. Watch someone complete the journey for fifteen minutes; it is still the
highest-leverage thing you can do. The closest the tooling gets is Elevator's where: edges, which say which UI page a capability shows up on — and those
are authored by a human who knew.
Type-level certainty everywhere
Exact type edges exist in Rust, TypeScript, Svelte, Java, Kotlin and Python. Groovy and
Impex give you entities and call edges, no type edges — so on the Groovy half of a
Hybris codebase, impact approximates. The limits page spells out what that means before you rely on it.
The staffing question
Three legacy domains at once, billed by the hour, is an organisational choice, not a
tooling gap. A map makes each reload cheaper; it does not make the arrangement good. The
honest use of it is evidence — “here is the blast radius of what you're asking for” is a
much better argument than “this feels risky”.
Nobody wrote it. Something still has to know its shape.
Whether the author was a language model last Tuesday or a contractor in 2013, you inherit
the same problem — and the fix is the same: a map derived from the code, current by
construction, cheap enough to re-read on every context switch.