This article is going to be a bit different than the previous two. In the first two articles, I talked about topics that were in the past. The rebrand cleanup is still very much in progress. Everyone is now busy cleaning up as that's almost as important as the project itself. It lays the path for the future of the product.
After a major migration in the product, you may be left with a lot of legacy code. That code may not even be used anymore. But tracking what it might affect and what bugs may potentially be introduced by removing it are still very real threats to the product. Even harder it is to coordinate this at scale, with multiple teams at once and continuous deployment.
Clean-up
At the point of release, we already had set the redesign feature flag to true by default on the production environment.
REACT_APP_REDESIGN=true;
This means that from this point on, all the developers would see in the app was the Nuri version of the app, and everything related to Bitwala was hidden completely. But the code for Bitwala screens and components was still in the app.
This flag would also be overridden in case we'd need to rollback the app. Since building and uploading a new app bundle to app stores takes a long time (building the app & app store approval), we needed to override this flag reactively from within the app. That meant there's a loading screen displayed on each app opening, which checks with the server whether we want to rollback or not. It degraded the UX of the app a little bit but since it was a safety measure and it'd be removed after initial release, it was well worth it.
After the release went well and we had no reason for this safety measure anymore, it was safely removed and the loading screen with it.
In a previous article Nuri — formerly Bitwala; How To Migrate A Brand, I described how did we go about migrating the app to its new design. And obviously, we cannot do a proper cleanup without first understanding how we got there. But now with that said, let's go step by step through the things that we intended to clean up.
Step By Step
Let's start with a list of things that we'd want to get rid of. And let's put them in order in which we're able to remove them as in some cases, they depend on each other.
There are three main things handled by specific feature teams. They were able to set up their own rules and plan for these things separately in hopes of giving them the freedom to go about it the best way for their own needs.
Old screens and their logic. These are screens that are no longer used anywhere. They belong to the Bitwala app only and are not even navigated to in Nuri app. Technically, they're still accessible but only in local environments. They are hidden from production.
For screens that were replaced with the same name, we introduced .deprecated.tsx suffix and migrated between the two in their appropriate index.tsx. We now could simply remove the deprecated file and export directly only the Nuri version of the code.
In-screen migrations. When migrating a screen didn't require many changes after the initial component migration, we were able to change just a few details. This saved us valuable time earlier as we didn't need to rebuild the entire screen.
As opposed to the previous step, this requires changing more of the Nuri code. But the changes should've been more minor in the first place. This requires removing the redesign flag from the screen-level components.
Feature-specific utilities. This step may not be necessarily required. But if we already know we're not going to use a certain utility function or if we already have a better version of it, it's better to remove code that's not used and therefore probably not intended to be used anymore. What could happen is that someone could import this older version of the utility function and expect it to work like the newer ones.
But there's a lot of shared code between the feature teams. For this, we need to have a cross-team communication channel to be able to track everyone's changes and monitor any potential breaking changes into them. It's critical that each team is aware of the changes. We went for the easiest solution and introduced a Slack channel for some members of each feature team and channeled the clean-up communication there. As long as everyone is responsible to announce all changes to everybody else, we should be good.
Deprecated shared components. Whether it's UI components or components with functionality that was used across teams, it fits into this category. This is a very tricky functionality to change or update. Doing so requires attention from every team and obviously also heavy QA.
What makes this a bit easier is that we also approached migration of components very similar way to migrating the screens. So removing deprecated components would require removing .deprecated.tsx file from the component folder and adjusting export in index.tsx file.
That's is where planning for cleaning up after a project really pays off.
Theme migration. This would be the overall theme of the app and also the web app. Since any of the steps above may include the usage of overrides of old colors, we needed to preserve this until the very end of the clean-up.
Redesign feature flag. At last, any functionality relating to redesign feature flag setting and its overrides can be safely removed if there's no more conditional code depending on the flag.
All of these things in the list would be used in case of a rollback. They helped us to stay in the safe zone of being able to switch back to Bitwala in case something really bad happened after the production release.
Complications
Due to the way we approached migration, cleanup couldn't be done in all cases by simply removing certain files. For example, to make components different, we'd sometimes migrate even overrides so that we wouldn't need to go and change overrides one by one. What this means in practice is that if we'd have a Button that has a default blue color, but we'd decide to override it with another blue
In the old design (Bitwala), it would work as expected:
// Defaults to `accentBlue` background
<Button />
// Override to `warningOrange` background
<Button background="warningOrange" />
But since we didn't want to change every single override in the code, we decided to migrate even the overrides in the new design (Nuri):
// Defaults to `primaryLilac` background
<Button />
// Override doesn't change to `warningOrange` since that was a color
// in the old design. Now the component automatically migrates it to
// Nuri-themed orange color.
<Button background="warningOrange" />
This meant we have a lot of overrides around our code that didn't necessarily represent the exact color that was stated in the override. This kind of thing needs to be handled manually and can take quite a lot of time. Even if we'd automate it (using terminal commands or using IDE features), we still need to manually go and check each instance.
There was really not much we could do about this. Nothing other than learning a lesson and try to limit any overrides like this in the future to allow us for easier migrations.
After this process is finished, it will leave us with way cleaner code that'll be more maintainable for everybody. It will be less prone to bugs and developers will be able to implement new features more quickly without worrying about legacy code introduced in the redesign project.
· · ·
If you're working on such a project that will either introduce code that'll soon become legacy code. Or if you're working on a project that is there to replace a legacy code. Consider estimating it along with the clean-up after it's released. Working with legacy code is tedious and by proper planning for clean-up within the project boundaries, you will be able to avoid major headaches in the future.