Monorepo! Monorepo! Monorepo!

Well, sir, there's nothing on earth like a genuine, bona fide, electrified, six-car monorepo!

What's it called? Monorepo! That's right, monorepo!

I've been maintaining many small Go repositories for personal projects, libraries, and a bunch of experiments. The overhead that comes with all of that adds up. After recently switching to a monorepo, I'm a convert: so much toil immediately disappeared.

The inspiration

Xe Iaso maintains a public monorepo, x, that contains many Go projects, experiments, and libraries. Sometimes full projects, such as Anubis, have even been spun out of it. The repo uses idiomatic Go project layouts with a go.mod at the root, plus repo-wide policies for things like linting, CI, and other similar items.

The problem

When I have a new idea, I usually make a scratch folder for it, because setting up CI, linting, and dependency management for a one-off experiment feels like more work than it's worth. If the experiment turns out to be interesting and I want to keep it around, I make a repo for it and copy the code over. I'd rather not sign up to maintain something that might be a dead end.

The repos that did get created were inconsistent, and a "breaking" change in a shared library could mean updating every repo that imported it.

Dependency management was the worst of it. I use Renovate to keep things updated, and it does its job well. With several repos sharing dependencies, "doing its job" means a steady stream of PRs across all of them, even for repos I barely care about. Automatic merging handles most of the updates, but the emails and notifications still come in, and sometimes the tests fail. Then I have to decide whether to context switch and fix it or let it rot until I next need that repo and notice the breakage. It's a minor annoyance, but that still adds up as the repo count grows.

One repo to rule them all

After looking at how Xe structured their x repo, I moved everything into a single repository. Most of those annoyances just went away.

The shared CI and build setup is available right out of the gate for any new project. A standard structure means I can copy an existing project to get started. The barrier to trying a new idea is much lower, and anything I keep ends up with the same coding style and structure.

I can also have shared utilities across multiple programs, and just have to update them once without needing to bump deps for a bunch of repos. I can also have shared CI and linting rules, so when I want to change a rule, I change it once and it applies everywhere.

Because the infrastructure is already there, more of my experiments end up in source control instead of dying in a scratch folder. That makes them easier to find if I want to revisit one later.

The freedom of building for one

A personal monorepo with no expectation of being used by others gives me the freedom to make choices I wouldn't make for a project that has to take into account the needs of others and how different people may approach using the tool.

Those decisions might be wrong for an open source library or a shared codebase at work, where separate repos provide clear versioning and ownership boundaries. For my personal projects, that is too much overhead and it creates a barrier from actually building things.

Give it a try

If you maintain a pile of small repos, and spend more time on repo maintenance than on the code itself, you might want to look into monorepos. Xe's monorepo is a good reference for how to structure one.