Refactoring vs Rewrite: What We Have Learned Until 2023


There comes a time in the life of every software development project when developers and managers need to make a decision: what to do with the old code?

Should they invest the time and effort into refactoring the existing code or start from scratch with a complete rewrite?

The decision to refactor vs. rewrite application code can be a major one for a company. What do these terms mean, and how does refactoring differ from rewriting?

Refactoring involves making small yet helpful changes to the software’s existing code. Most of the code is kept intact with minor adjustments overall. Rewriting involves getting rid of most, if not all, of the software’s original code and beginning again.

Both approaches have pros and cons, but ultimately the right choice will depend on the specific situation and goals of the project.

In this article, we’ll compare refactoring and rewriting, explore their benefits and drawbacks, and provide guidance on choosing the best approach for your project.

What is refactoring?

Refactoring is the process of working with existing code and making minor adjustments to help make the existing software:

  • easier to understand,
  • add new features,
  • and maintain.

Refactoring may be done to make minor yet essential adjustments to how the software functions, integrates, and performs without changing the external behavior of the original source code or affecting the basic look, functionality, or user experience of the software.

Refactoring helps software developers to restructure the existing code so they can work easier with it in the future. This can be an excellent option for a newer software codebase that already works well and requires only minor adjustments and patching.

Code refactoring should be a part of regular code maintenance. This means you should refactor:

  • when you add new functionality,
  • while fixing a bug,
  • during the code review.

If your team can continuously refactor code, this can help keep the code workable, fresh, and valuable and help avoid a complete rewrite.

What is rewriting?

Rewriting, on the other hand, means that you throw away most, if not all, of the software’s existing code and write new code to build something fresh and new.

Many engineers prefer rewriting, allowing them to use their skills, creativity, and existing code knowledge to start with a clean slate.

If you successfully rewrite software application, then you can reap the following rewards:

  • Building software that is easy for the team to understand.
  • Building code that is familiar and quicker to modify if needed.
  • Working on all needed security features as the code is developed.
  • Ensure the new code works optimally with all hardware, operating systems, and technology currently in use.
  • Future-proofing the code with an eye on the foreseeable future.

What is more, many teams and individual engineers prefer to work with and on something they have developed themselves. And, if the code will be a vital and frequently-relied-upon factor for your company from here on out, ensuring that it’s something your engineers are familiar with and can enjoy working with is important.

However, just because developers want to rewrite the code, that doesn’t mean you should do it anyway. You should carefully weigh the pros and cons of that approach. More importantly, we should learn from the past and observe how similar code rewrites ended.

What are some examples of app rewrites?

There are instances where rewrites can go horribly wrong and were clearly not the best move for a company.

Some well-known application rewrites

One of the biggest disasters in the history of rewrites was Netscape 6.0.

As a result of this decision, tons of valuable knowledge and workable features were lost, and the new 6.0 was released so late (thanks to the rewrite as opposed to a refactoring) that in the interim, Netscape’s value and market share had tumbled. Had the company simply refactored, they could have released early enough to remain competitive and fresh.

Uber’s famed middle-ground was chosen by Basecamp, which decided to allow existing users to use legacy software simultaneously or upgrade to the newest form of rewritten Basecamp software. This meant that good legacy code was still usable to hard-core fans, and newer updates would help push the company into the future.

How NDepend refactored their legacy application

As a part of my research before creating the article, I spoke with Patrick Smacchia, a founder of the NDepend tool. If you are unfamiliar with NDepend, it is a tool for static code analysis for .NET applications. (I will explain later how you can use it in your refactoring process.)

At some point, they also migrated the legacy code to the latest version of the .NET.

So I asked Patrick a few questions.

Do you have experience in using NDepend to make a decision on whether to refactor or rewrite?

Not really, we always choose the option to refactor, rewrite is too expensive.

Could you summarize the most important takeaways from your whole migration process?

In hindsight, I can say that a year later, netstandard2.0 is still our primary target for our code and libraries we’re using and I guess our position won’t change for years. Migrating from netfx to netcore is quite a challenge and with netstandard2.0 we are sure we won’t face anymore migration challenge in the future. Of course, some code, like UI code, cannot compile on netstandard2.0, but we’re thinking of abstracting most of it in netstandard2.0 (button, textbox, radio… they are all equals no matter the platform). However, complex drawing stuff (like the graph) cannot be abstracted and we’ll still have a tiny fraction of our code not compiling on netstandard2.0.

Based on his answers, it seems like refactoring is always a safer option. That is unless you have too much technical debt. Or you are dealing with the legacy application.

Legacy code and technical debt play a role

Legacy code refers to the existing old system that has been inherited from previous engineers. It is the software code that your engineering team has been handed, and now need to decide what to do with it.

