My experiences while modernizing a legacy system
Tags: agile, architecture, microservices, tdd
Published: 2020-07-24 18:59:03

When was the last time you changed your laptop or phone? Or to be more precise, What was the reason behind that change? The reason for me was, the new gadget gave me a better user experience, had more features and had better accessibility. In short, it felt good. The old gadget was working fine, but it couldn't offer me the necessities which the new one was offering, everyone else had started using the new model, and more importantly I was unable to be on track with the on-goings. I think this holds true in software world as well.

Lately there has been a lot of buzz around microservice architecture, dockerizing apps and services, and deploying these containers to cloud. These all approaches are kind of go-to implementations if we are building a software from scratch. The reality is, currently many industries use applications that have been built ages ago. So in order to be on par with their competitors or simply to survive, they need to adopt these latest trends and be future ready. They should either develop a new product or enhance the existing monolith. The latter is commonly referred to as legacy modernization. Currently, the project I am working on deals with this. Let us skim through the journey, along with the challenges we had while doing this.

It all started when our current client came to us with a problem. The problem was, they are using a 15 year old application and they want to break this monolith and make it extensible and scalable, as well as enhance the user experience in a cost effective way. During inception we learnt that there are few slices in this monolith, which if modernized would deliver an instant business value. Therefore, we could still use the old app but few of its features would be revamped.The reason for doing this was:

   1) The app is being actively used, and hence revamping a slice instead of the entire app all at once would prove beneficial(as the users won't have to adjust and adopt at one go) 
   2) We can use strangler pattern in future i.e. if the entire change happen in phases, we could switch to the all new experience, once all the changes are made
   3) We can get timely feedback and user responses to ensure we are heading in right direction

Once we identified the thin slice, the question was, choices in the tech stack. Since it is a legacy app and an additional effort for maintenance was needed if there is a change in tech stack, we didn't change much of the tech stack.

Few changes that we made were:

   1) Updating frameworks
   2) Setting up test frameworks
   3) Setting up build pipelines for our new slice

The changes were made keeping in mind that, the client should be self sufficient when it comes to support and maintenance of the application.

Now lets dive into the tech aspect. In our new codebase we replicated the features from the old codebase and let me mention, it was not an easy task.

The code that was written was not at all testable and we could sometimes find various logic to be replicated at different places(this was usually because the code was old and many people worked on the same codebase at different times). The most difficult part was that not much of SOLID principles were applied to the code and hence there were many dependencies, and breaking and modularizing these chunks was difficult.

We started restructuring the code by using MVVM model. We identified all the view models and domain models. We had to refer few of the dependencies from the old codebase but we tried making this slice as isolated as possible. For mapping objects we used autoMapper and for achieving IoC/DI we used structure map.

Writing unit tests was one of the herculean tasks here. There was a lot of code and as mentioned earlier, code that is not testable. We used fsCheck and xunit for testing our scenarios. We tried TDD wherever feasible, and worked mainly towards creating a safety net for our application. For our functional tests, we used gauge which were triggered in our pipelines.

Few learnings and heads-ups:

  1) Adjusting to change is always difficult, so identifying the correct thin slice plays a crucial role here. Do note that this MVP could make or break further potential engagements.
  2) Understanding the domain is very much needed if you want to do TDD
  3) Once the slice has been identified, coming up with boundaries for apis is important. Remember, not everybody needs a microservice architecture or docker. The need here is to be able to transform to these. So identification of api boundaries can make the application future ready. For identifying these boundaries, domain driven design could help in few cases.
  4) Also, your code will go through a lot of changes in phases. Restructuring and refactoring will be a continuous process. So always strive to write evolvable code. Make use of SOLID principles and focus on writing clean code  
  5) Prepare yourself to write a lot of tests. These tests could prove highly beneficial in order to make sure that no existing functionality is breaking
  6) Add logs wherever required. How and why your app is working/not working shouldn't be a surprise
  7) If there are any configuration files to deal with, it would be wise to add validators for these files. An extra comma can sometimes make all the difference 
  8) Lastly, in such applications, there will usually be a lot of tech debts. Identifying and prioritizing them would help

This is pretty much I had to share. Do comment if you've come across similar challenges or have ideas to share.  

Happy Coding :)