DEV Community

137Foundry
137Foundry

Posted on

7 Free Tools for Auditing an Old Codebase Before You Migrate It

Before replacing a legacy system, you need an honest picture of what it actually does, not what the original documentation says it does or what everyone assumes. These free tools cover the inspection work that should happen before a single line of replacement code gets written.

server racks with organized network cables
Photo by Brett Sayles on Pexels

1. Your version control's blame and log commands

git log --follow and git blame on the files that make up the core business logic tell you when a piece of code was introduced and, often, why, if the original commit message was written with any care. This is free, it's already in your repository, and it's the fastest way to distinguish a rule that's been stable for a decade from one that was a quick patch six weeks ago. Git's own documentation covers both commands if your team isn't already using them for this kind of archaeology.

2. A static analysis tool for dead code detection

Legacy systems accumulate code paths that no longer execute, conditionals guarding for a state that can't happen anymore, whole functions nothing calls. Tools like Vulture for Python or built-in dead code warnings in most modern compilers help separate logic that's still load-bearing from logic that's safe to simply not carry forward into a replacement.

3. A dependency graph visualizer

Understanding what actually calls what, especially in a codebase that's grown organically over many years, is much easier with a visual dependency graph than by reading files in whatever order they happen to be organized. Tools like Graphviz can render a call graph or module dependency graph from most languages' static analysis output, making it far easier to spot which parts of the system are actually central versus which are isolated, rarely touched, and lower-risk to leave for last.

4. A test coverage report, even a rough one

Legacy systems are notorious for having little or no automated test coverage, but generating even a rough coverage report (which lines execute during a typical run, which never do) tells you where the system's actual behavior is exercised regularly versus where it's dormant. Dormant code paths deserve extra scrutiny during a migration precisely because nobody's recently confirmed they still work correctly.

5. A database schema visualizer

If the legacy system's business logic lives partly in stored procedures, triggers, or an unusually complex schema (common in systems old enough to predate modern ORMs), a schema visualization tool like SchemaSpy surfaces relationships and constraints that aren't obvious from reading application code alone. This matters especially for systems where business rules were implemented at the database layer rather than the application layer, which is a common pattern in systems built before that separation became standard practice.

6. A log aggregator, even a temporary one

If the legacy system produces logs at all, pulling them into a searchable aggregator, even a temporary open-source one like the ELK stack (Elastic's website) or a simpler alternative, lets you search for patterns across months of production history instead of reading log files one at a time. This is especially useful for confirming how often a specific rare code path actually fires, information that directly feeds into the frequency-times-risk prioritization that should guide how deep an audit goes on any individual finding.

7. A documentation generator for whatever comments do exist

Even sparse or outdated inline comments are worth extracting into a single searchable document rather than left scattered across hundreds of files. Tools like Doxygen or language-specific documentation generators pull every comment into one browsable reference, which makes it much faster to spot patterns (a comment style that shows up around every workaround, for instance) than reading file by file.

Putting these together with the human side of the audit

None of these tools replace talking to the people who currently use or maintain the system. What they do is make those conversations more productive, because you can ask specific, informed questions ("I see this function hasn't been touched in eight years and nothing in our test suite exercises it, what does it actually do") instead of vague ones. The tooling surfaces where to look. The interviews explain why what you're looking at exists in the first place.

For a fuller framework on how this technical audit fits into a complete legacy retirement process, including how to prioritize what to preserve and how to structure a parallel-run verification period before fully decommissioning the old system, see https://137foundry.com's guide on sunsetting a legacy system without losing institutional knowledge.

A note on scope

None of these five tools are expensive or complicated to set up, which is part of the point. The barrier to a proper legacy audit is rarely tooling. It's making the time for it in a project plan that's usually under pressure to get to the "real" migration work as quickly as possible. Running these tools takes an afternoon. Skipping them and discovering the gaps in production takes considerably longer, spread out over months of confusing, hard-to-diagnose incidents.

Budget the audit phase as its own line item, with its own estimated hours, rather than folding it silently into "planning," where it tends to get compressed first whenever a deadline gets tight.

Combining tool output with human context

None of these seven tools tell you why a piece of logic exists, only that it exists and roughly how often it runs. The tooling narrows down where the interesting questions are; answering them still requires talking to whoever's closest to the system. A useful workflow is running the technical audit first, compiling a short list of the specific business-logic questions it raised, and then bringing that concrete list into interviews rather than starting the interviews cold. People give much more specific, useful answers when asked "why does this specific condition on line 340 exist" than when asked a general "tell me everything you know about this system."

Keeping the audit from becoming its own multi-month project

There's a real risk of over-investing in tooling and analysis to the point where the audit itself becomes the bottleneck. Time-box each tool's pass: a day or two for the dependency graph, a day for the dead code scan, rather than letting any single step expand indefinitely. The goal is a good-enough map of the system's actual behavior to inform the interviews and the migration plan, not an exhaustive, perfectly complete catalog of every line of code.

A reasonable target for a mid-sized legacy system is a week of tooling-based audit work before moving into the interview phase. If a specific tool's pass is taking noticeably longer than that without converging on useful findings, that's usually a signal to move on with what you have rather than keep digging for diminishing returns.

Keep a running list of open questions the tooling couldn't answer on its own. That list becomes the actual agenda for the interview phase, which keeps those conversations focused and efficient instead of open-ended and hard to schedule around, and it respects the interviewee's time by showing up with specific questions rather than an open-ended request to "tell us everything."

Top comments (0)