One of the highest-risk activities with some monolithic applications is upgrading. To do so you basically need to upgrade the entire application at once. With even a medium-sized application there are far too many aspects to reasonably test. Thus at some point you simply need to cut over from the old system to the new system. With a microservice approach, the cutover can be done for each individual service. Smaller services mean that the risk can be spread out over a long time and, should something go wrong, the source of the error can be more quickly pinpointed: the singular new component.
At issue are the services which are still talking to the old version of the upgraded service. How can we continue to serve these services without having to update all those services too? If the interface to the service remains unchanged, say our service calculates the distance between two points on the earth and we change it from using a simple Pythagorean approach to using haversine (a formula to find the distance between two spots on a sphere), then there may be no need to make changes to the input and output formats. Frequently, however, this approach isn't available to us as the message format must change. Even in the previous example there is a possibility of changing the output message. Haversine is more accurate than a Pythagorean approach so we could have more significant digits requiring a larger data type. There are two good approaches to deal with this:
Upgrader services should have a limited lifespan. Ideally we would want to make updates to the services which depend on deprecated services as quickly as possible. The small code footprint of microservices, coupled with the ability to rapidly deploy services, should make these sorts of upgrade much easier than those used to a monolithic approach might expect. An example message upgrader service can be seen here: