Refactoring - Is It Worth It or Not?
Every project development consists of different stages. In software development, the business practices of building applications are referred to as a life cycle. The software development life cycle (SDLC) consists of different stages, usually, 6 to 8, like planning, setting up requirements, creating a design, building, documenting, testing, deploying, and maintaining.
However, every project in its lifecycle gets to the point when refactoring is needed. But what is refactoring and what are the pros and cons of it?
In simpler words, code refactoring means restructuring but keeping the functionality at the same time. Refactoring is needed when software engineers need more abstraction or when the business logic has changed and the code which worked well in the past won't fit or won't be optimal/effective anymore.
Project managers, project owners, and non-technical people often ask developers: "Why do we need to spend time and money to refactor the existing code which perfectly works if we get the same functionality at the end?". Well, actually, developers spend every day at least 20% of their coding time refactoring the existing codebase! Whenever developers implement a new feature or fix bugs, they need to touch those part of the code that does not directly belong to the original domain. Saying this, doing refactoring is part of the everyday work for a developer.
The Payment API Project
One of our clients entrusted us with moving forward with their existing application. The application is an API, a payment gateway between many-many payment providers like PayPal, Sofort, Apple Pay, etc. The scope of the project was to add new functionalities for fixing bugs and maintaining the infrastructure.
The technology scope is PHP 7.1 backend with Symfony 3.4, MySQL database, and Redis, in AWS EC2 cluster with 4 instances and a load balancer. The application had to serve a couple of thousands transactions per day.
The challenges and the solutions
When our development team took ownership of the application, they realized there are some gaps in the logic:
- it used event bubbling which was not the best for the performance;
- missing indexes in the database;
- bad code structure with lots of unused/deprecated code fragments.
The first idea was to do the refactoring in small pieces. Meanwhile, the number of incoming requests has started to grow because of the new clients and merchants lots of payment data that has been migrated into the database from the other systems. All these happenings with the bad structure of the database and code caused more and more issues. They made harder not just the feature development but the stability of the application as well. We couldn’t get reports from the database in a proper amount of time because of the slow queries.
Whenever we had issues, it was hard to identify them because of the bad logging. Long response time with third party services and bad design caused transaction failing and when we started to fix an issue and dig deeper into the domain, we found more and more bad logic and implementation into the codebase.
First, we tried to scale up the infrastructure just to save some time, but we achieve a point soon when it could not work anymore. So we started to talk about these issues with the client to help them understand what the problem is and how it can evolve. We created a time and effort estimation to show how much time it could take to update the PHP/Symfony version, refactor the data model, etc. We also compared it to the time and effort of creating the same application from scratch. We collected all the pros and cons and calculated a lot of stuff, e.g. how much time it takes to add a new provider to the old app compared to the new one, how long it takes to find a bug in the code in the new structure, etc. While the management was discussing the financial side of the work and was trying to choose the best option for the long term, we created the architectural plan for the new application.
The plan was not just to rewrite the application as it is, but we had to be able to scale it up vertically and horizontally as well. We decided to separate the application from the services. Every service has its task, the gateway had to process the incoming requests, communicate the database, do all the authentication/authorization work, call the 3rd party fraud service to check whether the payment can happen or not, collect the merchant data for the provider authentication, and then only pass the necessary information to the provider service. This service transforms the given data to the specified format for the payment provider, calls it, and then transforms the response to a common format and returns it back to the gateway for further work. The scheduled tasks service for report generation and all the background tasks and non-time-sensitive processes have their own separated service. Like the front end with the dashboard for the managers and the user manager part as well.
One of the largest changes was the database and data model. In the old system, we stored the data in bad normalization way. Sometimes we had the same data twice or more in different tables. So, we created a normalized database structure, eliminated the non-used attributes/duplications, and added the proper indexes, foreign keys into the tables. As a result, we got back the power. The queries are now fast, the access of the data is also fast, and the load of the database has been deceased. The other important change was that we increased the usage of the Redis to store as much data as we can in the cache to avoid unnecessary database queries.
Refactoring - Is It Worth It or Not?
In our case yes, definitely, but it's not always this easy. Most of the time deciding whether a project needs full refactoring or rewriting rather than moving forward with fixing a ton of smaller pieces to achieve better performance is hard because it depends on a lot of parameters. Measuring the refactoring of a huge project like this is not always accurate.
Sometimes it may seem easier to write everything from scratch, but it has a lot of side effects. When the database changes, old data has to be migrated. When the API endpoints change it causes extra effort from the client-side to implement the new version. However, there are cases when this is the only solution.
So, our advice is the following: before saying that you need to start again from scratch, step back and start investing all possible options. What if you take out a critical part from the code and put it into a separated service? Does scaling up the infrastructure help? What about buying an application from the market and personalizing it?
These are just a few topics that are worth thinking of before rewriting an application as well as before creating a totally new one.