The Debate Nobody Wins

Ask ten engineers whether to use a monorepo and you will get eleven opinions. The polyrepo crowd will tell you that separate repos mean clear ownership, independent versioning, and small focused histories. The monorepo crowd will point at Google, Meta, and Microsoft and say: if it scales there, it scales anywhere.

Both camps are right about something. And both camps tend to argue past the thing that actually matters: what is the right fit for your situation?

I have lived on both sides. For years I had separate GitHub repos for every project. A Rust database engine here, a language interpreter there, a few private experiments scattered across repos I had quietly forgotten about. It worked, in the same way that a pile of sticky notes works. Until it does not.

When I started building out my homelab, I had a choice to make. This time, I made it deliberately.

Why Separate Repos Feel Natural at First

There is an intuitive appeal to the polyrepo model. Each project is its own thing. Its own history, its own CI/CD pipeline, its own README that tells a coherent story. You clone exactly what you need and do not have to care about anything else.

For a while, that is genuinely fine. When projects are truly independent, different languages, different lifecycles, different audiences, keeping them separate has real benefits:

  • Isolated CI/CD. A broken pipeline in one repo does not touch the others.
  • Fine-grained access control. You can give a contributor access to one project without exposing anything else.
  • Clean public presence. Each GitHub repo is a self-contained portfolio piece.

These matter. But a polyrepo approach has a cost that is easy to miss until you are already paying it.

The Hidden Tax of Many Repos

Cross-project changes become a coordination problem

This is the one that gets you. You refactor a shared utility, and suddenly you are opening three repos, making three commits, filing three PRs, and hoping nothing breaks in the window between them. What should be one logical change becomes a multi-step synchronization ritual.

In a monorepo, that is one commit. Atomic. Everything ships together or nothing does.

Dependency drift happens silently

Each project pins its own versions. Over time, project A is on library version 2.1, project B is on 2.4, and they have diverged in ways that matter. Nobody meant for this to happen. It just did, one cargo update at a time.

CI/CD sprawl is real work

Every new repo is another pipeline to configure, another set of secrets to manage, another YAML file to maintain. If you care about consistency, same linting rules, same build patterns, same deployment approach, you are either copying config files around or building some abstraction layer to share them. Either way, it is work that a monorepo gives you for free.

Onboarding yourself is genuinely painful

This one sounds silly until you come back to a project after six months. Where was that repo again? What does this do? Why does it exist? In a monorepo, ls apps/ tells you everything you have. No archaeology required.

I had projects on GitHub that I had completely forgotten existed. Years ago I put together a small repo to teach a friend some Java patterns. Harmless at the time, forgotten immediately after. Fast forward a few years, and I am sitting in a technical interview. We get to the practical part, and the interviewer pulls up a section of an application and asks me to modify it. The application was mine. I had no memory of it. That was the moment I understood that you cannot just leave everything public and assume it represents you well. A monorepo behind a private git server does not solve that problem entirely, but at least entropy stays contained where you can see it.

“But Google Uses a Monorepo” and That Is Actually the Point

The counterargument you will hear is that monorepos do not scale. That is worth pushing back on directly.

Google runs what is arguably the largest monorepo in the world, billions of lines of code, tens of thousands of engineers, a single trunk. Meta, Microsoft, and Airbnb have all made similar bets at scale. The tooling they have built (Bazel, Buck, Nx, Turborepo) exists precisely to handle the performance challenges that come with size.

The argument is not that monorepos scale infinitely with zero effort. It is that the problems of monorepos, build performance and tooling complexity, are engineering problems with engineering solutions. The problems of polyrepos, coordination overhead, drift, context loss, are human problems that tend to get worse as your codebase grows, not better.

For a solo developer or a small team, the tooling overhead of a monorepo is near zero. The coordination overhead of a polyrepo is not.

What My Monorepo Actually Looks Like

Today, I have exactly one repository on my self-hosted OneDev instance. Everything lives there:

  • NixOS configurations for all my machines and LXC containers
  • The blog, the Hugo source, content, and publishing scripts
  • Apps, ArchSim, go-brain, a distributed database engine, an LSM tree implementation, and whatever I am building next
  • Dotfiles and homelab scripts

One repo. One place to look. One place to push.

The tradeoff is that it is private by nature. My NixOS secrets and infrastructure details are not things I want public. But several of my projects should be public, both for the open-source ethos and because an active GitHub profile matters when you are job hunting.

That is the one real problem a monorepo creates that a polyrepo solves by default: how do you share parts of it without sharing all of it?

Solving the Privacy Problem

The answer I landed on is git subtree. It lets you treat a subdirectory as a first-class git repository and push its history independently to a public remote. My OneDev CI/CD automatically triggers these pushes whenever the relevant paths change.

The result: atomic commits and unified history in private, with my public GitHub repos staying up to date automatically without me thinking about it.

Next week I will write about the exact mechanics of that setup, the Justfile, the OneDev buildspec, and a few gotchas around token handling and history performance. If the monorepo-plus-selective-publishing pattern sounds useful for your own setup, that post will have everything you need to replicate it.

Should You Use a Monorepo?

If you are a solo developer or a small team starting fresh, default to a monorepo. The tooling is simple, the coordination overhead is zero, and you will thank yourself the first time you make a cross-cutting change in a single commit.

If you have genuinely independent projects with different teams, different deployment cadences, and serious access control requirements, a polyrepo might be the right call. Not because monorepos cannot scale, but because the independence is real and worth preserving.

The mistake is picking one approach for ideological reasons without thinking about what you are actually building and who is building it. The repo structure is a tool. Use the one that solves your actual problems.

For me, one repo solved them. Everything else was overhead I was carrying for no good reason.