Notes to Myself on Software Rewriting
Rewriting a system from the ground up is essentially an admission of failure as a designer. It is making the statement, “We failed to design a maintainable system and so must start over.” —Max Kanat-Alexander, Code Simplicity
So as other software designers, I’ve been in a software rewriting process and I’ve admitted that we failed to design our software. I learned a lot from that exhausting process and here I am sharing lessons that stuck to me.
Rewriting code is a developer delusion, not the solution in most cases.
When you are in trouble with your code, it is important to diagnose what is the issue exactly. As every developer will do, your initial thought shouldn’t be rewriting. This is just a delusion.
It is a delusion because you are struggling to read someone else’s code and you think you would do a better job if you rewrote it from scratch. However, as developers, we fall into this trap over and over again. Rewriting seems easy because you don’t have to read someone else’s code, you don’t have to waste your time by changing small pieces of code and test it again to make sure you didn’t break anything.
You subconsciously choose the easier way but at that point, you fall into another trap. Someone will think the same thing when it comes to reading your code. Even there is a big possibility that you will not like your own code that is rewritten by you in the future. When that happens, will you think again to rewrite the code? Most probably NO. Therefore always remember these facts when you think to rewrite the code from scratch. Remember that this is a trap in most cases.
Consider refactoring before taking a step to code rewriting
Targeted rewrites are useful to deal with the worst offenses in your codebase. Don’t do a whole rewrite if you can limit the scope and address the majority of your problems. For example, the loading of your software is so slow. But this only affects a small part of the project. These problems can be solved, one at a time, by carefully moving code, refactoring, changing interfaces. You don’t have to rewrite the whole thing.
Beware. This is a longer, harder, more failure-prone path than you expect.
There is a fact that developers usually realize it after they miss the deadline: everything takes longer than you think. Be very pessimistic in your estimates about the cost of a rewrite. It almost always costs more and takes longer than you would think. There will be always a lot of complexity that will make the rewriting process harder and more painful. In the end, the possibility of failure is hard to miss.
Make sure the new product is better at solving user’s problem (or at least the same). Worse cannot be acceptable.
Rewrites have no direct effects/benefits for the customer. Your users don’t care about your code. They just want to solve their own problems. That’s all. In their eyes, you are successful if your product fits in solving their problem. Otherwise, they are not using the product. They don’t care about your rewriting decision, so the rewritten version must at least work as efficiently as the old one.
Keep maintaining and supporting the existing product.
In one rewriting story that I’ve participated in, we didn’t give any update to users for one year. This is too long in the world we live in today. Our product was still good enough, but users were complaining about no updates. Never stop maintaining a system that is currently in use so that the programmers can rewrite it. During the rewriting process, the old code still needs to be maintained. Small updates and bug fixing should be given to users while you are rewriting the old code. Otherwise, you will face losing your customers.
Involve users in the design process as soon as possible.
Always show your current progress to your end users at regular intervals so that they can help you catch the worst offenses. It is important to meet your users as soon as possible. Their feedback will help you design a new product based on their needs. Don’t implement any unnecessary features. This will save you from having a complicated codebase.
Keep the teams working on the product synced.
The product is not only about the programming team. Marketing, support, programming, design… Many teams work on it. Keep them synced by giving them regular updates about the rewriting process.
In our case, we have dealt with many problems. For example, the marketing team was preparing our product beta campaign and they had to know exactly what was going on the product side so that they could prepare customers for upcoming product changes. Sometimes we made some changes without informing them. And this caused them to prepare their campaign all over from scratch. Don’t spend anyone’s time inefficiently.
Don’t make dramatic changes to the product.
It is important to know your product’s weak and strong sides. Don’t change the strong sides, the ones loved by users. If users are satisfied with your UI, don’t change it. Do minimal changes and small UX improvements. When you replace your existing software with the new one, your users shouldn’t be confused with the new dramatic changes. There are many cases where users abandoned new products because they didn’t find the same functionality as the previous product provided. Don’t let the same thing happen to you.
Don’t make your product depend on only one developer.
In my case, one of our main programmers was the responsible developer for our software. Since he was in management, he had to deal with other problems as well. Due to his position, our product development was going slowly. Even small changes were taking several weeks, sometimes months. The point is to always keep moving. Never stop.
Migrations should be slow and steady.
Replace your original software with the new one when you are sure that the new one is ready. Do it step by step.
First, start with a small private beta group and ship your product into that group. Continuously collect feedback and crash reports, fix the bugs, iterate new versions and again the same thing. Follow this cycle until you ensure that your product is ready to go public beta.
When you go public beta, feedbacks are going to be your best friend again. Your first goal here should be to ensure that your product solves the users’ problems. When you are sure that you are providing the same or better functionality as old software did, a replacement can take place. Release the new software for new users, and migrate your existing users to the new one.
Those are the key lessons that I learned from our rewriting process. Rewriting is almost never the answer. More often than not, refactoring is a better bet. I strongly advise the slow approach of using refactoring. It’s less risky and you keep your customers happy.
When to rewrite the code
There are times when it is appropriate to do a rewrite. If I could have made a list about when to rewrite the code, this would be my list:
Switching to another language or platform
The language is so old. It is hard to find a developer or you have to pay a lot of money to get one. In both cases too much effort.
The existing codebase is not maintainable anymore
How do you decide your code is not maintainable? It is hard to determine but if even small changes are hard to be done, if new updates take longer than usual, if any new change affects other parts of the software and introduces new bugs, your software is unmaintainable.
You have the resources available to both maintain the existing system and design a new system at the same time
Never stop maintaining a system that is currently in use so that the programmers can rewrite it. Systems must always be maintained if they are in use. And remember that your personal attention is also a resource that must be taken into account here — do you have enough time available in each day to be a designer on both the new system and the old system simultaneously, if you are going to work on both?
The developers in the team are a bottleneck for software
This shouldn’t be a reason to rewrite the code from scratch. You can always switch developers within the team or you can hire new developers to eliminate the bottleneck.
However, sometimes, there might be times where you have to choose the rewriting option. The software was written with old technology and one of your main developers was the only responsible person to develop it. You can try to find a new developer who has experience with the old technology you are depended on but I am warning you, it is a difficult process and it will take too much time. Even if you could have found a new one, it may be very expensive to hire. So together with other conditions, this may be in your list to decide to rewrite code.
The software is long-lived (I’m talking like 10–20 years or more)
Maintenance becomes more and more expensive over time. This is due to the fact that the code is becoming more and more spaghetti-ish as the original architecture is sacrificed for quick maintenance patches. Also, developers for older technologies become rarer and more expensive. Finally, hardware begins to age and it gets harder and harder to find new hardware, operating systems, frameworks, etc. to run the old application on top of it. Also, businesses evolve, and most likely an older system will not be meeting the business needs of the organization.
So you have to weigh all of the ongoing maintenance costs, as well as the potential benefits of a new system, against the cost of rewriting it from scratch.
If your case fits in one or more of the above points, you may be in a situation where it is acceptable to rewrite. Otherwise, the correct thing to do is to handle the complexity of the existing system without a rewrite, by improving the system’s design in a series of simple steps.
Rewriting your code from scratch could be the single biggest mistake you make, but equally so, not-rewriting your code could lead to the same result.— Getting away with rewriting code from scratch.
Here is a piece of advice. Refactoring should be the first option.