Analyzing a legacy application for an AWS migration means gathering enough evidence to understand how it is built, how it behaves, what it depends on, and which target options are practical. It does not mean refactoring every difficult module before you know the application’s risks or migration goal.

Use these five strategies together. Each answers a different question and produces evidence you can review with application owners. If you are assessing many applications, include business value and cross-application dependencies when deciding which systems to investigate first.

What legacy code analysis should establish

“Legacy” describes a system’s context, not just its age. The useful questions are whether the team can build and test it reliably, whether it meets current business and operational needs, and what would have to change for a proposed target environment.

Separate three kinds of evidence throughout the assessment:

  • Source evidence: code structure, dependencies, build configuration, tests, and static-analysis findings.
  • Observed runtime evidence: executed workflows, resource use, errors, and network connections seen during a defined observation period.
  • Decision evidence: business priorities, constraints, target requirements, cost assumptions, and migration options.

One kind cannot substitute for another. A source call graph does not prove which paths run in production; a network connection does not prove that two servers are part of the same business function; and an AWS assessment is not a final migration decision.

Prepare a baseline before choosing tools

Start with a bounded application or service and name the people who can explain it. Record the repository and branch, language and runtime versions, build process, deployment units, data stores, scheduled jobs, external interfaces, owners, and business-critical workflows. Gather architecture diagrams, runbooks, incident history, release records, and any existing test or inventory results.

For every data source, record its collection window, coverage, and gaps. Keep a list of assumptions as well as confirmed facts. This prevents a quiet test environment, incomplete server inventory, or missing owner interview from looking like proof that a dependency does not exist.

Five strategies for analyzing legacy applications

1. Inspect source, builds, and dependencies

Inventory the supported build and deployment paths first. Reproduce a clean build where possible, list direct and transitive packages, identify unsupported runtimes or frameworks, and run language-appropriate compiler checks, linters, tests, and static security or dependency scanners.

Review findings in context instead of treating a scan as a verdict. A static security scan can point to a risky code path; a dependency scanner can identify a known vulnerable version; neither proves exploitability in this application or verifies behavior after an upgrade. Record the file or package, tool and rule, severity, reproducibility, and whether a test covers the affected behavior.

Output: a reproducible build record, package and runtime inventory, test coverage gaps, and a triaged list of source-level findings. Static analysis does not reveal every path the running system takes.

2. Observe behavior under representative workloads

Run automated tests and representative workflows in a controlled environment, or use approved production telemetry when staging does not capture real usage. Include normal and peak periods, batch and scheduled work, important error paths, and integrations. Collect application logs, traces, error rates, latency, throughput, and resource use that can be tied to the workflow being observed.

Use the results to find behavior that tests or documentation missed: for example, an infrequent report that reads a shared file, a retry path that writes twice, or a database query that slows sharply under load. Protect production data and availability when instrumenting or replaying traffic, and note which jobs or time periods were not observed.

Output: a set of observed workflows with their calls, failures, response or processing times, resource use, and test or observation coverage. The result is limited to the workloads and time window you exercised.

3. Map runtime dependencies across systems

Combine repository and configuration evidence with observed connections between hosts. Build graphs from package manifests, build files, and source references to see declared code and package relationships; these may miss dynamic loading or runtime configuration. Map the application’s databases, queues, file shares, identity systems, external services, scheduled jobs, and network paths as well. Confirm important edges with owners and logs; a source reference or observed connection can each have different meaning.

For new server-inventory work, the current AWS Transform discovery tool can collect server inventory from VMware, Hyper-V, and imported server records. With access to target systems, it can also gather operating-system and performance data and track TCP connections between servers in its inventory; process attribution depends on available permissions. Its network view is not a source-code call graph, and connections to endpoints outside the inventory can be absent.

AWS Application Discovery Service and AWS Migration Hub are in maintenance mode and stopped accepting new customers on November 7, 2025. Existing customers can continue using them for ongoing work. Check the current service notices and your account’s access before planning around them; AWS describes AWS Transform as the current migration and modernization workbench. AWS service availability notice.

