Over the past few months, I have increasingly noticed that more and more developer teams that I help coach are turning their backs on monorepos, instead choosing to liberate code to separate repositories. For me, this couldn’t have come sooner.
Don’t get me wrong, I have seen examples of monorepos work well in the past - a few years ago I worked on a scope-limited system written in Go which was deployed in three different environments with significant amounts of code reuse and a single team owning the entire codebase. In circumstances like this, monorepos made sense for this project.
Unfortunately, over the past few years, companies have blindly adopted monorepos as an architectural pattern for all organisational code without considering the individual circumstances of what they’re building. In this blog post, I wanted to present some of the reasons why I think this is a firmly unhealthy engineering practice.
One of the most common arguments used by those who advocate monorepos is an appeal to the authority of big tech companies who have adopted them. If Google, Facebook, etc use monorepos, surely shouldn’t we too?
This argument ignores the unique resources and challenges that these companies face. Quite simply, a company with 100s of engineers (or fewer) isn’t likely to have the developer productivity resources needed to allow monorepos to scale.
For example, Google internally has a tool called Rosie that helps manage the review and merge process of cross-team Pull Requests in a monorepo. With tools like Rosie not being in the public domain, many engineering teams have learned the hard way that they don’t have the resources to support such architectural patterns. According to the tweets of the former VP of Architecture at Monzo (a UK online banking start-up), they experienced this very problem.
Instances like this provide a stark reminder that the fact a big technology company does something is not a substitute for your own professional engineering judgment.
Google’s DORA State of DevOps Report research consists of seven years of research and data from more than 32,000 professionals across the industry, in companies of different sizes. Their research reports in 2021 and 2019 were clear; loosely-coupled software architecture is one of the strongest predictors of successful continuous delivery.
Monorepos add significant complexity to the deployment pipeline of software. As the monorepo scales, deploying all software for every change quickly becomes impractical. Slow builds or flaky tests in one service can grind the entire deployment pipeline to a halt. Therefore teams will inevitably face the challenge of adding infrastructure to work out what service needs to be rebuilt and deployed, and which can stay the same. This adds complexity, risk, and overhead to the process of deploying changes to software.
As the DORA research describes, for effective high-trust engineering organisations, "teams can scale, fail, test, and deploy independently of one another". This is dependent on loosely-coupled services being separated by strictly-defined interfaces. Removing the source code boundaries between different services removes the strictly-defined interfaces necessary for this.
In my own experience, the outcomes of architectural practices like monorepos are worse than if the organisation had no centrally proscribed architectural mandate and simply allowed the systems architecture to emerge organically. This raises the question, why is this done in the first place?
Conway’s Law is an adage that says: “Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization's communication structure.”
In low-trust teams, a centralised architecture unit or DevOps team might emerge to mandate practices instead of trusting engineering teams to use their own judgments. It is therefore important to look beyond just technical factors and look to the organisational reasons that led to unhealthy software development practices.
The character of The Architect in The Matrix films reminds me much of how unaccountable software architects end up conducting themselves. Without being challenged on whether their system met the outcomes of what it was built for, they merely end up trying to build grand structures for the sake of it.
By contrast, the engineering teams actually building the functionality at hand are constantly held accountable for the work they produce. Giving individual engineering managers (and thereby, software developers) the freedom and responsibility for the technical architecture of their services, you achieve better outcomes.