It is the code that has the following characteristics:

  • hard to understand and change,
  • doesn’t have tests,
  • written by someone else,
  • everyone is afraid to touch it,
  • has business value.

Look, this code can be entirely usable and perfectly suited for your company’s needs. If the legacy code is easy for your team to understand, modify, clean up, and tweak, a simple refactoring may be all that’s required.

On the other hand, if the legacy code is complicated and written using outdated technologies and frameworks, you should rewrite it and use the new technologies. In this instance, you can save time in the long run by rewriting instead of the time your team would spend attempting to understand and work with the legacy code.

Technical debt is another factor in deciding whether to refactor or rewrite your code.

Technical debt is a term often used to describe the extra work that a team must do in the future to fix problems caused by taking shortcuts in the current development process. This happens when a team is pressured to deliver a new version quickly. So, they implement an easy solution that may cause problems later.

For example, if a team makes design decisions that result in a messy codebase full of code smells, but they get the product out of the door faster, they should refactor the code and add tests after the release. This will make the code more maintainable in the long run. Otherwise, not paying down the technical debt results in a slower development process in the future. Which leads to legacy code. And causes demands from developers to rewrite the software.

Refactoring vs rewrite: how to decide what’s best for your application?

Depending on the complexity and workability of the code you have been handed, refactoring can save your company money over the time needed to begin afresh and code the entire project from scratch.

This is only true if the code you are working with is already easy to understand and well-suited for the task. If only a few adjustments need to be made and your team can quickly understand the software’s structure and make fast adjustments, this will save you billable man hours and help get the software usable quickly.

If time and money are both of the essence and there’s pressure to have a project up and running quickly, refactoring can be a simple fix that keeps you within budget and allows your work to move forward with minimal delays. Refactoring is best if you and your team are comfortable moving forward with existing software and making minimal adjustments.

There are instances where rewriting, though at first glance the more daunting choice, can save a company time. For example, if a company has been handed legacy code, understanding it well enough to work effectively with it may be difficult, if not nearly impossible.

Large parts of the code may be antiquated, unusable, or entirely unsuited to the needs of your engineers. In this case, it will take your experienced engineers less time to rewrite new code than it would for them to take apart and modify frustratingly puzzling labyrinths of information.

However, if the application is big and contains a lot of business logic that was poured in over the years, you need to have enough manpower for building the new version of the application.

Even then, it’s a risk since you must cover all business rules correctly. Which is not easy to do.

Consider your users’ needs

One factor which can help you decide whether to refactor or rewrite is to examine the needs and expectations of the users, clients, or consumers who will interact with your application.

Both approaches can have their drawbacks and their benefits. For example, users are often unhappy with minor changes to familiar interfaces over which they feel some degree of personal ownership. These small changes, however, are relatively easy to walk back to should you learn that they are wildly unpopular and that users are better served with older features.

On the other hand, giving the public an entirely new user experience with novel software can be a double-edged sword. You can risk driving away existing users or help bring in new interest with a wholly refreshed and reimagined product.

Which approach works for you will depend greatly on the current success of the existing product, future trends, and the enduring usability and popularity of what now exists.

Consider the needs of your team

If you ask developers what they want, their answer will probably be to rewrite an app.

Most software engineers prefer working with the latest cool technologies and would like to avoid the outdated code that someone else has written as much as possible.

Not forcing your team to work with legacy code can go a long way in ensuring team happiness and productivity and can help you retain good engineers.

While a rewrite may work best for many teams, if projects already beset your engineers, have many time constraints, and if existing code is largely workable, asking everyone to build a new system from scratch can foster frustration and set your company back. This frustration, coupled with the inevitable time investment and long hours required, can harm your engineers and cause delays that can disappoint or even lose customers.

The decision boils down to what your team needs, what they can invest, and how much time your company has to make quick adjustments or begin again. For example, does your company have enough engineers to successfully rewrite the new code while supporting the existing app? If not, and if refactoring is possible, the decision to refactor may be the wisest and most feasible for your team overall.

Try refactoring, then consider rewriting after that

…(if refactoring is not possible)

How can you decide if refactoring or rewriting is best for you? Well, first, start with the code analysis. Then, allow yourself or your team to review, understand, and see if refactoring at scale can save your existing codebase.

If the code is proving to be too unwieldy, difficult to understand, or poorly suited for your company’s needs, this will soon become apparent to your team. They’ll quickly realize that this is no longer a matter of patching. If the entire garment is now nothing but patches, it’s time for a new tunic. 

So, a good methodology to follow if you’re unsure is 

  • Begin by assuming that the code can be refactored

  • Work as you go, and consider what tasks you need the software to fulfill now and in the future

  • If you’re noticing more patchwork than the original cloth or the security elements of the existing code is problematic, a rewrite may be a better choice.

