In the beginning of the year, there was a strongly-worded technical article about monorepos1, and why they’re worse, in absolute terms, in comparison to using multiple repositories for code (i.e., “polyrepo”). The article certainly generated a healthy discussion on its validity, and it’s worth a read if you’ve ever had to work inside of a huge code repository and have thought about these trade-offs.
The crux of the author’s main argument was that: since custom tooling is required anyway for any repository structure at scale, a polyrepo default encourages decoupling and sets up independent modules by default, with the added bonus that it’s a setup that most version control systems support by design. By contrast, monorepos encourages a single codebase that all engineers work against, so as teams scale and break off into sub-teams with separate workstreams, there’s additional coordination required to keep a monorepo building and deploying smoothly, though it’s easier to upgrade dependencies since the entire repo is deployed in a single step.
What I found interesting was the inevitable counter-article that argues for the merits of a monorepo. Written by the CTO of Chef Software, his point is that since a monorepo enables and promotes tight coupling between disparate parts of the system, having code broken from system upgrades is a a good thing, as it forces the teams to work through how to resolve the discrepancy as soon as it’s identified. That is, polyrepos make it easy to pin to old versions of dependencies, which is less work in the short run but can easily come back as tech debt once those old versions are no longer supported.
It’s the classic tech religious war: two diametrically-opposed solutions to a common problem, with reasonable technical arguments on each side, healthy heapings of anecdotes and hard-to-quantify opinions about how programmers and code behave, with a handful of appeals to authority and brand names for good measure. It’s the MySQL vs. Postgres, React vs. Angular, AWS vs. “bare metal” datacenters, fight of code repository configuration.
My takeaway is that, at least in this case, the difference in opinion can largely be explained by the corresponding difference in concerns. In fact, the Chef Software article identified this point at the end:
In both cases, I’m going to build tooling to sand off the rough edges. As a leader, I’ll pick the monorepo every time: because tools must reinforce the culture I want, and culture comes from the tiny decisions and behaviors of a team every day.
That’s the perspective and set of considerations that the head of an engineering organization has, for how they want to set up their team’s tooling, behaviors, and therefore development culture.
It reminded me of a similar decision that we made years ago at Square, when we decided to break out the front end webapp from the Rails monolith and chose a relatively unknown JS framework at the time—SproutCore 2.0, which would eventually become Ember.js. Admittedly, this was on a much smaller scale than Chef2, but Ember’s strong opinions on how webapps should be built and associated set of defaults became a feature of the framework. As we scaled up the teams who adopted Ember within Square or had to learn JS development, the inefficiencies and steep difficulty curve were worth the trade-off in uniformity and building towards a singular direction.
The wholly unsatisfying conclusion here is that technical choices and preferences are usually made for good reasons, and someone’s specific set of concerns—which by definition wouldn’t be universal and may very ell be unique to their situation—can provide them with their “right” technical decision. The next time you come across another article on why people should or shouldn’t use some piece of software/language/framework/design pattern, it’s worth the empathetic effort to try to understand why, from their perspective, they made that choice.