Innovate around your legacy system!
5 min read.
In this day and age agile software development is the norm. No longer do we spend months on a complete specification before we start to write code. Today we know that we don’t plan a really good solution we discover it by working in iterations, by trying and failing and trying again.
When I say failing I mean failing to achieve the desired result. I am not talking about crashes in production, even though those can happen. You should strive to detect those kinds of failures in your delivery pipeline.
Discovering a good solution can be a problem, however, if you are stuck in an old and rigid legacy system. These systems often share some common attributes. They are often lacking a proper test suite and a loosely coupled design and they are often business critical.
But you have ideas for your business! Right? Good ones! Digital transformation anyone?
And you know that the only way to end up with a good solution is by trying and failing. But failing is not an option in your current system. The build alone takes a long time and you can only release in certain time slots. And most importantly your system needs to be up and serving customers!
So what do you do?
You could rewrite the whole thing from scratch, in an attempt to make it safer and easier to innovate in. Big rewrites, though, are almost never a good idea.
Or you could find a way to innovate around your legacy system. Extract the data and the events in your current business critical system and contribute back functionality, all in a safe way. You could have a proof-of-concept (POC) environment with an event stream based system.
In the illustration above you have a simple legacy system to the left and a POC environment to the right. You add a component (an EventEmitter) to your legacy system that can emit events, i.e gossip to the POC environment what’s going on and who’s doing what.
”But we don’t have events” you say?
- There are always events, in every system, even if you are not currently tracking them.
It could be as simple as: An administrator approved a new permission request. This could be an event, and it could carry all the data needed for some service to act upon this event without the need to “call back and ask for more” (read more about different event driven architecture patterns here)
The EventEmitter component emits events unobtrusively, i.e in a way such that it will not crash the legacy system if the event stream becomes unavailable. It should also be placed behind a feature toggle so that it can be switched off without having to do a full legacy system release.
In the POC environment you have single purpose POC services listening on an event stream and acting on events.These can be added, altered and removed without affecting the legacy system. Before you deploy a new POC service make sure you can monitor and measure the effect it has on the target business process in production, you need to be able to tell ”how well it works”. Also make sure you can deploy them separately.
I have, as an example, set up an environment like this with Docker and RabbitMQ on a Linux VPS but this can be accomplished with a number of other technologies as well.
A POC service can provide an admin UI, notify the team about something via slack, email, sms etc but it can also enrich the legacy system by contributing back functionality. Directly to browser sessions via a socket server is only one example on how to do this. When contributing functionality back to the legacy system you should do this with safety measures, timers and toggles.
Every POC service consumed by the legacy system should be consumed behind a feature toggle and fallback to a default value if the POC service fails or does not respond within a reasonable time. When you have these measures in place you can release new versions of your POC services frequently with confidence and see the effects in production right away.
When a POC service matures - when you are done failing and have discovered something that works well, you are ready to stabilize this functionality.
Now you can move this functionality to a more permanent home for production services. And if you were innovating and improving on existing functionality you can now ”switch off ” and later delete the old functionality in the legacy system.
You can also choose to not go the microservices route. You can embed the new tried and proven code from your POC service into your legacy code base as a component or a class instead of a stand alone microservice.
Either way, with this strategy, you are not doing the mistake of rebuilding from scratch and you are not rushing to the promise land of microservices - You are slowly and safely rejuvenating the system, one piece and one idea at a time.