Microservices are currently the hottest topic in software development. The concept is simple: Break down your application into smaller pieces that each perform a single business function and can be developed and deployed independently. These pieces, commonly called services, can then be assembled into an application using some flavor of service discovery like nginx or consul. The microservices approach is considered the architecture of choice for teams that want to build scalable platforms and efficiently and rapidly innovate on them.
As infatuated as I am with this architecture, our journey to microservices was a long and winding road. It has finally led us to a version of the architecture that gives us the scalability and agility we require as a business. I want to share my thoughts, experiences, and lessons learned in a series of blogs around this topic so you may benefit from our experiences. Also, I would love to get your feedback or comments on our approach.
When you start moving to microservices, the first question before you write a single line of code is: How do you organize your codebase? Do you create a repository for each service, or do you create a single ‘mono repo’ for all services? The two approaches are illustrated below:
We started out with the first approach with multiple repositories. It made more sense for many reasons:
Clear ownership: Since the codebase mimics the architecture, a small team can own and independently develop and deploy the full stack of a microservice.
Better scale Smaller codebases are easier to manage and lead to fewer instances of "merge hell". Teams do not need to co-ordinate with other teams leading to faster execution.
Narrow clones Most source control providers including git do not support cloning parts of a repository. For large codebases, clones, pulls, and pushes take too much time, which is inefficient.
So did this work for us? At Shippable, we run a fairly big, distributed organization spread out across multiple time zones. When we initially broke down our 5-tier platform into microservices, we ended up with 50+ services, each with its own code repository. And then the chaos started.
It was challenging to enforce standardization of code across loosely-coupled repositories. This meant that code became very complex to read and nobody understood the platform end to end. This caused code reviews for pull requests to be ineffective -- someone working on the same service lacked understanding of the the bigger picture, and anyone not working on the service had no context on it. We were also duplicating effort across teams since there were no shared components.
Ironically, tightly coupling teams with services and repos was causing most of our problems. We realized we wanted to build one team with knowledge across services as opposed to several teams with localized knowledge.
To solve these issues, we switched to a ‘mono repo’ a few months ago. This meant that even though our services are still developed and deployed independently, the code for all services lives in one repository. Almost immediately, we saw these improvements:
Better developer testing: Developers can easily run the entire platform on their machine and this helps them understand all services and how they work together. This has led our developers to find more bugs locally before even sending a pull request.
Reduced code complexity: Senior engineers can easily enforce standardization across all services since it is easy to keep track of pull requests and changes happening across the repository.
Effective code reviews: Most developers now understand the end to end platform leading to more bugs being identified and fixed at the code review stage.
Sharing of common components: Developers have a view of what is happening across all services and can effectively carve out common components. Over a few weeks, we actually found that the code for each microservice became smaller, as a lot of common functionality was identified and shared across services.
Easy refactoring: Any time we want to rename something, refactoring is as simple as running a grep command. Restructuring is also easier as everything is neatly in one place and easier to understand.
The results? Our productivity has increased at least 5x.
The best thing about our move to a mono repo is that we didn't give up any of the advantages of the microservices architecture.
We believe mono repos are the right choice for teams that want to ship code faster. There are concerns that this doesn't scale well, but these are largely unfounded. Companies like Twitter, Google, Facebook run massive monolithic repos with 1000s of developers.
The only thing you really give up with a mono repo is the ability to shut off developers from code they don’t contribute to. There should be no reason to do this in a healthy organization with the right hiring practices. Unless you're paranoid... or named Apple.
Go MONO today and start shipping coder faster than ever before!