7

I'll try to be concise so as to not make this into a blog post. If there are specific questions, let me know and I'll do my best to answer them.

Basically, I'm interested in what methodologies people have come up with, and what benefits and short-falls they experienced, for managing the transition of a project from one framework (or one programming language, or one technology stack) to another.

The Setup

Suppose you had a simple web application written in a once-popular back-end framework that ran on a single Apache/Nginx/whatever server and delivered server-side rendered pages. Each time a user clicks a link to perform an action, the request is sent to the server, the one server handles the request, connects to the one database, makes the changes, then delivers a new page with the results of their action visible.

Now suppose 10 years down the line your app has grown into a widely popular behemoth and you're running into scaling concerns, you're having performance issues, you can't create features very quickly because all of the code is tightly coupled, some big names have started depending on your service being "Always Available" ™, or for some other reason you now need to change the engine underlying your app.

After weeks of research you may have made one small decisions like "Let's use React and offload a lot of the processing and display logic to the client's CPU", or you may have made multiple decisions involving splitting out your app into several micro-services and re-inventing the database using a map-reduce algorithm and a splash of machine learning. Whatever the final decision was, you need to retain feature parity with your old app and get it moved over to the new framework.

The Question

This question isn't about the technical details of this decision (e.g. "How do you do that") -- this is about the managerial details of this decision. How do you manage such a change?

Do you treat it like you're building a new app from scratch and every feature in the old app is just a user story in the new one?

Or do you treat it as a new feature for the existing app?

Should you branch off of the old code and begin the rework on a new branch of the old repository?

Or should you start a new repository for the new framework, since merge conflicts are not only expected but almost a roadblock as your underlying models and assumptions could be entirely different?

What becomes of the old app during the transitional period? Should you feature-freeze it to make parity easier? Or should you continue to develop in parallel?

I've seen a lot of methodologies for managing projects and I've picked and chosen my favorite tools from everything I've seen. But I've never seen someone manage to change frameworks well. Even something supposedly simple like moving from ReactJS to Vue (they serve the same purpose, they're both written in JavaScript, and many of the underlying assumptions and mental models are identical) can lead to months of development and wasted time before the change is scrapped because it just wasn't worth the effort.

Tags

I literally had no idea what tags to choose. Please help.

2 Answers 2

5

I've done this a couple times, tho it wasn't always a webapp & wasn't for scaling reasons. Here's what we did:

  • Treat it like a new app. In our case, we renamed the app when we did that.
  • Put it in its own repo.
  • Freeze development on the old one except for critical bugfixes, so all resources can be devoted to the migration.
  • Pare the feature set if possible, at least for the first release. EG if you have features that are rarely if ever used, don't commit to them. Obviously do this in consultation with stakeholders, explaining that you're trying to manage scope to keep the schedule reasonable.
  • Define acceptance criteria very clearly. Help your stakeholders understand why the behavior, appearance, or results may not be identical, because of under-the-hood stuff they don't normally think about and/or because the new framework has different things built in.
  • Anticipate that you will find bugs in the existing code, that should be corrected in the new code. However, bear in mind that you may need to patch the new code to reproduce the old code's bad behavior for ease of acceptance testing, with the understanding that after acceptance, you'll remove those patches and get the new behavior approved.
  • Resist the temptation to make changes or improvements in behavior during the migration. File tickets for them; maybe leave architectural hooks for them, or comments in the code; but don't try to "migrate to new framework" and "change existing behavior" at the same time.
  • Plan to write a commissioning report at the end, that explains how you vetted the new code against the old code and addresses any changes that were made, including an appendix with any relevant test reports. Write it as you go, it'll save you grief later.
  • Allow yourself a generous amount of slack in your schedule, because you WILL likely run into unanticipated technical difficulties. It's always better to underpromise and overdeliver.
  • Make sure your stakeholders are aware of that schedule, & buy into it up front. This manages expectations & gives you something to point to when they get impatient that it's taking so long.

A couple of technical points:

We already had a large set of regression tests & the concern was mostly about numerical results. We put all our patches-to-reproduce-bugs into the same module with an obvious name, and kept the correct code present in comments, so that it would be easy to remove the patches when done.

There's a tension between doing the easy things first so you can prove the concept and get a subset up and running and under review as soon as possible, and doing the most complicated things first so that you don't end up having to re-architect halfway through. The usual agile assumption to do the simplest thing that works & refactor as necessary as you go along is a bit of a mismatch to this situation, in my opinion, because in this case you DO know "you're gonna need it". I might suggest having part of the team investing in analysis of the most complicated bits while the initial infrastructure, technology selection/learning curve, etc is going on.

I mention this because this was the reason one of my migration projects ran significantly over the planned schedule: we did the easy things first, and then had to shoehorn complexity in under time pressure.

Hope this helps! I'll be interested in other answers as well.

4

I don't think there's a "right" answer to this. Vicki's answer is perfectly valid. I'm going to give another answer that I think is also right.

Start with making a list of the biggest problems your application has now. Maybe that is database redundancy, load time, architectural fragility, test coverage, whatever. Now sort that list into an improvement backlog. Now start at the top and work your way down.

Easy, right? Well, there are a few things to consider:

1) is your application's code loosely coupled or, better yet, decoupled? How many pieces can you separate out? If you have a lot of tightly-coupled components, you're probably stuck in a full-replace approach like Vicki suggests or you have to put in some effort in improving the architecture and maintainability of your application before you can do anything else.

2) Small partial fixes or big fixes? Let's take your React example. Let's assume for a moment that your existing UI communicates with web services for business logic. You could just replace the whole UI side and let react communicate with the web services. That may also mean some adjusting of the web services for REST if you used SOAP or the Microsoft web service protocol before. This is a big fix for the problem of resources. On the other hand, a small fix might be as simple as adding hardware or a load-balancing solution. Doing that may not permanently fix the problem, but may drive the problem much lower on the backlog. For the cost of a server or three you just bought yourself a year of in your application and you can focus on other problems that are harder to solve than buying some extra CPUs.

3) Focus on solving problems, not implementing designs. For each of those items, the goal is to improve load speed X amount or reduce errors to Y threshold. If you are just implementing a predefined solution bit-by-bit, you may be better off taking a full replacement approach, because you're basically doing that anyway. This approach only pays off if you're getting the benefits of solving each problem as you go.

As I said, there isn't necessarily a "right" answer, in which of these approaches to pic or how you'd go through this approach. In this approach, you iteratively make the best choices at the time and get benefits as you go. In the other approach, you get to start from a clean slate with no constraints, but you only find out at the end if your new approach solves the problem. You have to look at your situation and decide which is right.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.