Skip to main content

Demystifying Legacy Code with AI

Source: Suriyo/stock.adobe.com

Outdated legacy code can pose a significant challenge for engineers due to its age and lack of modern practices. These challenges include security vulnerabilities, compatibility issues, and difficulty in maintaining and updating code.1  This article explores how artificial intelligence (AI) tools can be used to automate tasks like code analysis, refactoring, generating new code, and testing.

In the simplest terms, legacy code can be defined as existing code that still serves a valuable function but was developed without the use of modern coding practices. Upon examining legacy code, modern developers may find that familiar coding practices are frequently ignored or completely unused, making the code frustratingly difficult to understand and work with.

With the recent surge in generative AI, a plethora of tools have emerged to help modern developers automate their coding efforts. This has been made possible by training large language models (LLMs) on vast amounts of existing code, which allows AI to generate new programs based on user prompts. The question is whether the same automated coding tools can be used to help engineers understand legacy code. If so, this would save a lot of headaches and help bring old systems in line with modern requirements.


Why Is Legacy Code Still Around?


 Many might wonder why legacy code is still used today if it is so difficult for modern developers to work with. The issue is that time and effort have been invested in creating these older systems, with significant financial investment from the companies that use them. Since many legacy-code-based systems still operate as intended, there is no financial incentive to replace them. As a result, legacy code is now deeply embedded in many critical systems, including those in the medical, financial, and government sectors.

However, this presents a major challenge: In a world that is increasingly security-conscious, legacy code may not support vital updates. When ported to modern hardware, it may not run efficiently, resulting in bottlenecks that adversely affect performance. Even when these systems run as intended, legacy code is simply not written in a way that supports maintenance and modification by modern engineers. The following sections illustrate the main ways that legacy code differs from modern practices:

Outdated Programming Practices
Legacy code is often tightly coupled and monolithic rather than modular. 2 This makes it much harder to test and modify, as changing one section might significantly affect the overall functionality of the entire codebase.

Older Programming Languages                                                                                                                                      While modern developers may be aware of older programming languages, they may not be proficient in using them. Unfortunately, legacy code is sometimes written in languages that have long fallen out of popular use, which can make understanding it and using it with modern hardware particularly challenging.

Technical Debt
Technical debt refers to code that is inefficient or messy because the speed of development has been prioritized over best practices.3 While technical debt may not prevent legacy code from running, it can make it harder to understand what a section of code is doing, which makes it difficult to modify. A classic example is the so-called "spaghetti code," where a tangled control flow makes it almost impossible to understand how different sections of a codebase interact.

Lack of Documentation
Without documentation, it is challenging for engineers to approach existing projects and understand what a piece of code does and why it was written that way. In a modern context, a developer may be able to ask the original programmer for help, but for legacy code, this developer may no longer be around. As such, the organization loses essential knowledge and understanding of the legacy system.

The Basics of Code Analysis


The first step in understanding legacy code is to analyze it. Code analysis comes in two variants: static code analysis, which examines the code without executing it, and dynamic code analysis, which monitors code behavior during runtime. While code analysis is a good practice in general, it is particularly useful when working with unfamiliar legacy code.

Start with Static Analysis

Static analysis can help identify where legacy code fails to meet modern coding standards, such as code formatting and structure, naming conventions, and commenting.4 Coding standards help maintain consistency within a codebase, which makes it easier for a development team to collaborate on the same code.

Static analysis can also identify poor coding practices, such as unexecuted, duplicated, or overly complex code. By removing or addressing this noise within the coding environment, legacy code becomes easier to read, understand, and maintain.


Identify Deeper Issues with Dynamic Analysis
Dynamic analysis can help developers identify areas where performance suffers due to legacy code that is not optimized for modern applications. This analysis helps to avoid negative end-user experiences that might hinder the adoption of products or applications that use repurposed legacy code.
Developers can use both static and dynamic analysis to detect and address security issues within legacy code, which is rarely written with modern threats in mind. This code may also not be compatible with modern security tools, patches, or updates, so code analysis brings these issues to light before deployment.

Legacy Code Analysis Using AI Tools


Traditionally, code analysis is performed manually, with a team of developers reviewing the code line by line to produce the results outlined earlier in this article. While automated tools can dramatically speed this process by identifying issues, generative AI adds a new dimension to these efforts by providing feedback in natural language.

CodeRabbit is an example of a popular static code analysis tool that uses AI. It integrates directly with GitHub and GitLab, allowing automatic code analysis and providing detailed, context-sensitive feedback on what the code does and where it may have issues.5

For those working as part of a team, CodeRabbit can also generate easy-to-read documentation that summarizes any changes made during development, saving developers time writing these reports themselves. AI-driven dynamic code analysis tools, such as Dynatrace and New Relic APM, also aid developers in understanding code as it runs.6

Refactoring and Generating Code for Modern Systems

