Making the case for Rails monoliths over microservices

(Based on a thoughtbotter’s experience)
Once upon a time, a small company was running an MVP Ruby on Rails monolith. Things seemed to be going well. Then one day, they hired a new Chief Technology Officer (CTO). He was a trusted individual with a strong background at other successful companies.

He had some negative experiences with Rails monoliths in the past and wanted to get ahead of that here. He led the development team to split their monolith into microservices.

As time went on, work slowed. More people were hired. Testing was isolated to within each microservice, providing less confidence. Microservices felt too coupled, leading to difficult local development setups.

It became clear that the team was 40% LESS productive despite increasing the team size 400%! And there was a looming deadline to comply with new regulations!

The team had to cut their losses and migrate back to a Rails monolith architecture – time and effort that could have been avoided if the team was able to sit with the following questions and considerations:

Considerations before moving from a Rails monolith to Rails microservices

Is the product and client base big enough to justify this change?
This type of change should not be made before there is a need. Anecdotally, 99.99% of new applications should be written as a monolith to begin. When a project is new, like the MVP mentioned above, you want to prioritize speed and ease of development. A Rails monolith provides this agility as well as a straightforward infrastructure.

Can we afford the increased costs of maintenance (tooling, hiring up a team, etc)?
Splitting into microservices often means a more intense infrastructure set up. Especially if the current Rails app is on Heroku, which does much of the infrastructure heavy lifting for you, this is a serious consideration. A team could easily go from not needing anyone specialized in infrastructure to needing an entire team (even just a 2 person team) to handle the orchestration! In addition, if the team wants to migrate to AWS, that’s a significant expense.

Don’t plan to migrate to microservices too quickly/all at once.
Moving fast might seem natural in the world of Ruby on Rails and agile development. But encouraging a team to move too quickly to make an architectural split can cause a lot of pain down the road. Separations might be made based on what seems like the largest part of the app, instead of being made in the naturally occurring edges of concerns. This can also cause the next point:

Take care to properly de-couple concerns.
If a change to one microservice requires changes to one or more other microservices, it’s likely the microservices were not properly de-coupled. It’s important to treat each one as a separate entity, otherwise the product acts more like a “distributed monolith.”

Take into account how this can affect deadlines.
If there are deadlines on the horizon or promises to customers needing to be met, the team needs to be real with themselves on how a migration like this would negatively impact that. Not only the development time to separate the code, the infrastructure time to orchestrate the microservices, but also the learning curve for developers to code locally across these new microservices.

Will this solve the problems quickly enough?
You need to consider the problems you’re trying to solve with splitting. What is the timeframe you want to solve these problems? Would splitting to microservices solve those problems within that timeframe realistically? It’s possible scaling up your monolith would provide quicker results.

Would our product actually benefit from these separations?
What pain points are you trying to solve for? Does a microservice architecture address those sufficiently? What other solutions have been considered?

Is our team ready to work within a microservices architecture?
Successful Rails microservices setups require inter-team communication that is open and often. When changes to a microservice’s API are made, it affects the development cycle of other teams relying on that microservice. If you find your organization has less than optimal communication styles or are work mostly in silos, a Rails microservices architecture could be like trying to fit together mismatched puzzle pieces.

What is the reason for the proposed change?
It’s easy to blindly follow the advice of a trusted individual with authority and experience. However, folks putting forth the proposal should also provide sound logic and good evidence to defend the idea. The above considerations are a good starting point for making such a case.

A potential use case for a microservices architecture is having different user types with vastly different traffic levels. Scaling up a single monolith uniformly when 50% of the app is hardly used (think an admin panel vs customer app), this can be a good case for moving to a Rails microservices architecture. Even in this case, it is highly valuable to take your time and move incrementally. Allow the natural seams of the application to show themselves. There are also solutions that can take you closer towards a microservices architecture without giving up on the monolith entirely.

In reality, the cases for microservices are few and far between. It is important to take the time to deeply consider your issues and possible solutions. Unfortunately, a microservices architecture is one of those things that’s easy to do badly. There’s a difference between a well-designed, de-coupled microservices architecture, backed by a team that communicates changes openly and often and the “distributed monolith” we often see tagged as microservices.

I’d recommend avoiding the risk unless absolutely necessary.

That said, our team is made up of folks with an enormous wealth of experience who can help right a ship listing from water entering the cracks caused by microservices.

Ready to move from microservices to monolith? thoughtbot loves working with clients on their monolith journey. Let’s talk about making your project a success.