Understanding & reverse-engineering legacy code¶
Legacy systems share a common problem: the code still runs the business, but the people who wrote it — and any accurate documentation — are gone. Before you can safely change such a system, you have to rebuild a mental model of it. Understand exists to do exactly that: it parses your source into a cross-referenced database, then lets you read the structure that is actually there rather than the one you hope is there.
This page is an opinionated tour that routes you into the concrete how-tos in the right order.
Legacy often means old languages
Understand analyzes 17+ languages, and the list deliberately includes ones common in long-lived systems — Ada, Fortran, JOVIAL, Pascal, assembly, VHDL, and BASIC alongside modern C/C++, C#, Java, Python, and Rust. A mixed old/new codebase lands in a single project database, so you can trace calls and dependencies straight across the boundary. See Working across mixed-language codebases.
1. Get an accurate model first¶
Reverse engineering is only as trustworthy as the parse behind it. Analyze the code the way it is actually built so cross-references are complete. For C/C++ especially, match your real compiler and flags — see Accurate C/C++ projects, Build from CMake, and Fix parse errors.
2. Orient yourself¶
Start with the 10-minute tour. The core moves for legacy code:
- Information Browser — pick any function or type and see what it calls, what calls it, and everywhere it's used. Expanding Called By several levels deep is how you discover who actually depends on a routine nobody claims to own.
- Browse Mode — turn the source into clickable links so you can walk the code the way execution does.
- Find things fast — locate an entity by name, kind, or metric when you only have a fragment to go on.
3. See the shape¶
Undocumented code becomes legible as a picture. Right-click an entity for graphical views and pick the one that answers your question — call trees, control-flow, dependency graphs, and more are all in the graph catalog. Use Which graph should I use? to choose. To reason through one routine's logic path by path — without building or running the system — use the Virtual Debugger.
4. Recover the architecture¶
For a system-level map, use dependencies to see how files and folders actually reference each other, then build an architecture to name the subsystems you discover. If the module structure lives in your version history rather than the folder layout, you can auto-generate architectures from Git.
5. Find the risk and the rot¶
- Where is my risky / complex code? — the most complex, largest, and most-changed units are where legacy surprises hide. (New to the metrics themselves? Start with What metrics does Understand have?.)
- Find unused / dead code — legacy systems accrete code that nothing calls; identifying it shrinks the surface you must understand.
- Git blame & file history — see when and why a puzzling line arrived, using history you already have.
6. Capture what you learn — without touching source¶
As you rebuild the model, record it in place. Annotations let you attach notes to entities and lines without modifying the source, so the next engineer inherits your findings instead of starting over.
AI can accelerate the reading, not replace the model
Understand's AI features chat over the same cross-referenced database, so answers are grounded in your real code rather than guessed. See Why Understand with AI.