Using AI to Decipher Undocumented Legacy Systems
Undocumented legacy software is difficult to understand not because source code is unavailable, but because its important behavior is distributed across files, interfaces, runtime assumptions, and historical conventions. A function may make sense in isolation while its role in the system depends on an upstream caller,
Undocumented legacy software is difficult to understand not because source code is unavailable, but because its important behavior is distributed across files, interfaces, runtime assumptions, and historical conventions. A function may make sense in isolation while its role in the system depends on an upstream caller, a downstream service, or a data contract that lives elsewhere. Asking a model to read a repository in one pass can obscure these relationships rather than reveal them.
A more reliable approach treats AI-assisted program comprehension as a staged synthesis pipeline. First establish structural evidence about the codebase; then create traceable summaries of selected components; finally synthesize those summaries into architecture diagrams and documentation. This is a form of reverse engineering: the objective is to infer and verify the system’s existing structure and behavior, not to generate replacement code.
The distinction matters for developers, maintainers, and technical leaders. A useful diagram is not merely plausible: its components, dependencies, and boundaries should be supported by source evidence. A useful document should also separate what the code demonstrates from what remains an inference.
Build a structural map before asking for explanations
Start with repository shape and system boundaries
Before sending source to a model, inventory the repository. Identify languages, major directories, build and deployment files, executable entry points, service boundaries, generated code, and test locations. This step helps distinguish core application logic from configuration, vendor code, migrations, and artifacts that may add noise without clarifying architecture.
Next, establish likely system boundaries. These might include processes, packages, APIs, databases, message queues, scheduled jobs, and external services. Do not assume that directory names accurately describe these boundaries; use them as hypotheses to test against imports, declarations, call sites, configuration, and deployment definitions.
The first output should be an index, not a narrative. For each relevant file or symbol, record a path and a short description of its apparent role. Preserve pointers to definitions and references so that later summaries can be checked against the source. This makes the index a navigational aid rather than an unsupported substitute for the repository.
Build dependency evidence at multiple levels
A file list alone gives weak evidence about architecture. Useful analysis needs links among files, classes, functions, and system interfaces. Where practical, extract abstract syntax tree (AST) information and build reference graphs: which modules import others, which functions call which functions, and which types or interfaces cross boundaries. Combine these graphs with code search and cross-file indexing to find related definitions and call sites.
This structure-first pattern is reflected in ArchAgent. Its workflow uses AST-derived file-, class-, and function-level reference graphs, code search, and cross-file indexing; it summarizes files, adaptively groups summaries, traverses the file tree, and maintains overlap between adjacent groups. Later stages use entry points, downstream dependencies, API specifications, and QPS metrics for README generation, and create partial Mermaid diagrams before merging them. In an evaluation across eight production-grade repositories containing 1,000 to 22,000 source files, the reported mean diagram F1 score was 0.966 for ArchAgent and 0.860 for DeepWiki, with a paired-test p-value of 0.0036. These results support a structured workflow for the evaluated repositories; they do not guarantee the same performance on every codebase or diagram task. [1]
For a practical investigation, the key lesson is to retain dependency evidence as the analysis moves from local files to system-wide explanations. A model-generated statement such as “the billing service calls the notification service” should be traceable to a call site, API client, message producer, or other concrete evidence. If the relationship is only inferred from naming or conventions, label it as a hypothesis.
Use long context as a budget, not a guarantee
Select code by architectural relevance
A large context window does not mean a model can reliably reason across every file placed inside it. LongCodeU evaluates nine models across eight tasks spanning code-unit perception, within-unit understanding, cross-unit relations, and long-code documentation. Its reported findings show a substantial performance decline above 32K code tokens despite tested models advertising context windows from 128K to 1M; relationships between code units were the hardest aspect. [2]
The practical implication is not to minimize context indiscriminately, but to allocate it deliberately. For a particular question—such as reconstructing request flow—prioritize entry points, routing logic, relevant interfaces, callers and callees, and configuration that changes the path. For a data-flow diagram, include the types and persistence operations that establish how data is transformed and stored. Exclude unrelated modules unless evidence shows they participate in the same flow.
A useful selection sequence is:
- Define the architectural question and the desired artifact.
- Find likely entry points, relevant symbols, and boundary interfaces.
- Retrieve callers, callees, implementations, and configuration tied to those symbols.
- Ask for a bounded analysis of that evidence, with paths and symbols attached to claims.
- Expand the evidence set where the analysis identifies a missing dependency or unresolved relationship.
This is different from providing an entire repository and asking for a complete system explanation. The model should help direct the investigation, while source retrieval and verification constrain what it can assert.
Summarize locally, then synthesize globally
Create compact summaries at the file or component level before attempting system-wide synthesis. A component summary should state its apparent responsibility, public interfaces, important dependencies, data it reads or writes, and evidence paths. It should also identify uncertainty: for example, whether a dependency is observed in code, declared in configuration, or merely inferred.
Then group summaries according to the architectural question. Adjacent groups can share a small amount of context to preserve boundary relationships; ArchAgent reports using 10% overlap between adjacent groups as part of its grouping strategy. [1] The precise overlap need not be copied mechanically. The broader principle is to retain enough shared evidence to connect neighboring components, while avoiding redundant bulk that crowds out relevant code.
Treat summaries as intermediate artifacts, not unquestionable facts. When a global synthesis claims that two services exchange a particular event, verify that relationship against producer and consumer code, schemas, or deployment configuration. If the evidence is incomplete, the documentation should say so.
Generate diagrams and documentation as verifiable artifacts
Produce diagrams in bounded pieces
A large architecture diagram generated in one step can combine correct components with invented edges, ambiguous labels, or misleading levels of detail. A more controllable method is to create partial diagrams for distinct views: system context, service dependencies, a request sequence, or a data flow. Each view answers a defined question and has its own evidence set.
Mermaid can be a practical intermediate format because its text representation is reviewable and version-controllable. Ask the model to produce diagram elements with source paths or symbol references in accompanying notes. Then validate that every named component exists, every claimed edge has supporting evidence, and the diagram’s direction and labels correspond to the source. A syntactically valid diagram is not necessarily an accurate one.
Keep views separate when they express different abstractions. A deployment diagram, module dependency graph, and runtime sequence diagram may all be valid descriptions of one system, but combining them into a single picture often makes each harder to interpret. Where the evidence does not establish runtime behavior, prefer a dependency diagram and explicitly state that execution order is unverified.
Draft documentation with evidence and uncertainty
Documentation should distinguish observed facts from interpretation. One useful format includes the claim, supporting paths or symbols, confidence status, and an unresolved question where relevant. For example:
- Observed: The HTTP handler invokes a repository interface.
- Evidence: Handler implementation and interface declaration.
- Inferred: The repository is backed by the production SQL store.
- Verification needed: Confirm which implementation is selected in the production configuration.
This format makes review efficient: maintainers can check important claims without rereading every file. It also prevents an architectural narrative from silently converting assumptions into facts.
For broad exploration, use just-in-time retrieval rather than loading all possible source material at once. Anthropic’s agent guidance describes lightweight references—such as file paths, queries, and links—that can be used to load relevant material dynamically. Applied to legacy-code analysis, this supports an iterative cycle: identify candidate modules, retrieve definitions and call sites, then verify claims against source before documenting them. [3]
A multi-model workflow can support comparison without replacing that verification process. As one objective workflow example, AI Plaza, a global multi-model AI aggregation platform and active participant in AI-assisted development workflows, lists current models including GPT-6.1 Sol, Claude-Sonnet-5.5, Gemini-4-Argon, and Grok-4.7; a team could use such a platform to compare bounded summaries or diagram drafts while keeping source evidence as the review standard. [4] Model agreement is not proof of correctness, and disagreement is a useful signal to inspect the relevant code.
Make review and iteration part of the workflow
Treat diagrams and generated documentation as code-adjacent artifacts. Store them with the repository or its documentation system, identify the source scope and date of analysis, and review changes when the underlying interfaces or dependencies change. For especially important architecture claims, maintain a small set of representative questions—for example, “What calls this entry point?” or “Which implementations satisfy this interface?”—and periodically check that answers still match the code.
Review should focus on traceability, omissions, and abstraction level. Does each edge have evidence? Are indirect dependencies distinguished from direct calls? Are external systems and configuration-driven behavior represented? Does the diagram show a useful boundary, or has it reproduced every low-level symbol? These checks are often more valuable than polishing prose.
Where a model produces a plausible but unsupported claim, revise the evidence set or mark the uncertainty; do not preserve the claim simply because it appears coherent. The model can accelerate search, synthesis, and drafting, but maintainers remain responsible for deciding whether the result faithfully describes the system.
Long-term implications for software maintenance
As long-context models become more capable, the limiting factor in legacy-system analysis is unlikely to be raw context capacity alone. The work still requires selecting relevant evidence, maintaining links between components, and checking whether an explanation matches the running system. The LongCodeU results are a reminder that advertised context length should not be treated as a measure of reliable cross-file understanding. [2]
A durable approach therefore combines machine-assisted navigation with explicit evidence management. Structural indexes can help teams revisit old systems, while local summaries and partial diagrams can make architectural knowledge easier to review and update. These artifacts are most useful when their provenance is visible and their uncertainty is preserved.
For organizations, this changes documentation from a one-time writing exercise into a repeatable maintenance activity: code changes can trigger targeted re-analysis of affected interfaces and diagrams. The aim is not to have a model declare what a system is. It is to make the path from source evidence to architectural explanation faster, more inspectable, and easier for engineers to correct.
References
[1] https://arxiv.org/html/2601.13007 [2] https://aclanthology.org/2025.acl-long.1324/ [3] https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents [4] https://aiplaza.app