When replacing legacy software, too often the new software is forced upon users. Whether they like it or not. This article considers an alternative approach by persuading the users to use the new software voluntarily. By measuring the usage of old versus new one can determine the right time of retiring legacy systems.
Also published on linkedin.
Replacing legacy systems seem to have enormous benefits:
- Newer technology, showering buzz words, new possibilities! W00p!1!!
- Easier recruiting
- Power to the business, because IT is amazingly slow
- When done agile, this new system is superduper flexible (right?). Just thinking about it practically implements this new feature.
Whatever the reasons (realistic or not), there’s this thing still waiting for us. The actual replacement of the old, grumphy, nasty, legacy system namely. And the challenge with these existing systems is that they’re often still being used. Heavily.
In order to keep users or, even worse ;-), customers satisfied, the list of requirements is set in stone. And can be easily summed up: “It should do exactly what the old system did, but then better”. The ‘better’ is best depicted as the following:
Imagine the crowd to be your stakeholders, the waving flags their personal wishlists for new features. “While we’re completely rebuilding the system anyway, can we perhaps …” All those expectations…
And to make it a bit more challenging, old systems are often a result of enhanced insights in architecture. Or just plain creative (in a bad way), hybrid, schizophrenic, completely unrealiable, intangible spaghetti monsters.
We have to take the following challenges into acount when replacing old software:
- No noticeable disturbances for the users
- Manage ambitious expectations of various stakeholders
- Deal with a lack of, outdated or obsolete architecture
Staging a façade
In order to keep up appearances, and with that user/customer satisfaction, it’s always good to pull up a façade. If you don’t have already one. Basically a façade is a user interface with some loosely coupled links to a backend system. Note that the users are mostly unaware where this information is coming from.
It might be be that your frontend (clientside code) and backend (serverside code) are still intertwined. The best thing to do is to unwind those two disciplines first. You eventually end up with a separate frontend (the user interface) making distinct calls* to the backend.
Worst case: you end up with two more or less identical user interfaces. One is still intertwined with the backend and is not really being used anymore (but can be used awesomely for phasing out the application one page at a time, with the old code as a fallback). The second, new one is slowly overtaking the old one becoming the new façade of the system.
* It would be awesome to make these calls somewhat coherent and sound. Because then we can name it an API. And perhaps even make it available publicly, with some nice documentation what to expect. You just might make profits without having a user interface altogether. But that’s a different topic.
Not quite the empty shell it seems
What is the added benefit of just another new frontend?
- Since the façade is loosely coupled to its backend, the frontend can be released separately (and much quicker**; like with every feature or so).
- Frontend and backend developers are less dependent on each other (although there’s still this contract by means of an API)
- Behind the façade a microservice architecture can be introduced. This enables quick releases for backend, giving more flexibility for shifting priorities
- With this microservice architecture the backend can be completely rearranged incrementally so not to disturb the users’ workflows (too much). Chunks of functionality can be sliced off the monolith application. Just slice off until you have the monolith reduced to a size of a microservice. You probably end up with some duplicate functionality here and there. As long as it is manageable, patch-forward until the monolith has been completely dissolved into microservices that are more or less autonomous, not (too much) reliant on any other service.
If it’s not manageable, you probably picked a too large a chunk at once to devour.
At first this approach might feel patchy. For filling out an order, you need to go to the old legacy part of the system. For checking some invoice, you better go to the new application. To hold everything together, consider to setup some landing-page-kind-of-app. This app is merely pointing the users into the right, and eventually only direction.
It is important to keep all pages/ apps in the air. Both old and new. I get back to that later.
** Chopping whopping-big backends into microservices tends to speed up the release process. This is mainly because teams don’t have to wait on each other’s dependencies. Microservices can be released in parallel; try not to introduce a similar problem for the frontend discipline. For example by creating one monstrous Single Page Application (SPA). Rather, split the user interface in smaller apps for parallel releasing with some shell-app (or the earlier mentioned landing-page-kind-of-app) to connect everything together. Splitting can best be done by user groups and by the business process the app is facilitating. But it depends. And also a different topic.
For most of the microservices (backend) it’s pretty obvious when to retire the old service or part of the monolith. Whenever a call to a new microservice responds with the same data as the old one, shut it down. And thus you can start using the new one (with fancy switching using service discovery*** for some additional shine). Just make sure the performance is not decreasing (too much).
However, dealing with human interfaces (frontend) a gentler approach is often required. Especially if it involves users of the paying kind. That doesn’t excuses you for not treating non-paying users or employees any less gentle by the way: on the contrary. Most big IT implementations go hand in hand with loads of stress, frustration, missed opportunities and a hellhole of troubles, for both internal as external users. This is definitely the case with big bang-waterfallish implementations, but also common with more iterative approaches. The implementations are more or less forced upon the user. “Because this is new software, so better. And I don’t have anything to back this claim up.” Lots of dependencies make it hard to roll back. The amount of issues are staggering high making it hard to patch-forward. Result: the users are unwilling to adopt the new system peacefully.
Wouldn’t it be so much nicer if the users would choose by themselves to start using the new system? Something like candyshopification.
the process of creating something new (as an alternative to something existing) attractively addictive that targets voluntarily choose to abandon the incumbent something and are become your biggest fans and are filled with intense joy.
Or something similar. Not going to be adopted by any dictionary soon anyway.
Ever walked past that storefront with all that candy in it when you were a child? You just want to go in there. No need for paid boosters, expensive commercials, unethical campaigns… No, you just want to go into that shop voluntarily. Because you choose to. Because all that candy just looks so amazing, you don’t need any extra persuasion.
Well, when that magical moment has been reached with our new application (façade or not) any risks involved when retiring pieces of software will be completely vanished as well. Say everybody is convinced to use your new software. Nobody is using the old software anymore. By turning it off, no user will be bothered by it anymore. So no risks there. Of course your new software ought to be truly detached from the old software.
In order to determine when we can shut things down, we need to start measuring the adoption rate of the new software.
*** Can be applied to applications as well (a application discovery servive), if your applications deeplink into other applications not hosted on the same domain.
It’s all about the balance when replacing legacy
It’s important that both the old as well as the new software are still running in parallel. For each service and app that’s replacing an older piece of software, start measuring the usage. For services this means measuring the amount of requests for both the old and new service. For applications this means the amount of users using the application (both old and new). Tracking the usage of software can be nicely done with graphite/grafana for example. Make a nice dashboard to plot the results of old versus new usage. When the majority of users are using (an isolated piece of) the new software without looking back, you know that the candyshopification moment is within reach.
Some ideas that can help you reach a complete victory instantly; or rather some ways to just figure out what will help persuade the last bit of conservative users:
Do what big software companies do (“what would google do?”). Release the new software, but stick a beta logo on it somewhere. With an easy link to switch back and forth between the incumbent and beta version. In a beta version you can fix all sorts of ‘unencountered edge cases’ (better known as bugs; track them with tools like sentry). Gather early feedback via interviews with users or a feedback form (which you could hook to an actual forum application). If the beta version isn’t perfect yet it’s fine: users will not expect a full-flown shiny new piece of software without errors. Do take their feedback seriously, though. For example by getting back to them, or to communicate about upcoming features. Consider releasing the beta version to a subset of users or early adaptors.
Where’s this guy going?
With cookies you’re able to track users. Where they’re going for example within your software system. Log these, and plot them on a map of your software systems. Just on a high-level, so per isolated component. Include legacy parts as well (especially the ones you want to replace). Mark all components legacy or new. And then let it run for a while. This will give you insight in the workflow of users. Also, this gives you lots of information why pieces of your system are not adopted so easily. The software itself can be attractive enough, but can’t be used optimal together with older pieces. Also, such a map is just eye-candy, and definitely very cool overengineering.
AB-test the more experimental features
When in doubt what the best way to go is, and you’re not trusting the users or expert’s opinion (I fully agree by the way), A/B test the feature. Where the hypothesis of deprecating the legacy software overall usually is a no-brainer (and therefore validated learning is less exciting besides for the adoption rate) implementing new features can be a cumbersome, doubtful process. With A/B testing you can determine with small amounts of users what a good direction for a feature would be. When running multiple tests for a longer time in parallel, consider setting up a A/B testing framework to keep control over all running experiments and its results.
Spam to boost morale
When adoption is very low, don’t give up. Just keep on creating software to persuade users. However, spam or rather inform the (test) users a little about new (upcoming) features and invite them to try it. To tip the scales a bit more favourable, some occasional boosting never hurts.
Also applicable for developers
Developers are users of software as well. Not only editors, ticket tracking or version mangement systems. But also in-house delivery pipelines for example. There’s basically no good excuse to just change stuff without taking into account your users, geeky as they may be. Apply candyshopification, and you’ll see even the most conservative long-hairy AC/DC loving nerd is sympathetic for your cause.
Don’t just sit in the dark
It could be that nothing, whatever you’re trying, is working. It’s just that nobody wants your software. Maybe it’s the wrong proposition (then pivot). Maybe the need for replacing legacy is not that obvious after all. Maybe it’s something else. But if you don’t know exactly why users are not adopting your software, you’re in the dark. And you most likely are not gathering enough feedback. Pity.
Steps to take
A small summary of steps to take when applying candyshopification (eventually):
- Open up your backend for calls outside the monolith/ legacy system, so a different frontend can use the same backend.
- Put in a switch mechanism in order to forward api calls from frontend to backend services (initially all pointing to the same backend).
- Slice the backend into smaller microservices, re-engineer the whole shebang.
- When having a big frontend with (too) many dependencies, consider slicing it up too in several smaller apps. Just like microservices.
- Retire older backend services when the newer ones can do the exact same thing without loss of performance (or at least something acceptable).
- Retire (parts of) the frontend when the candyshopification metrics are favourable and likely to stay that way. Saves maintenance.