Should you start from scratch or invest time to refactor your application?
- The concepts explained: software rewrite, starting from scratch, and refactoring
- When should you create software from scratch?
- Making the decision: How to decide to rewrite or refactor?
- How to get started with refactoring?
Software rewrite vs. starting from scratch vs. refactoring
Building software products is an ever-changing and challenging process. Technology evolves, the way we build software changes, and there are many ways of executing your strategy.
Because of these variables, user feedback, shortcuts taken during development, and the time it takes to get your app shipped from an initial idea to an MVP or a stable live product, you might find yourself at the crossroads of refactoring what you’ve got or starting over by rewriting your software completely.
The desired outcome
Ultimately, you want software that your engineers can easily work with. A codebase that features well-tested business rules is easy to read and understand for new hires, and using the right technologies is only a dream for many teams.
The conversation to rewrite the codebase starts in several ways. It might be that the engineering team has leveled up recently. Perhaps they recently visited a conference and learned new design patterns to apply to the project. Maybe they’ve hired someone who has a unique viewpoint on the solution.
In other cases, the team might be struggling with delivering quality code quickly enough. Sometimes customers complain, but the team is already drowning in bug tickets. Or new functionality takes ages to get implemented. In some cases, you’ve even requested a technical audit, whether for your own reasons or as part of an investment round.
No matter how the conversation starts, you’ve realized a change is needed. Now you must choose between starting from scratch and refactoring your existing codebase. You’ll end up in the same place, with good maintainable code, but the paths could not be more different.
What “starting from scratch” means
When we talk about starting from scratch, we are talking about setting up a brand new code repository with the latest versions of your tech stack (the coding language, the framework, and the way the database or backend connects with the customer-facing front-end).
It’s really a blank slate with no preexisting code or decisions made. This gives teams the opportunity to build a new product from the ground up and rewrite the software completely.
Two types of refactoring
Refactoring is colloquially used to mean reworking an application’s specific functionality or module. This is sometimes called, confusingly, a partial rewrite. Technically, refactoring means that no behavior changes occur, only the code behind that behavior.
We like to think about refactoring as a never-ending process. It’s a slow and methodical way to make changes to your software, upgrading things that are out of date, optimizing performance, and resolving the small housekeeping tasks that keep the codebase easy to work with.
When to create software from scratch
Three scenarios for starting over
There are only three cases where you should be writing software from scratch. The first case is when you don’t have an existing product. This is obvious. If you want to build something and you don’t have anything yet, you’ll need to start fresh.
Second, if your target audience changes, you should consider creating a new application from scratch. When you are building a product for a new group of people, it’s time to go back to the drawing board — because you are building a new product, and new products deserve a new codebase.
Third, it’s possible that you are solving a different problem for the same group of people. In that case, you should consider a software rewrite since it’s likely there are large parts of the existing codebase that aren’t needed anymore. It’s far easier to start fresh and build toward this new vision. After all, you are building a new product!
How do you know when your audience changes?
Even when the original application could be salvaged, albeit, with great time and expense, it might make sense to start over when your audience has changed compared to the original. User behavior evolves, customer expectations change, or your product may not fulfill the original goal anymore.
We view the new application as a startup within the company, and it should be treated as such. The hypothetical startup has the advantage of stealing knowledge and intellectual property (IP) from the old product. The original product can still service the original customers during this time, providing a foundation for experimentation.
The bottom line is when you are building for new people, it makes sense to start over. This does not necessarily mean starting from scratch, as you can still reuse assets (especially knowledge and IP), but working in a new repository will likely be the right call.
When startups look for product-market fit
Maybe a huge part of the product is no longer used, or you are considering building a spinoff of the original idea. These codebases will need a different approach from what you had in mind when you started. You are, in fact, building a new product.
Another typical term is pivoting your startup. When a startup breaks with its core focus and fundamentally changes, it might be another sign you should consider starting fresh. The functionality of your product will be totally different than in the previous version.
Pursuing new markets comes with new challenges and new users who behave differently. It will result in new feedback. Without enough research, your company is likely to fail.
You need to start building a new product, and you have to consider it just like that: a new product. Because of this, it should have its own codebase. That doesn’t mean you can’t copy and paste some of the old code to a new project, though.
The bottom line is when you are building for new people, it makes sense to start over. This does not necessarily mean starting from scratch, as you can still reuse assets (especially knowledge and IP), but working in a new repository will likely be the right call.
The risks of a software rewrite
Rewriting software is not as simple as starting a new project and diving in. There are several risks that can make it difficult for an existing team to find success. Here are the major risks we see.
Developers will make new mistakes
Your developers might complain about the existing code and how badly it was built. And because it’s already a couple of years old, one might think with the knowledge gained over that time, they will not make the same mistakes again when they start over now.
But won’t they say the same when looking back on the new version just a few years from now? Probably, since they are pushing the boundaries of their knowledge. This is the Myth of the Software rewrite.
“I will not make the same mistakes,” they say. This is (probably) true. Great developers avoid making the same mistake twice, but they will make entirely new mistakes when implementing new architectural decisions.
And this is completely normal; it has nothing to do with your developer’s experience. But it’s better to invest in fixing your existing mistakes than to create new ones. The only way to prevent mistakes from becoming catastrophic is to regularly release software that is well-tested; more on this later.
One of the mistakes that developers might make is picking a specific technology stack or architecture because it is popular. Certainly, this popularity is an important part of the decision because of the implications for hiring.
However, do ensure that your technical team isn’t jumping on a bandwagon and switching technologies just for the sake of something novel. Do you want to bet your business on technology that hasn’t been proven in production environments?
The new system will be untested
Let’s press the green button and host a launch party when it’s all ready to go!
You think your new product is finally ready to go live. You are going to shift all your users from one product to another, all at once, with a big bang and a bottle of champagne to crack. Sounds good? Nope.
Almost every big bang release ends up badly. Here’s why.
First, the given deadline for the release is often a fixed date set by stakeholders that don’t work in the technical trenches. They have no proper understanding of the development pace, and — since hard deadlines are set — corners will be cut along the way resulting in lower quality and more bugs. Infeasible deadlines are one sure way to kill your product.
Then there is the actual usage of the product with a critical mass of users and all the edge cases and scenarios possible. Even the best teams in the world can’t test completely.
This means that everything in your new product will get tested in the live environment at the same time, and if things go wrong, your Big Bang release will go bang very hard. Compared to incremental software development, if something small breaks, your bang will only be small. Untested products “bang” very hard.
The product definition is not clear
Because you decide to start over, this is a nice chance to improve the product too. New technology means new opportunities. However, this can easily lead to a situation where, little by little, all of a sudden you’re building a product that’s twice as big as the old one, taking up 2 years of development time. Scope creep is real, so be on the lookout for that.
What is the scope of the current application? What edge cases are properly handled? What quirks have customers come to rely on? There is a lot of knowledge gained over the years that is only available by reading the existing code.
Another challenge is how to handle the old version of the application. It’s still in use by customers and changing every day since it’s a live product. Because your old product keeps changing, it’s nearly impossible for the new version to reach feature parity.
The alternative is to freeze changes on your existing product. But don’t forget about your competition. Depending on the size and quality of your development team, starting over means zero progress (new functionality for customers) for at least 2 to 12 months. Do you want to give your competition this free time to catch up or pass you?
People costs
Starting over and completely rewriting will require many increased costs since you’ll need additional product people, designers, engineers, marketers, and support analysts.
Building a new product requires research which means more people. Research is essential so you’ll need to invest in a product manager (or 2 or 3) to properly analyze the existing functionality and document what to keep.
At least one senior developer is needed to lay the foundations and prepare the system (environments, pipelines, and automation) for development work to begin in earnest. And then you have to actually build the new MVP.
If you instead decide to keep working on the old product, now you have a different problem. You’ll need to expand the team in order to find the bandwidth to make meaningful improvements.
Is it in the budget to hire a larger team? Switching codebases and technology might require hiring new people. You need experts in the new area to make the right architectural decisions and avoid making rookie mistakes.
Perhaps, your current development team has not proven themselves with the new technology stack. Now it’s a risk you take: your senior engineer might not be so senior and experienced anymore.
If you do expand the team, you need to evaluate those potential recruits. Do you have experience hiring in this domain? Evaluating JavaScript engineers is very different from figuring out how experienced a Rubyist might be.
One option is to work with new and more specialized recruiting agencies since you are exploring a new engineering field. But that comes with an overhead cost.
Morale is also a consideration. The high cost of low morale becomes clear when your development team gets split up or needs to start working with other (unfavored) technology.
Usually, with a rewrite, the old app still needs attention and one or more developers get assigned to work on and maintain that version instead of getting to play with the new thing. Being permanently stuck on bug-fixing duty for an old codebase can be soul-crushing.
On the flip side, we’ve seen developers prefer to work with the technology and stack they are familiar with. Switching to a more modern codebase might even scare off and demotivate them. There is also a chance your employees might leave because they didn’t sign up for this when they were hired.
At worst, your decision might result in people quitting. Make sure you know the preferences of your existing team. If a key team member decides to give notice, what types of knowledge are lost with them? Does your team have a reflex to document to mitigate this risk?
If employees do leave, be prepared to pay more to replace them. The war for software engineering talent is tougher than ever before. And when that person arrives, will they need to learn one system or two?
Infrastructure costs
If you decide to start from scratch, you now have to manage two different products. In the common scenario of having to keep the old version running, you’ll end up with double the bills.
The new product needs its own development, staging, and production environments. So this means twice the infrastructure costs for the next 6 to 12 months.
Sometimes it’s worth the effort to have two versions of a product. But unless you know what you are doing, avoid building two products simultaneously. You’ll end up managing and paying for two of everything before you know.
Users might not like it
Users usually don’t like change unless you educate them properly. They weren’t necessarily waiting or ready for a big change. And since you have not gathered any feedback from live users on your new product, as it simply didn’t exist yet, you have no idea how they will feel about it.
And don’t overlook that it will take time to transition users from one platform to another once the new product is ready. Some users might prefer to stay on the old version, and then you will continue to pay maintenance and infrastructure costs until they’ve all been migrated.
Things will take longer than you think
Time is money. By starting over completely, you give your competition a chance to catch up or extend their lead. Even if you can rewrite your product in 3 weeks (haha), you give away 3 weeks of potential improvements to a risky and unclear rewrite.
And while you can, of course, reuse a lot of existing functionality, and the IP is never lost, it will take you more time than you think to get your new product somewhere near feature parity with the old app.
The idea is the same as with a feature: is it worth our time? Data-driven, no gut feeling. Does rewriting make us so much faster that we will gain back the time spent later on? We can imagine this being true with 3 weeks of rewriting, but it will be much harder with 1 year of rewriting.
Data migration is complicated and can quickly get messy. It’s very prone to human error, especially when you take the opportunity of a rewrite to change how your data stores are organized.
You’ll also need to consider the new system’s effects on other processes. Do you need a different on-duty roster? How will support manage an influx of tickets on a new product? Is there customer-facing documentation that needs to be updated? All of this takes time to manage.
How to decide to rewrite or refactor
Cleaning up and refactoring the old product is almost always faster than building from scratch, specifically because you can use the knowledge and lessons learned from previous mistakes. Incremental changes and improvement will buy you time in the long run.
The short answer
While working at a startup, you may want to completely rewrite the codebase. But this is seldom a good idea. We think that 95% of the time, it will be easier, better, cheaper, and faster to refactor your existing product.
Antiquated vs. old code
Remember, if you are building a new product — because of an audience change or you are solving a different problem — then it does make sense to start from scratch. In other cases, the code quality of the existing system can also play a role in making a decision to rewrite.
We do not use the words old or outdated, and there is a good reason for that. Old code can still be good and written cleanly and thus will be easily updated or rewritten.
Outdated technology or frameworks might need a bit more attention to get updated and leveled up to standards. Even bad code might be easier to clean up, refactor, and upgrade when compared to starting completely from scratch.
The upgrade path is too complicated
You need to understand that things will break sooner or later, especially if you do not touch a server for 6 months. This is why teams allocate resources for maintenance. But why does this happen?
There are dependencies, code libraries, and packages that go into making your product. Third-party pieces of (open-source) software are often used to help speed up your development.
These things get updated by people (or systems!) whose timing you cannot control, and while it’s okay to skip a few versions, you will, inevitably, have to keep up with modernization. This is especially true when security patches are released!
Antiquated codebases typically have not managed this properly, and all of a sudden, it makes it next to impossible to “quickly” update your codebase from PHP Version 3 to PHP Version 8, for example.
Every upgrade affects one another, and luckily, there are plenty of blog posts about upgrading most technologies. If upgrading documentation of your chosen stack is scarce, you might find yourself using antiquated technology.
Oh, and don’t forget your operating system, coding language, database, and server technology all need updates regularly as well. Consider a software rewrite when the upgrade path is too complicated.
End-of-life software
End-of-Life (EOL) basically means that the third-party company or developer has stopped providing (technical) support, new updates, and security patches for the software or tooling you are using.
Generally speaking, software products have a lifecycle of 3 to 6 years, give or take. If your product — or the technologies your product relies on — are reaching their due date, this is another factor to take into consideration when making the decision for a rewrite. Many pages track end-of-life dates, such as laravelversions.com.
Data breaches, breakdowns, inability to move services or parts of the product to a cloud environment, and missing customer support are typical signs of a technology or product reaching its EOL.
You might also check the number of open issues, the volume of change requests, and the frequency of new releases for open-source projects. When a major component of your product has reached end-of-life, consider starting from scratch.
Paradigm shifts with new technologies
When talking about antiquated codebases, there are a few concepts you need to understand. First, there is the chosen stack. Five to ten years ago, a popular stack for web applications was the LAMP stack (Linux + Apache + MySQL + PHP); whereas today, it’s very popular to use MERN (Mongo, Express.js, React, Node.js).
This does not mean your 5-year-old LAMP product needs to change to MERN just because that’s what all the cool kids use. Understanding the strengths and weaknesses of your chosen stack gives you valuable insights when deciding to rewrite or not.
Sometimes there has been a paradigm shift within a specific technical domain, and you must rewrite your product from scratch to stay competitive. This can occur in areas of applied computer science, such as when working on advanced game engines or certain machine learning applications.
Even if you created the product 6 months ago, the paradigm shift forces you to start again. If you suspect this is the case, you’ll need an expert to help you determine if it’s true.
A good CTO should keep a pulse on the technical horizon and know what innovations will be disruptive. Then, your team can plan to respond and update the system with enough time to lead the market.
Let’s talk about tests
Tests over critical business logic make everything easier. If you have good test coverage, then it’s much easier to refactor your codebase, making small improvements to keep it healthy.
According to Hamlet D’arcy: “I don’t know how much more emphasized step 1 of refactoring could be: don’t touch anything that doesn’t have [test] coverage. Otherwise, you’re not refactoring; you’re just changing shit.”
On the other hand, if you have an antiquated codebase with no tests, it might just be easier to start from scratch and ensure that the team agrees on how the product should function.
A checklist for making a decision
- Determine if the audience or core problem has changed
- Understand the test coverage on core business rules
- Research end-of-life support for the major components of the product: currently used software languages, frameworks, and libraries
- Estimate complexity and cost for upgrading in place (refactor)
- Estimate costs for rewriting from scratch, including:
- maintaining two systems
- staffing impact
- user or data migration
- Share findings with advisors and request comments
- Hire an impartial technical expert to give you a second opinion
Getting started with refactoring
Congratulations! You’ve made the smart decision to refactor your existing system. What now? The first thing to do is focus on the core problem at hand and do some research.
Research first
Start with the simplest user or use case and focus on documenting it. Define the goals the customer is trying to solve and confirm your assumptions and findings with qualitative research. Then do the same for the other customers you support.
You’ll want to understand and document the product vision very clearly before you can begin development. Once that’s been reviewed, your CTO can create a technical vision and strategy before making any changes to the system.
Applying the right strategies
Your engineers will need to apply several strategies in order to successfully rebuild the system. We will write a more in-depth article soon about these approaches. For now, we’ll briefly discuss them.
The first strategy is called the “strangler pattern.” It’s named after strangler vines that grow up from the base of a tree, eventually suffocating it and taking over.
Your team starts by creating a new codebase with the modern libraries and frameworks configured — this sounds like a rewrite but trust us, it’s not! Next, they configure the new system to handle all incoming requests and then forward the request to the existing legacy system.
The system is deployed in this state, so the new code is being used immediately. Then, little by little, specific functionality is extracted from the old system and into the new one. As this occurs, the legacy codebase is slowly deleted.
Since you’ve already identified the simplest use cases, you can focus on those ones first, building up complexity over time. The new product builds up until it’s handling everything and your codebase aligns with the technical vision.
The second strategy is to avoid bulk changes. Suppose a small improvement can be applied across the entire application. The right choice is to wait until later to do that work when engineers are already working on each individual section of the code. This avoids touching dozens of files at once, creating a massive pull request that is impossible to review.
Third, when refactoring, make easy changes. To quote Kent Beck, “for each desired change, make the change easy (warning: this may be hard), then make the easy change.”
Incremental improvement
Research, prototype, validate, polish, deploy, repeat. Do one thing at a time. And dedicate plenty of time (roughly 50% of engineering effort) towards keeping the system up-to-date. This way, you won’t fall into old ways. And don’t forget to communicate — even the smallest of changes — with your audience.
Refactoring should be part of your day-to-day routine. This is most easily done by setting a scouting principle named after the Boy Scouts of America. As a note, we feel this organization can improve when it comes to diversity, equity, and inclusion.
“Try to leave this world a little better than you found it” is a quote from their founder, Sir Robert Baden-Powell. If your engineers take this to heart, every time they touch the code, it will improve.
The road toward a stable and well-crafted software product might be long and winding, but it’s definitely worth taking. Learn more by reading about technical debt and creating a strategy for slowly working your way through it. Almost every application we’ve seen is worth investing the time and effort into code cleanup — instead of starting over.
Member discussion