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 is well-structured, using idiomatic Go project layouts, with a go.mod at the root, and has repo-wide policies for things like linting, and other helpful items.
The problem#
Whenever I have a new idea, I usually just make a temporary/scratch folder for it, since setting up CI, linting, dependency management, etc. 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, then I make a repo for it, and copy the code over. I do this, since I don't want to have to sign up for maintenance on something that might just be a dead end.
Whenever repos were created, they were inconsistent, and sometimes using a shared library meant signing up for potentially having to update multiple different repos that imported it when making a "breaking" change.
Dependency management was the worst of it. I use Renovate to keep things updated, and it does its job well. When you have several repos with overlapping dependencies, "doing its job" means a steady stream of PRs across all of them, especially for repos of low importance. Even with automatic merging on dependency updates, those emails still come in, notifications happen, and hopefully the tests pass. If not, and something needs to be fixed, then I have to decide if I'm going to spend the time to context switch and fix it, or if I'm just going to ignore it and let it rot until I need to use that repo again and notice the problem then. It's a minor annoyance that adds up. Especially as more and more repos get added.
One repo to rule them all#
After looking at how Xe structured their x repo, I moved everything into a single repository. Most of the annoyances I described above just went away.
With a monorepo, I have CI set up right out of the gate for any new CLIs, servers, docker images, linting, etc. I don't have to worry about that for new things, and by having a standard structure, I can just copy and paste from existing projects to get started. The barrier to entry for new experiments is much lower, so I'm more likely to try out new ideas, and have them use a consistent 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.
Having the infrastructure already set up means I can start new things without worrying about it. More of my experiments end up in source control, giving me better visibility into them if I want to go back and revisit them later.
The freedom of building for one#
The monorepo approach doesn't only work at the scale of one person; some of the largest companies in the world use monorepos for similar reasons. Those companies have the resources to build tooling to manage the complexity of a monorepo at scale, but the core motivation is the same: reduce overhead and friction in maintaining code.
This isn't unique to monorepos, but having a personal one with no expectation of supporting a wide audience gives me the freedom to make choices I wouldn't make if I were building a library for others to use.
This is the opposite of what you'd want for an open source library or a shared codebase at work. In those contexts, the overhead of separate repos with clear versioning and ownership boundaries exists for good reason. But for personal projects, that overhead is solving a problem that doesn't exist.
Give it a try#
If you are maintaining many small repos, possibly more than just Go, and you find yourself spending more time on repo maintenance than on the code itself, it might be worth trying. Xe's monorepo is a good reference for how to structure things.