If you work for an organization that understands the value of high-quality code, refactoring should be a key part of the code remediation process. Code refactoring is the process of improving existing code without changing its external behavior. The goal of refactoring is to make the code easier to understand, modify, and maintain.
With the rise of AI, there are high expectations about its potential to support and automate refactoring activities. But is it really the silver bullet we’ve been waiting for? In this article, we will describe what healthy code looks like and its business value. We will then evaluate if AI can live up to the promise. So, let’s explore…
Healthy Code and Its Benefits
What is the difference between ‘good’ and ‘bad’ code? While there are multiple definitions and different points of view, there is consensus on the following: good code must be easy to evolve and easy to change.
There is no single KPI that can be measured to establish code health, meaning to measure how ‘good’ the code is. Instead, we need to look at multiple aspects, such as:
- Readability: Code should be easy to understand for anyone on the team, not just the person who wrote it. This includes proper naming conventions, clear comments, and straightforward logic.
- Maintainability: The code should be structured in a way that makes future updates and changes easy to implement without introducing new issues.
- Performance: Efficient code that runs smoothly, minimizing resource use, ensures that the application performs well under various conditions.
There are plenty of other code health metrics beyond the ones mentioned above. For instance, CodeScene, a company known for its code scanning tool, uses 25 different metrics to evaluate the overall health of a codebase.
Code Smells
“Code smells” are hints in the code that point to deeper issues within the application or codebase. They’re not bugs or defects. They are signs that something in the code design or development process is off. While the code works as expected, these smells can slow down development and make an app vulnerable to performance or security problems down the line.
Some examples of code smells:
- Obscured intent: Code where the purpose or functionality is unclear or difficult to understand at a glance.
- Bumpy road: Occurs when a function is packed with layers of nested conditional logic. This kind of code slows down comprehension.
- Inconsistent naming: Using different naming conventions or terminology for similar elements throughout the codebase.
- Inconsistent styling: Lack of uniform formatting, indentation, or overall code structure.
There are many more examples – check the resources section at the end of this article for a link to a full catalog of code smells.
The Business Value of Healthy Code
So, how can organizations work toward a healthier codebase? This is where refactoring comes in. Like other aspects of technical debt, building a business case for refactoring can be tough. Fortunately, the business benefits of code health have been demonstrated:
- Faster changes: When the code is clear, well-organized, and easy to understand, developers can implement changes more quickly. The time spent untangling complex or messy code is significantly reduced. This allows a faster response to the business demands.
- Faster problem resolution: Similarly, when there are defects, if the code is well-structured and easy to navigate, this will help developers trace the root cause of the problem quickly, minimizing downtime for business operations.
- Faster time-to-market of new features: If code is easy to understand and change, new features can be implemented and rolled out faster, keeping the business competitive and responsive to market demands.
Ultimately, when code quality is prioritized, the entire business becomes more agile.
AI to the Rescue?
Artificial Intelligence, especially Large Language Models (LLMs), are designed to excel at working with text and spotting patterns. There are plenty of reasons to believe that AI could significantly speed up code refactoring and maybe even fully automate it one day. But not so fast – there are still a few hurdles we need to clear first.
There have been critiques from the developer community about how AI tools focus too much on speed and task completion over code quality, and it’s a valid concern. For example, a seasoned Salesforce Developer shares her experience with GitHub Copilot:
“I use GitHub Copilot to assist with LWC and Apex code generation. It’s a valuable tool for providing code suggestions and accelerating development. However, it’s important to note that AI-generated code might require refinement to meet quality standards. For instance, while Copilot can generate basic test cases, more complex test scenarios often necessitate human intervention to ensure thorough coverage and edge-case handling.” Edith Valencia, Salesforce Developer
While AI can help get things done faster, when it comes to refactoring, code quality should always be the top priority. And as much as we would like AI to solve all our coding issues, the reality (at least for now) is that there are challenges to be considered.
Let’s look into some of them…
Context Awareness
Generative AI may struggle with context awareness, potentially leading to suggestions that don’t fully align with what’s available on the codebase. For example, AI might suggest Apex methods and classes that don’t exist, try to query objects and fields that have not been created, and attempt to invoke JavaScript functions that have not been written.
Correctness
Refactoring code must not change the external behavior of the application.
JavaScript (the language behind Salesforce Lightning Web Components) is particularly challenging to refactor due to the nuanced nature of its syntax.
In their paper, “Refactoring vs. Refunctoring”, the CodeScene team found some common patterns that made the code incorrect when refactoring JavaScript:
- AI inverting boolean logic – for example, a && b becomes !(a && b)
- AI may incorrectly refactor code with the expression “this” by moving code into a separate function, causing “this” to lose its intended context
- AI might eliminate entire logical branches
These are major changes that will break functionality, so having a thorough process for validating any AI-generated changes is critical. This means that fully automating the process is still not reliable enough.
Bad Code
Large Language Models can only be as good as the code they’re working with. When using vague variable names like “xyz”, the AI tool will have a hard time understanding their purpose. LLMs also tend to fall apart when dealing with legacy code. Deep nesting, high cyclomatic complexity, and even something like a “continue” statement inside a loop can confuse the model and lead to less accurate results.
Security and IP Risks
AI-generated code can introduce security issues or intellectual property risks that may not be immediately apparent. For example, some teams found during their tests that there were some cases where AI threw away an entire if-block. This type of bug could become a cybersecurity threat if, for example, important input validation gets removed.
Refactoring for the Future
When human teams refactor, they not only consider what’s done but also what’s on the roadmap. They have a vision for that codebase. They might modify the code with the intention of enabling future changes. This is something that generative AI cannot do.
Familiarity of Code
Fast code generation can be great, but when the team isn’t familiar with the code and no one feels fully comfortable with it, changing the code becomes challenging. Debugging for troubleshooting also becomes more time-consuming. If AI tools handle refactoring, entire teams may become unfamiliar with the code, defeating the purpose of refactoring as maintenance could ultimately take longer.
AI-Generated Tests
Writing test cases is often one of the least favorite tasks of developers. Test-driven development is considered a best practice, and it’s tempting to ask AI to write the tests for us. But this is a risky approach. The same LLM that produced wrong output could very well produce wrong tests. AI can assist in initializing some of the test data and suggesting different test scenarios, but ultimately, a human developer must verify that the test is valid.
Good Use Cases of AI for Refactoring
Let’s explore some cases where AI can bring benefits to the development teams that are working on refactoring existing code:
- Explaining code: AI can be of great help in assisting developers to understand an existing codebase much faster. Even if the explanation is not 100% correct, it can still provide valuable guidance.
- Identifying code smells: AI tools can quickly scan large codebases and identify common code smells. By flagging these issues, AI helps prioritize areas for refactoring and reduces the time spent on manual code reviews.
- Automating repetitive refactoring tasks: AI can handle routine refactoring tasks, like renaming variables, simplifying conditionals, or extracting methods. These repetitive changes can be time-consuming for developers, but AI can automate them, allowing the team to focus on higher-level design decisions and more complex code improvements.
Recommendations
Developers are increasingly relying on coding assistants, and refactoring tasks will be no exception. So, how do we mitigate the risks?
Here are a few key strategies:
- Combine traditional tools with AI: Pair AI-powered assistants with tools like linters to identify potential issues early and ensure adherence to coding standards. Linters are traditional code scanning tools that analyze static source code to detect and flag issues that might lead to bugs, vulnerabilities, or code smells.
- Code reviews: Regular code reviews by humans are essential for maintaining code quality and ensuring that everyone on the team is familiar with the codebase, regardless of whether an AI helped generate it.
- Monitor key metrics: Keep an eye on code growth rate and debugging time. If either starts to spike, it could be a sign that the AI-generated code is introducing unnecessary complexity or issues.
Final Thoughts
Generative AI tools can help with refactoring, but we’re still in the early days. Human developers are still a must. They need to review and validate the AI’s suggestions, making sure the changes are real improvements and that nothing breaks the intended functionality.
The dev-in-the-loop is still required for code refactoring activities. By balancing the use of AI with the best practices, we can leverage its benefits while minimizing potential risks.