Isn't there some middle ground between microservices and monorepos? For example, a few large clearly defined projects that talk to each other via versioned APIs?
Assuming you're just referring to repos: not really IMO.
As soon as you split 1 repo into 2 repos you need to start building tooling to support your 2 repos. If your infrastructure is sufficiently robust with 2 repos then you might as well have 3 or 4 or 10. If it's built to _only_ support 2 repos (or 3 or 4) then it's brittle out of the gate.
The value of a monorepo is that you completely eliminate certain classes of problems and take on other classes of problems. Classic trade off. Folks that prefer monorepos take the position that multirepo problems are much harder than monorepo problems most of the time.
> As soon as you split 1 repo into 2 repos you need to start building tooling to support your 2 repos.
No, not really.
If you're talking about projects for modules and components, all you need is a versioning strategy and release consumable packages of your projects.
If you're talking about services, all you need to do is support versioned APIs and preserve your contracts.
No tooling required. For projects you can even make do with git submodules. For services, all you need to do is update clients of your downstream dependencies.
If you aren't using a monorepo, you need some versioning process, as well as procedural systems in place to ensure that everyone's dependencies, stay reasonably up to date. Otherwise, you end up deferring pain in really unwanted ways, and require sudden, unwanted upgrades through api incompatibility due to external pressure.
This also has the downside of allowing api-owning teams to make changes willy-nilly and break backwards compatibility because they can just do it behind SemVer, and then clients of the api need to own the process of migrating to the new version.
A monorepo fixes both of these: you cannot get out of sync, so it is the api-owning team's responsibility to upgrade clients, since they can't break the API otherwise. Similarly, you get a versioning process for free, and clients can never be using out of date or out of support versions of a dependency.
Services work approximately the same either way, since you can't assume synchronous upgrades across service/rpc boundaries anyway.
The classic 0, 1, or N. You can have 0 repos (no-code or outsourced or just some unversioned blob of code). You can have 1 repo (a mono-repo). Or you can have N number of repos (individual micro services). Limiting yourself to a number that's higher than 1 but still finite is just asking for pain. (Of course implementation limits can apply but there shouldn't be logical limits)
Hard disagree. I've found that the best setup is two monorepos - one with feature/app code, and one with operations configuration.
The fundamental mismatch is the feature/app code will have longer testing times, stuff like Puppeteer and creating-infra-per-MR that just fundamentally takes a long time to run. But in ops, you need configuration to roll out quickly, maybe run an autoformatter and a linter or two beforehand and that's it. When you want to rollback, you don't need to wait for all your tests to run.
Yes you need to deal with versioning. You can just use automatic timestamps. You can write easy automation to push new timestamps to the ops/config repository when there are new releases in the app repository. The ops repository has minimal configuration to pull the latest version of the app repository and apply it, where the app repository includes infra and deployment scripts themselves.
The problem you describe with waiting for tests for unrelated code changes (or potentially related but you want to skip it for the reasons you describe) is a problem that seems to deserve a solution even within a single monorepo.
What are the solutions? You would need multiple merge queues.
There isn't really a solution that preserves the vision and advantages of a monorepo. If infra, config, and code are stored in the same repository, then rolling back one necessarily means that the others are supposed to have been rolled back as well. When the version of the code is not a simple identifier pointing to the code but the code itself, there's simply no way to do that safely without recompiling and retesting the code. Same as when a change to code results in a revert to the infrastructure - run plan, run apply, wait for the slow infrastructure APIs, and that must happen before the change to code.
If you try to come up with fancy rules like forbidding changes to infra, config, and code in the same commit, then you're just cramming a polyrepo design into a monorepo, with much more fragility, because commits get rebased and squashed and etc.
Do you not just write code that’s backwards compatible?e.g an api deployment should not break the front end. The api changes should work on the old front end and the updated front end that uses the api change.
Done that at two different places. This was public sector in both cases so typically many products in the organization. So one product was one monorepo.