How and when to refactor?

Refactoring is a continuous process.

You or your team should do it regularly as part of maintenance. This ensures that code is kept up-to-date and optimized. It’s essential to consider both the functionality of your code and its readability when refactoring. You want to make sure that it’s easy for other developers to understand what the code does.

However, never refactor without having tests in place first. If you already have tests, then great! You can start refactoring.

If you don’t have tests, then you can add characterization tests. I have a separate guide on how to add them to your project so that you can start improving your code.

Having tests in place ensures that no unexpected bugs are introduced during the refactoring process.

Useful refactoring resources

It’s dangerous to go alone!

Here are the tools and books that can help with the refactoring journey if you are dealing with C# code.

Some tools that can help are NDepend and Resharper.

NDepend

I have already introduced NDepend, a code analysis tool for .NET code.

What you can do with it is install a Visual Studio extension and run the analysis across your whole solution. As a first result, you get a detailed report on the quality of your code. The report is split into several metrics, some of which are:

  • Lines of code
  • Debt
  • Coverage
  • Issues

You can also see where the code smells are in the code using the Issues and Debt window.

Selecting one rule allows you to identify places in the code where that rule is broken.

Once you know where the issues are in the code, you can use Resharper to fix them.

Resharper

Resharper is another popular developer tool for Visual Studio. It is a code analysis and refactoring tool that helps developers write and improve code.

It includes many different features, such as:

  • Code quality analysis
  • Eliminate errors and code smells
  • Automated safe refactorings
  • Compliance with team coding standards

It offers suggestions on how to change code.

It can also do solution-wide changes.

Now, Resharper is a paid tool.

And in the last few additions, Visual Studio has closed the gap by offering more built-in tools that Resharper has.

There are also great free alternatives to Resharper, so check them out. My favorite is Roslynator.

Books and courses

Some books that can help are:

In addition to books, I also have a course on how to refactor C# code using the latest Visual Studio’s built-in tools. It is explicitly designed for C# developers.

How and when to rewrite?

If you are thinking about the rebuild, you can take two main approaches.

Big Bang rewrite

The Big Bang approach refers to a strategy in which an organization completely replaces an existing system with a new one. This happens at once, once the new system is ready. This approach is often used when the existing system is outdated. This can be if it was developed by using a technology that is outdated or not supported anymore. In this case, you need a complete overhaul to bring the system to the modern age.

What happens in this approach is that you essentially split your development efforts into two parts:

  • One part of the development efforts goes to the existing system – these developers make sure the old system that pays the company’s bills still works as expected. They fix bugs and add some requested features.
  • The second part of the development is involved in creating the new system – they write new code and attempt to cover most of the functionality from the old code.

One of the key advantages of the Big Bang approach is that it allows an organization to implement all the desired changes to the system at once rather than making incremental updates over a period of time. This can be more efficient and cost-effective in some cases as you can focus on the new system and minimize the time and resources spent on maintaining the old system.

However, this approach also has some risks:

  • It requires a significant upfront investment.
  • It requires you to focus on two systems at once.
  • It’s not easy to collect all knowledge of the existing system, especially if it is very old.
  • It usually takes way more time and more to rewrite than estimated.
  • It requires you to decide which new features go into both the old and new system, and which only in the new system.

As a result, it is important for organizations to carefully consider the pros and cons of the big bang approach before deciding to undertake a complete software rewrite.

Incremental redesign – Strangler Fig pattern

The Strangler Fig pattern is a software rewrite approach that involves gradually replacing a legacy system with a new system by wrapping the new system around the old one. The old and new systems live in parallel, in a single codebase maintained by a single development team, while you add more and more functionality from the old to the new system.

This allows you to continue to use the old system while the new one is being developed and tested. It also reduces the risk of the transition. In addition, the new system can be rolled out incrementally, as each feature is completed until the entire old system has been replaced.

The Strangler Fig pattern was coined by Martin Fowler back in 2004.

This is the same approach that Shopify took while redesigning one of its most important modules in the system. There is also a presentation available on YouTube about that.

Conclusion

Refactoring involves restructuring existing code to increase maintainability so you can easily implement new feature requests. Rewriting the code involves starting from scratch and developing an entirely new software product with unique features, aesthetics, functions, and signature.

Deciding which is best for your team means looking at how workable the existing legacy code is. Refactoring is a good start if the code can be simply and efficiently modified to suit your and the client’s needs.

But, if the code is too complicated or archaic to work with easily, a rewrite may save you time and money in the long run and allow your software engineers to develop something entirely new that will be easier for them to work with in the future. Not to mention that you will more easily onboard new developers in the future.

Recent Posts