Code rewriting or refactoring? How to work with legacy code?
Every programmer knows how much trouble and frustration is old or carelessly written code.
According to the software development life cycle, the code should be tested at every stage of mobile app development as often as possible. Unfortunately, programmers "forget" about this task, and the client receives carelessly written code. It is best to find a trusted software development company with references and implement many successful projects to avoid such situations. In the article "Choosing the right software partner - guide (2022)", you will find some tips to make this task easier.
Well, since you are left with old or sloppily written code, you need to find a solution to this situation. There are several options, and each has advantages and disadvantages. Still, it is worth knowing the possible solutions and their consequences because only then will you be able to make the best decision
What to do with legacy code?
Legacy Code is old code or software that no one wants to touch, no one knows who wrote it, and everyone is afraid to replace it. Most developers have experienced this disturbing feeling of tinkering with an old component to extend it or fix a bug. At best, it's an obscure piece of code that accounts for a small function. At worst, it's the core of the entire system.
Old code is a fear for many programmers. Indeed, many of us found ourselves in a situation where we received an exciting task or order, prepared our tools and knowledge, and set ourselves up for a challenge. However, on the day we saw what we were dealing with, all our enthusiasm flew out in the first breath of disappointment. There is an old codebase in front of us. And we need to introduce modern features and compatibility to it.
In many cases, working with the old code is like reading old texts. We can never be 100% sure whether the wrong or incomplete code we are looking at is the result of a poor programmer's work, poor programming conditions, or just the realities of split code times. It does not change that before this code becomes compatible with our work, it will take a long time to repair it. Or maybe even a rewrite?
Do not worry. We have some tips for you on dealing with working with Legacy Code. But first, let's try to define it.
What is code rewrite vs. code refactor?
A code rewrite offers a distinct advantage: you know the flaws of the old system, the design shortcomings, present needs, and future road map. You can anticipate it, create a mechanism to overcome problems, and plan for the future. The disadvantage is that you'll have to maintain two systems while writing the new one, and trust me, even if the old system is in "maintenance mode," there will be a critical bug or important client request that will require your attention. Also, keep in mind that once you've integrated a customer demand into the legacy system, it must also be implemented into the new one.
You will replace old code in your system with new code over time if you do code refactoring. Rewrite a function, a class, or a module inside your project's scope. All of your tests and connections are there, and it's simple to ensure that nothing has been broken. You can't accomplish much with code refactoring; you can't simply change the programming language or even the basic framework of your project.
We can say that code refactoring is a change in the internal structure of the code without changing its external behavior. It is a way to improve the readability and maintainability of the code, for example, by renaming variables or methods, moving code blocks around, or extracting standard functionality into separate methods or classes.
On the other hand, code rewrite means changing significant parts of the code to work differently than it did before. In most cases, this means starting from scratch. However, there are times when you can partially rewrite a piece of Legacy Code without affecting the rest of the system.
When should you consider a code rewrite?
Several signs indicate it might be time for a code rewrite:
- The code is difficult to understand or maintain.
- The code is slow or inefficient.
- It's not possible to add new features or fix existing bugs.
- The code doesn't work the way you want it to.
- The codebase is large and growing.
- The project is no longer under active development.
How do you go about doing a code rewrite?
There's no single right way to do a code rewrite, but there are some general steps you can follow:
1. Analyze the Current Codebase
The first step is to analyze the current codebase and determine what needs to be changed. It includes understanding the business requirements, identifying the areas of the code that need to be rewritten, and planning out how the rewrite will be implemented.
2. Create a New Version of the Codebase
Once you have a plan in place, you can start creating a new codebase version. It involves creating new classes, modules, or packages and moving the existing code. It's essential to keep the structure of the new codebase as close to the old one as possible so that it's easy to migrate existing code over.
3. Rewrite the Code
Next, you can start rewriting the code. It can be a time-consuming process, but it's important to take your time and get it right. Make sure to test the code as you go and fix any errors.
4. Migrate Existing Code
Once the new codebase is complete, you can migrate the existing code over to it. It can be a slow process, so be patient and take your time. Again, make sure to test the code as you go to ensure that there are no errors.
5. Refactor the Code
Finally, once all the code has been migrated, you can refactor it. It's important to do this last, as it can disrupt the development process. It involves cleaning up the codebase and making it easier to read and understand.
By following these steps, you can rewrite your codebase without causing too much disruption to the development process. Just be patient and take your time, and you'll eventually have a codebase that is easier to read and understand.
How to choose between rewriting vs. refactoring
In rare cases, rewriting the code from scratch may prove to be a more effective and less painful solution and possibly more helpful in the long term. However, rewriting a particular hunk of the system is rarely a good idea, as it requires an excellent understanding of what that hunk does. Without the proper level of knowledge, the most common scenario is digging into an impossible task.
Legacy parts in systems often play a significant role in functions we have no idea about and handle many undocumented cases. So if, after fixing one bug, you say you would rewrite it all, cool down your enthusiasm, and spend more time researching the problem in a given fragment of the codebase.
If you do not have tests and are forced to act independently to understand whether it is better to rewrite the code or refactor, run the characterization tests.
I know it appears that code refactoring is your only option for removing legacy software without shutting down your business for a few months, but keep reading; you may be surprised. Refactoring will be considerably easier to sell to your product manager. What would be more simple for a client of a modest firm to comprehend: waiting three months with no improvement while R&D works on something they alone understand or having a little bit of time taken off each sprint to enhance the product?
Characterization tests
Depending on how old and complex the code is, people may rely on it in even weirder ways than you could imagine. Before doing anything about it, you should learn as much as possible about the code understanding and catalog as much of its behavior as possible. Characterization tests are automated tests that check the operation of old code and protect it from changes that could destroy functionality. Again, we thank Michael Feathers for this method. Your goal is not to figure out what the system should do but what it does. Fire up, observe, catalog, repeat.
Characterization testing is an effective method if we have the right conditions. The code can be divided into relatively isolated pieces of functionality, and the scope of the changes introduced is relatively narrow.
However, this will not always be possible. Legacy code is rarely modular, and it hardly has a consistent interface by which it communicates with other parts of the system. It very often has a lot of bizarre dependencies (anti-patterns, big ball of mud, or stovepipe system). It can use external resources from the inside. Boundary tests will usually not be enough - mainly because they are not well defined. However, some tools can automate final system testing to help characterize the code without knowing its details.
After the code has been thoroughly characterized, we can gradually make changes. The tests should prepare us to make small changes without damaging the functionality.
However, remember that changing is still a risk. Even with the best effort of characterization testing, the old system can be unpredictable. Therefore, the changes you introduce must be made in the minor possible steps. It is the most arduous work but relatively less tedious than going back searching for a mistake you made before. The small steps will help you make sure that the tests have passed.
A third option: "Strangle" your app
There is still another option. StranglerApplication is a technique coined by Martin Fowler to "strangle" legacy code components by rewriting them and then, via event interception, calling the new components instead of the old ones.
Let's assume we have a monolithic application that sells books online. The user history, profile management, and purchasing functionality are all present. We decide that the entire application is no longer manageable and likely to grow when demand spikes. We want to rewrite the purchasing component of the system. We'll start writing it in a new project and then deploy it to a different server. When a piece of functionality is complete, we'll replace it in the old system every time.
Assume that the purchase and confirmation order is ready. At this time, we'll locate the parts in our old system that require the old purchase function and replace it with a call to the new system.
We'll keep repeating this until all uses of the old purchasing component have been replaced with calls to the new system. At that point, we can safely delete that old code. The most significant advantage of this approach is that it minimizes risk. When you replace components of an old system one at a time, you can still use your E2E and integration tests since the functionality hasn't changed and the calling code is still in the old system. Another advantage is that while maintaining control over the rewrite, you can pause or slow it down as needed while also having access to any tech stack you desire.
Conclusion
Making a significant change to your codebase can be daunting, but with the right approach, it can be manageable. By breaking the rewrite into smaller pieces and taking advantage of existing functionality, you can minimize risk and maintain control over the process. Best practices for rewriting code:
- Break the rewrite into smaller pieces
- Use existing functionality
- Maintain control over the process
- Pause or slow down as needed
- Have access to any stack you desire
Rewriting has the allure of being the ideal solution, an opportunity to outperform the old system by avoiding its mistakes. However, you must evaluate your situation carefully before leaping into it. Do you have the resources to maintain both systems? Is it feasible for you to save time? Will everything be that much better? Sometimes code refactoring is a safer and more practical option than rewriting. While performing code refactoring, you may get a fast gain in maintainability and performance while also allowing you to start from scratch with new elements of your product development process.
We know old or sloppy coding is very complicated, especially for non-technical people. As an offshore outsourcing company, we have been running various projects for years, so we will be happy to share our knowledge and advise you on the best solution for your project. Make an appointment for a free consultation with our specialist, and you will surely be satisfied.