Use cases Legacy work

Use case · the worked example

Three projects. None of them yours.

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.

  1. “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

  2. “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

  3. “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

  4. “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

  5. “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

  6. “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.

    items.impex — nodes and edges, not opaque text
    $catalog = electronicsProductCatalog
    $lang = en
    
    INSERT_UPDATE Product; code[unique=true]; name[lang=$lang]; $catalogVersion
                         ; HW1210-11        ; Camera lens     ; ;
    
    #% if: de.hybris.platform.util.Config.getBoolean("seed.demo", false)
    #%   impex.includeExternalDataMedia("demo-prices.impex");

    Impex parserGroovy parserJavaKotlinEducator (Java)

Where it stops

What a code map cannot give you.

The workflow itself

“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.