Code refactoring is another important practice for bringing legacy code in line with modern systems. Code refactoring is the process of restructuring existing code without changing what it does. When applied to legacy code, refactoring is significant for making code easier to understand, reducing its complexity, and enhancing its maintainability for future engineers. It also helps to remove inefficiencies caused by technical debt and is an essential next step after performing code analysis.

Like LLM-based applications that can generate and rearrange text without changing the meaning, coding AI tools like GitHub Copilot can make code refactoring almost instantaneous and even generate new code to support the process. GitHub Copilot installs as an extension to many popular integrated development environments (IDEs) and offers coding suggestions based on simple written prompts. Alternatively, it can suggest improvements to existing code or translate it into new languages better supported by a new system.7  These may be considered GitHub Copilot's most useful functions for refactoring legacy code.

However, there are some possible drawbacks to using AI to help refactor legacy code. The primary reason an AI tool may struggle to refactor legacy code correctly is a lack of documentation. If the developer cannot specify what a section of code is meant to do, the AI may fail to interpret it correctly, resulting in improper refactoring. Moreover, application context is key to ensuring that any refactoring is practical. If given free rein, an AI may make unreasonable coding suggestions or try to draw from libraries that are unsuitable for the application.


Another concern with using generative AI for code refactoring and generation is intellectual property (IP) liability.8  Developers understand that not all code is open source, but AI trained on many proprietary examples might produce code that is licensed or protected if not instructed to do otherwise. Therefore, it is essential that human expertise and knowledge remain at the forefront of development when using code refactoring and generation tools.

Ensuring Legacy Code Meets Modern Requirements


Finally, as with any other coding activity, legacy code must be tested and verified before being deployed. Testing ensures that code functions as intended, meets the application's requirements, and is free from defects. AI is not yet at the stage where the code it generates can be trusted without much scrutiny, but this does not mean there are no AI tools to assist in this process.

Tabnine is another AI-driven IDE add-on that enables automated unit testing, which involves testing individual software units in isolation before considering the complete system.9 Previously, developers had to write their own test procedures, but Tabnine creates them on request as the code is written, using text-based prompts. Not only does this automation dramatically reduce the manual effort required to write tests and identify bugs, but it also simplifies the process and improves coding efficiency.10

Unit testing is a fundamental practice in modern coding environments,11  but when applied to legacy code, it may be difficult for the following reasons:

  •  Legacy code is often riddled with hidden dependencies, making it difficult to break down into easily testable modules.
  • Poor documentation can make it hard to determine what specific sections of code are meant to do, so writing tests is challenging, even for AI.
  • Modern testing frameworks may not be suitable for legacy code due to language barriers and outdated build environments, including obsolete operating systems and compilers.

Nevertheless, there are effective methods for conducting unit tests on legacy code. As previously noted, many AI tools are available for code analysis and refactoring, and developers can use them to rewrite legacy code in a modular format that supports unit testing. Of course, this requires significant programming expertise, but it is far from impossible and an essential part of ensuring repurposed legacy code meets modern standards.

Bringing Legacy Code into the Modern Age


Legacy code remains a challenge for modern developers tasked with maintaining and modifying it. However, with the prevalence of generative AI, automated coding tools like CodeRabbit, GitHub Copilot, and Tabnine can generate valuable insights into how legacy code works and provide essential functionality to repurpose it for modern use. While many AI tools require further training to better understand the nuances of legacy coding practices, it appears that with human expertise, these tools will continue to improve in reliability so that legacy code does not have to be a mystery to modern developers.

1https://swimm.io/learn/legacy-code/legacy-code-5-challenges-tools-and-tips-to-overcome-them
  2https://www.ibm.com/think/topics/legacy-code
  3https://www.productplan.com/glossary/technical-debt/
  4https://blog.codacy.com/coding-standards
  5https://cloud.google.com/blog/products/ai-machine-learning/how-coderabbit-built-its-ai-code-review-agent-with-google-cloud-run
  6https://spectralops.io/blog/top-dynamic-code-analysis-tools/
  7https://docs.github.com/en/copilot/copilot-chat-cookbook/refactoring-code/translating-code-to-a-different-programming-language
  8https://www.bloomberglaw.com/external/document/X4H9CFB4000000/copyrights-professional-perspective-ip-issues-with-ai-code-gener
  9https://www.forbes.com/sites/technology/article/unit-testing/
  10https://www.tabnine.com/blog/unit-testing-definition-pros-cons-and-best-practices/
  11https://devops.com/unit-testing-in-development-ensuring-code-quality-and-reliability/

About the Author

Brandon has been a deep tech journalist, storyteller, and technical writer for more than a decade, covering software startups, semiconductor giants, and everything in between. His focus areas include embedded processors, hardware, software, and tools as they relate to electronic system integration, IoT/industry 4.0 deployments, and edge AI use cases. He is also an accomplished podcaster, YouTuber, event moderator, and conference presenter, and has held roles as editor-in-chief and technology editor at various electronics engineering trade publications. When not inspiring large B2B tech audiences to action, Brandon coaches Phoenix-area sports franchises through the TV.

Profile Photo of Brandon Lewis