The first steps into anything new can be difficult, and it’s often hard to know where to start with microservices. In previous posts, I have looked at the benefits and risks of microservices, compared its advantages to traditional applications and looked at a few of the surrounding decisions, technologies and approaches needed to be successful in the long term. But how do you begin to adopt a microservices approach, especially if you already have a large investment in a monolithic solution? Migrating from a traditional monolith application to more flexible microservices is a fundamental change to the application, carrying significant risk and complexity. Hence, a strategic approach is one of the critical pieces of the microservices puzzle.
Before we go through some ideas for adopting a microservice approach, it’s worth mentioning some of the failures of the past, and ways we can learn from them.
This was the failure of numerous SOA programs, which spent months and years preparing standards, patterns and roadmaps until the business was tired of waiting for the proposed benefits. Any successful adoption of microservices needs to demonstrate value at every stage.
I tend to over think everything to ensure I have covered every angle, afraid of taking risks or making mistakes. This leads, inevitably, to either an over-engineered solution or total “analysis paralysis”. Instead, the adoption of microservices needs to be done in a much more flexible manner, embracing discovery and evolution, not afraid to take controlled risks.
I have mentioned this one before, and will likely again, but a key mindset in delivering microservices is that you’re delivering a business product, not a project. This is one of the failures of traditional ways of creating applications, where the product is constrained by the project structures, rather than being driven by business requirements. Projects are often loathe to spend additional time delivering re-usable components for other business units, for example, and this constrains the ability of a team to deliver efficiently.
There are a number of strategies for migrating from a monolithic application to a microservice based approach, and there is usually no right or wrong answer. In the case of microservices, however, rather than taking a “big-bang” approach, I’d suggest a few alternative approaches, which minimize the risk and allow your microservices architecture to mature progressively.
In this approach, microservices are established in parallel to the existing application, duplicating work for a time. While it’s not always possible to do this, it’s a great way of refining the approach with little or no risk to the main application. Once the approach has been proven, at scale with real production data, users/functions can be cut over to the new approach. For example, you could create a customer service with basic functionality to create, read and update. In the monolithic application, create a call out to the service over an asynchronous messaging platform at the point when customers are created or updated. The great thing about this approach is that it allows you to discover the best approaches for the solution: embedding automated deployments, infrastructure, security, etc. without affecting the existing application. Critical microservice capabilities, processes and cultures described in my previous post can be established, refined and refactored without impacting the application functionality. Once the parallel services are proven, it remains only a matter of switching over. It’s even possible to switch the direction of data flow: modify the existing application to receive updates from the microservice, rather than the other way around.
The biggest downside to this approach is that for a period, effectively two applications need to be maintained. Any function or defect changes made to the monolithic application will also need to be re-applied to the microservices, and there are risks, overheads and costs associated with this. I’d also strongly recommend managing the release governance cycles, to ensure that the microservice components are deployed and updated in short release cycles, rather than the formal application or enterprise release cycles required for the monolithic components. If you’re locked into a quarterly release cycle, this approach may take a considerable amount of time and the effort and overheads of managing two applications may become too great.
Another approach is to look at existing application modules and extract these into independent microservices. This approach can potentially deliver immediate value for components that can be reused outside of the application context. For example, in a parcel delivery context, there’s often a calculation module used to provide a delivery SLA date. This date is used in billing and QA processes to determine whether a consignment was delivered on time. Extracting this as a distinct service allows the capability to be exposed to customers via the public website.
This approach has higher risks. Performance and reliability may be impacted if the microservice hasn’t been well designed or developed. There’s also a risk of propagating any technical debt from the monolith to the microservice. Lastly, this strategy can take a long time to deliver, as each module extracted will incur a significant regression testing cost and risk to the application and changes would likely be required to be done as part of a longer release cycle.
In some cases, it may be more appropriate to leave the core of the system in the monolithic application, and build new capabilities as microservices. I believe the Guardian website takes this approach, which eliminates the risk to the core application while embracing the flexibility and agility of microservices for new functional requirements. This approach particularly suits scenarios where the core functional domain is very mature (e.g. CMS systems), but new value-add capabilities need to be delivered quickly.
Whilst this is a great approach for the initial adoption of microservices, once you’re confident and mature with microservices, a complete migration is recommended to avoid maintaining a legacy monolithic application indefinitely.
Regardless of the architectural approach you take, any transition needs to be funded, and it’s worth taking a moment to consider how the above can be supported financially. Traditionally, transformative changes to ways of doing things were made through a Program, which usually takes the form of a large, enterprise mandated approach. Think programs and roadmaps, with enterprise architects working with program managers to form a business case and establish a program of works. During its heyday, SOA initiatives usually took this approach, strongly funded and supported by senior decision makers. This allowed companies to make large investments in platforms, tools and processes, but meant that actual business value took a long time to deliver. ESBs such as TIBCO were particularly guilty of this, where the upfront license costs can run into hundreds of thousands of dollars of upfront capital investment. On the flip side, the microservice approach has grown out of the failures of these initiatives, where the business moved on, tired of waiting for promised benefits that were rarely actualized. Microservice approaches focus on delivering quick value with little up-front investment. Ideally, microservices should be supported by a dev-ops model, where teams can take ownership of a business capabilities and service, from development through production support, as per the “You build, you run it” ethos espoused by Amazon.