Output: a dependency map that labels each edge as documented, statically inferred, or observed at runtime, and records its evidence source, time window, confidence, and owner. Treat unobserved connections as unknown until coverage is adequate.

4. Rank complexity and change risk

Use code size or complexity metrics as clues for where to investigate, not as a refactoring queue. Compare them with change frequency, test coverage, defect and incident history, security findings, ownership, business criticality, and dependency centrality. A large, complex module with stable behavior and good tests may be lower risk than a small, frequently changed module that supports a critical workflow.

Ask maintainers to explain high-risk areas and verify the explanation against source and runtime evidence. Identify the concrete failure mode or migration constraint before proposing a change. If a module is hard to change, a characterization test or a small pilot may reduce uncertainty more safely than a broad rewrite.

Output: a short, ranked set of hotspots with the evidence behind each ranking, the impact if it fails, missing information, and the next useful check. Complexity alone does not establish that code should be simplified.

5. Assess target fit and compare migration paths

Define what the application must continue to do before selecting AWS services: supported operating systems and runtimes, licensing, data consistency, latency, availability, recovery, security and compliance controls, integration boundaries, operational skills, and cost limits. Compare those requirements with candidate targets and identify the gaps that need a prototype or owner decision.

Then evaluate the migration strategy, commonly described as the 7 Rs: retire, retain, rehost, relocate, repurchase, replatform, or refactor/re-architect. Choose based on business goals, evidence, risk, and the cost of change. Refactoring is a major modernization effort; an old codebase, a high complexity score, or a tool recommendation by itself is not a reason to do it during migration. AWS explains the 7 Rs and their tradeoffs.

AWS Transform offers distinct capabilities for infrastructure migration assessment, server discovery, code analysis, and transformations. Its assessments depend on supported inventory and scenario assumptions; its code transformations have specific supported stacks and availability states. Treat their results as options to validate, check the current scope and prerequisites of the particular feature, and review generated code changes before using them. See the AWS Transform continuous modernization documentation and its managed transformations and availability states.

Output: a comparison of viable paths, each with its assumptions, known blockers, expected changes, test plan, and unresolved questions—not a generic “AWS compatible” pass or fail.

Check tool status and scope before adopting it

Some historical AWS code-analysis recommendations have changed. Since November 7, 2025, Amazon CodeGuru Reviewer has not allowed new repository associations; existing associations continue to work. Amazon CodeGuru Security reached its announced end of support on November 20, 2025, so do not start a new workflow with it; the AWS CLI reference records the cutoff.

For current source scanning, AWS documents Code Security for Amazon Inspector for vulnerabilities in application code, dependencies, and infrastructure-as-code. AWS Transform’s continuous modernization capabilities cover different analyses and transformations. Neither is a universal AWS-compatibility checker: confirm language, repository, feature, permissions, and data-handling requirements against current documentation before selecting a tool.

Turn findings into migration evidence

Use one findings register so application owners can trace a decision back to its evidence. For each item, record:

  • the application, component, file, host, or workflow affected;
  • the observation, source, tool version, and collection or test window;
  • the impact on business behavior, security, operations, cost, or the proposed target;
  • confidence and limitations, including conflicting evidence and unknowns;
  • the owner, next verification step, and decision that depends on it.

Rank work by the consequences of being wrong and by what the next step can resolve. Verify urgent security or support constraints, missing data or interface ownership, and important workload paths before estimating a large modernization effort. Do not turn tool severity or complexity scores directly into project priority.

Use a pilot to test the chosen path

After the assessment, select a representative, bounded workload and agree on acceptance criteria with its owners. Exercise its key workflows, security controls, data handling, recovery, performance, and operating costs in the proposed target. A pilot tests assumptions; it does not prove every application or workload will behave the same way.

After a migration or pilot, compare results with a pre-migration baseline using AWS post-migration performance benchmarks, including representative workloads and business-facing service goals.

Conclusion

Source inspection, representative workload analysis, runtime dependency mapping, risk ranking, and target-fit assessment provide different pieces of migration evidence. Record their coverage and uncertainty, then use that evidence to compare migration paths and design a focused pilot. Make code changes when the business goal and verified findings justify them, and check the resulting behavior against agreed acceptance criteria.