p.s., I originally wrote this blog here - cross-posting to my main blog for visibility.
In my experience, every successful application will eventually reach a stage, when it becomes worthwhile contemplating, if it is a better investment to continue to improve on the existing codebase or to rewrite the whole application.
The gravity of this decision is huge, as the consequence of getting this wrong can be disastrous. Many experts have pointed out that this exercise is not even worth considering.
It is good to learn from projects that succeeded or failed. Every project is unique and patterns that work in one organization might not translate well into other companies. But it is worthwhile looking at the available literature. Here are some:
Why should we consider the option of rewrite?
Software is supposed to be infinitely malleable - so we should be able to change any piece of software -right?
And only successful software becomes legacy - so that is valuable as well. And, if the current system is not broken, why fix it?
All these are important considerations while contemplating on the question of application rewrite.
Also, I want to draw a distinction between refactoring and rewriting. All well managed products should invest in constant refactoring of its codebase.
Failure to do so will result in reduced shelf life of the application and making it more ripe for rewrite. In spite of constant refactoring, there could be many reasons to consider an application rewrite. Some of the most common reasons are:
- Existing code has become too difficult to handle and extend. Even simple changes have cascading unintended consequences. This makes it difficult to add new features and increases the cost of maintaining the current codebase. This could result in a system that is changing slower than its competition. Even for a market leading application, this could mean slow death as slow growth equal slow death.
- It becomes increasing difficult to motivate newer engineers. General enthusiasm decreases. If a happy team delivers good products, the opposite is also true.
- As the product evolves, design decisions that were appropriate for that time could become a liability now (historical design mistakes) - Invariants that are built into the system makes it near impossible to repurpose the system, when we find newer approaches.
Let’s look at some reasons why software rewrites fail
These are some high level patterns that we have to keep in mind:
- Underestimate effort: The crufty-looking parts of the application’s codebase often reflect corner cases and weird bugs. An immature approach to estimation can overlook the many man-years of development that went into creating the current software. Underestimating the project can lead to the initiative failing because of cost overruns.
- Setup for failure: For a project of this magnitude, the team should start off by setting appropriate expectations with the business stakeholders. The team should not fall into the trap of over promising to justify the cost of the rewrite.
- Morale effects: A project rewrite can split the current team. Given that domain knowledge is not evenly distributed among team members, splitting up the team can cause resource issues that are not anticipated.
- Elitist mentality: This can also have the unintended consequence of splitting up the team into cool guys and legacy members.
- Unrealistic Constraints: Usually, software rewrites are sold to business stakeholders with the expectation that, at the end, the current system can be switched off and all users will be on the newer system. This way of positioning the rewrite will setup the initiative for failure because of the following constraints:
- Feature parity: The project is never done until the target system achieves feature parity with the current system. This will apply for feature that might not have aged well, but customers have grown used to. Customers have grown used to a certain way of working in the current system and they don't want their cheese to be moved. This hinders innovation on the target architecture.
- If you are not in a saturated market, and if the current product does not appeal to a new-generation user, then sticking to existing features and workflows to satisfy the current user-base will guarantee that the product will die in obsolesce. “Golden Hand-shake”
- Moving target: Usually massive rewrites are multi-year initiatives and the current product continues to evolve. This makes the goal of achieving feature parity that much more difficult.
- Less resources on existing product: Splitting the team also results in lower resources across both existing and newer initiative.
- High burden of correctness: This is easily overlooked aspect. The current product has benefitted from incremental releases are tested and validated in the field. And corner cases are fixed. A completely new system with all the existing feature implemented new has to short-circuit the otherwise evolutionary release process. The entire release has to be correct on day one. Which in reality will never happen. This means that at launch time, the system will be perceived as defective and less stable.
- Migrate Data: In my opinion, this is the biggest cost of rewrites. This will be the topic of a new post. Watch this space.
In my opinion, the best way to fight product obsolescence is to disrupt the current product from within. Instead of rewriting the current product, create something innovative in a related category. This is similar to what BaseCamp did. Instead of sunsetting BaseCamp, the team committed to perpetually supporting current product as BaseCamp Classic and they launched BaseCamp 2. There was no guarantees on upgrades. Existing users can stay and will be fully supported. While doing rewrite, its best not to burden yourself with existing users. This gives the team immense freedom:
- Freedom to drop features without backlash from current users. Not constrained by influential existing customers.
- Freedom to launch Minimum Viable Product (no need for feature parity). Launch a new product optimized for current non-consumption and learn from it.
- And above all, freedom to not migrate data
For BaseCamp, this model was so successful that they repeated this pattern and created BaseCamp 3 as well. Another industry example is the case of Visual Studio and Visual Studio Code.
Gmail team did something slightly differently. They came up with ‘Inbox’, a different take on Gmail. But, they decided to keep the same backend. This meant that users could potentially switch between these two experiences. And also, they eliminated the need for data migration (remember, that is certainly a big burden). But, this also put significant constraints on the amount of change they can introduce.
Enterprise application rewrite is non-trivial. You get better success with launching a new product that is optimized for new customers. It might make sense to split features into micro-services and let the newer product use that. This also gives an opportunity to add these capabilities to the classic product and avoid data migration.