If you have worked in the software industry, chances are you’ve encountered a program that needs to migrate an application or feature. Often the term “migration” brings a sense of fear and foreboding, but it doesn’t have to. At Softrams, we have often taken ownership of applications that use legacy technologies and require some modernization to be sustainable for future use. We’re going to share some insights we’ve gained in doing this, as well as outlining some best practices that will help your organization if they find themselves needing to perform a migration themselves.
What’s a migration?
A standard software lifecycle is to define a product, build it, launch it, and then maintain it. Occasionally during the lifecycle of a software application it is found that the original way something was built isn’t adequate for the entire life of the application. There may be new requirements that the first build of the application aren’t well-positioned to support, or perhaps one of the platforms the application was built on will no longer be supported. In these cases, you may have to take your existing application and “migrate” the functionality in some way. This could mean:
- Your application is written in a particular language, and you need to move it to a new language or runtime (perhaps ASP.NET to Node).
- The application needs to be moved from one platform to another (hosting using Heroku to AWS, for example).
- You may want to split your application from a monolith to a microservice approach, or the opposite.
Each of these scenarios will require a very different approach and bring their own challenges. For instance, changing languages depends greatly on
- Developer knowledge of both languages. If developers are weak in one or both languages, you may consider training to ensure that the migrated codebase is written with best practices.
- Developer knowledge of the business logic of the application. If developers do not understand the existing codebase, it is likely that subtle bugs will be introduced during the migration due to a misunderstanding.
And each of the examples above will present their own gotchas.
How do you approach a migration?
When the challenge of migrating your code base is presented, it’s essential for the team to approach the migration intentionally. To have a high chance of a successful migration, ensure you:
- Understand why this migration is necessary
- Identify the specific goal of this migration
- Document all requirements necessary for the migration to occur
- Identify and communicate the risk associated with performing this migration
Let’s break each of those down a little further and understand why they’re crucial to a successful migration.
There are very few directives for a development team less motivating than being told to do a bunch of critical, risky work without adequate reason. There can be many real and important benefits to performing a migration, and any initiative needs to start with why. If the team isn’t motivated to achieve real benefits with a migration, you will find it takes longer and carries more risk.
Identify the goal of this migration
Though understanding “why” a migration is occurring is helpful, it should be taken one step further to also ensure that each migration has criteria for success. This could be as simple as specifying that “my application needs to work on AWS instead of Heroku” or it can be a specific metric such as “bringing the application launch time from 20 minutes to < 1 minute”. Either way, identify criteria for success and ensure the team understands how they succeed.
Document all requirements
The complexity of this documentation will depend on the type of migration. If you’re rewriting a codebase from one language to another, you would want to highlight any language specific considerations that may impact delivering comparable functionality. If you’re migrating platforms, you’ll want to ensure you understand exactly how you want your infrastructure to behave with robust analysis. This type of documentation also gives you a nice checklist to work through as the migration occurs and can provide visibility on how the migration is progressing.
Identify and communicate risk
Any changes to a software application carry risk. Whether you’re making small changes to a layout, or changing core aspects of your system, be assured that change brings the possibility of error and the larger the change, the greater the risk of error. Before you decide to migrate any part of your application identify what the major risks are and agree on mitigation strategies. For instance, if you are migrating the hosting of your website, the last step may be to route all traffic to your newly hosted site and a solid failsafe is to keep your existing site up and running in parallel for a while so that you can revert to the existing instance in cases of failure.
As mentioned above, different migrations can have their own challenges. Below, we outline some specific things to keep in mind for each.
Lift + shift
A “lift and shift” migration is one where you take the exact functionality and code conventions of an existing codebase written in a specific programming language and rewrite it in another programming language by changing as few things as possible. Sometimes this means that you can copy an entire codebase with only some simple syntax changes.
When to consider this:
- Your timeline is tight and you have a small amount to migrate
- You think there will be time in the future to revisit and refactor
- Your existing application architecture and structure is solid, with sound conventions
What to examine before pursuing:
- Does the new language support all existing conventions of the current language?
- Are system requirements documented well enough to inform developers of how to make decisions when a clear “shift” is not possible?
- Are there any poor coding practices present that may be in need of a refactor? If so, this may not be the correct approach.
Refactor + shift
In contract to a “lift and shift” where you avoid changing code as you migrate it to a new language, a “refactor and shift” approach will require a more deliberate plan and coordination. This approach takes an existing codebase and rewrites in another language or framework, maintaining existing functionality and core structure while improving the structure and consistency of a codebase.
When to consider this:
- You have an adequate timeline to complete the migration
- You have solid engineering leadership
- Your system could use a little TLC on the maintainability scale
- Make sure you define how far to take the “refactor”. Some developers hear the word “refactor” and don’t know when enough is enough.
- This approach requires more robust testing and quality control, since a larger portion of the application will be changed and unintended side effects may creep in.
Sometimes you have an application that was either poorly written or written with requirements that are no longer relevant. In this case, you take the desired functionality and purpose of the system and rewrite it from ground up. This option provides an opportunity to change frameworks or languages, if desired.
When to consider this:
- You find yourself consistently unable to meet new requirements due to prior (limiting) architecture decisions.
- The purpose or requirements of the system have changed dramatically since its original launch.
- You company can accommodate a timeline that prioritizes refactoring to new features.
- You have well defined product requirements. Scope creep can affect re-writing software just as much as a new product, and you don’t want your migration to be affected by ill-defined requirements.
- Having a working application brings stability and confidence that vanishes when you decide to launch fresh. The moment you decide to do a rewrite, you need to be prepared to treat the initiative as a new product launch.
- The only sure way to know how long it will take to develop software is to develop it and then measure the time it took. The larger the initiative, the larger the potential variance in timeline could take.
Migrating a codebase, especially when it supports a live application, can be an intimidating task. There are times when it is the best course of action for the lifecycle of your application. In these cases make sure you set your program and team up for success by making good decisions and considering all your options.