Risk free experimentation

Back in late 2013 we had two parallel product features to develop, and a monolithic application in which to do it. Splitting the team in two with each sub-team focused on one feature seemed like a good approach. The first team dove straight in and started making changes to the monolith. The second finished up other work and then one month later started implementing their feature as an independent service. The differences in productivity were striking.

The team building the new Microservice were super-productive. They even had to reimplement some infrastructure the first team had added to the monolith, but it took only a couple of days for them to catch up. After two weeks the Microservice team were much further ahead than the team that had been working in the Monolith for a full six weeks. There are costs to building Microservices (and we’ll talk a little about what we’ve done to manage those too), but for us the productivity benefits were striking. Not only did the team working with Microservices power ahead in development, much fewer bugs were found in development through production release than was typical for us as a team. The code they produced was also much simpler.

The benefits were so clear after just six weeks that we abandoned monolithic style development on our team and have not looked back since.

The decreasing cost of implementing Microservices

Having solved one problem (drastically improving the team’s productivity), we quickly began to hit new ones. We delivered features faster but new services began to proliferate. This introduced both operations and development related challenges. How can we manage so many services? How should they communicate? What permutations of failure scenarios exist and how do we handle them? Is there an easy way to manage versioning and backward compatibility?

At the time we didn’t have good answers for all of these questions, but ultimately these were easier problems to solve than increasing the team’s productivity by other means. What’s changed since then is not just our own understanding of how to deal with these issues, but also the broader Microservices eco-system. Our monolith was written in Java and represented years of investment that could not easily be abandoned. The tech infrastructure for Microservices back then was not what it is today, so we rolled our own (and open sourced at least some of it -> [AOL’s Microserver](https://github.com/aol/micro-server) & [cyclops-react](https://github.com/aol/cyclops-react) ). Our total productivity gains easily accommodated this additional development cost, but as time goes on we’re finding we have to spend less time building our own Microservices infrastructure as the approach has become more mainstream and much better supported.

For those that are interested, we first decided that a BASE model to state (Basically Available Soft State) suited our particular application. Which meant we designed services to cache as much locally as possible, and to make interservice calls for data asynchronous where appropriate also. This resulted in huge improvement in reliability and availability across the system, and mitigated the potential for cascading failures. We decided that all interface changes should be backwards compatible, and incompatible changes really should be a new service. The challenge of interface version proliferation effectively disappeared. To manage a large number of services we built infrastructure for service discovery, plugged into some neat modern monitoring systems and followed the crowd in adopting containerization and deploying to the cloud via CI/CD pipelines. That in itself was a lot of work, but the benefits are enormous.

Unexpected benefits

Almost as a side effect to adopting Microservices we’ve encountered a significant number of other benefits.

1. Simpler code — much less use of moderately complex patterns such as The Strategy Pattern, and generally less need for complex conditional logic

2. Better ability to choose the right tool for the task at hand — where our monolith was Java, our Microservices are JVM based (primarily still Java 8), but we make use of both Groovy and Scala based services when those languages and their libraries are a better fit

3. The ability to scale up development teams — we’re actively moving towards an ‘inner source’ model of development. It’s much easier for new external engineers to come into our codebase and create a nice new greenfield Microservice than understand a complex monolith. When needed we can facilitate much larger numbers of developers working productively on our project than we ever dreamed possible before.

4. Independent deployability of services — this isn’t something we set out to achieve deliberately. But the fact we can make changes to services quickly in an emergency and push a change out to production in a tiny fraction of the time it used to take has been a wonderful surprise of adopting this approach!

No silver bullet

Microservices aren’t a silver bullet, adopting them just because they are trendy is very high risk. But we believe that if you face real challenges around scaling development teams and improving productivity Microservices can help. Our experience is that pragmatically, conservatively and incrementally adopting Microservices, reap massive benefits for development teams.