Sam Newman, in his book Monolith to microservices: Evolutionary Patterns to Transform your Monolith walking through patterns designed to separating functionality from the monolith into microservices, described the pattern which even though might be hard to use and implement but could be very helpful if you are trying to migrate very risky piece of code to new service. This pattern is called the Parallel Run pattern, and today’s post will be about it.
Let’s imagine the situation when you have a critical for your organisation, a piece of code (some algorithm etc) and you have to migrate it to the new service. In a traditional scenario, you create a new service, write tests (unit, integration etc) and finally, you attempt to change the redirect from your old code in monolith to the new service. And you pray that all is well. In real life, this is rare. There will always be mistakes and edge cases not covered in tests. And you do not have a 100% guarantee that the new code works like the old one. This problem solves the parallel run pattern. You can run, at the same time, two implementations (the old one is still the source of the truth!), and for example, you can save results which can be compared later to get a 100% guarantee that new implementations are compatible with the old one.
How to implement this pattern depends on how the code which you want to migrate to the new service is invoked. If an HTTP request invokes your code, you can use a reverse proxy to call your new service. If your code is invoked within an old monolith system, you can, first of all, use a branch of abstraction pattern and call two implementations (remember that the old one is the source of truth for your system) at the same time. There are a lot of possibilities. Noteworthy is that the old code must be the source of truth, and it is a good idea to save results from both implementations for comparing them later.
You can test your service with no risk in the production environment with all possible edge cases, test network infrastructure (timeouts etc), resilience etc.
Implementation might not be easy, and you must remember that results from both implementations should be compared in some way.
You should consider using this pattern if you have a critical piece of code which you have to extract to the new service and you cannot afford any differences in the results, and extra effort is not a problem.
You probably should not use this pattern if the code you need to extract is not critical, extra effort is unprofitable, and you can use, for example, canary release instead of the parallel run pattern.
You must remember that you must make extra effort to write some additional tools to compare results from both implementations to get a 100% guarantee that the new code works well.