
How does storing every project in one repository actually work?
Google and Meta run much of their multi-language engineering output through one shared monorepo, though not their entire codebase since some projects and services live outside it. Meanwhile plenty of teams still treat splitting code into separate repos as the default sign of good hygiene. So which one's the anti-pattern here: cramming every service into a single monorepo, or scattering shared logic across a dozen repos that quietly drift out of sync? Honestly, it comes down to tooling, and that's the thread running through everything below.
The mechanics matter more than the definition. A monorepo doesn't mean one giant application. It means one version-controlled tree that can hold services written in Python, Go, and TypeScript, plus infrastructure-as-code and internal libraries, all versioned together at the same commit.
- Bazel: a build system originally developed at Google, built to handle dependency graphs across large numbers of packages in one repo while avoiding rebuilds of unrelated code.
- Lerna: a tool commonly used for JavaScript monorepos that helps manage versioning and publishing across multiple packages in a single repo.
- Unified source of truth: every engineer pulls the same commit and sees the same dependency versions, so nobody has to guess which repo has the latest change.
- Directory-based organization: projects get separated by folder structure, not by repository, so cross-project changes show up in a single diff.
- And then there's Google and Meta themselves, both running large-scale, multi-language monorepos internally to coordinate changes across armies of engineers.
The core tradeoff: a monorepo centralizes visibility and consistency, but it demands purpose-built tooling to keep builds fast as the codebase grows. Skip that tooling investment and a monorepo scales poorly, plain and simple. Make it and tools like Bazel let a company run a repo at massive scale without every commit triggering a full rebuild. Tooling investment in exchange for centralized consistency, that's the trade, and it's worth examining closely before you commit to either side.
Why would a team choose a monorepo over splitting code across many repos?
The practical case for a monorepo comes down to what happens when shared code changes. Say several services depend on the same logging library, and that library lives in its own repo. Updating it usually means opening multiple separate pull requests, publishing a new package version, and hoping every team upgrades before a bug ships somewhere. In a monorepo, you refactor the library once, run it against every dependent project immediately, and merge the whole thing in a single change.
The decision also hinges on the tooling ecosystem a team is willing to adopt, because a monorepo without the right build system just becomes a different kind of maintenance headache.
- Cross-project refactors: a single commit updates a shared module and every consumer of it, no coordinating version bumps across a pile of separate repositories.
- No version mismatch guessing: teams stop checking which version of an internal library another team happens to be running, because everyone reads from the same commit.
- Consistent tooling: linting, testing, and CI configuration apply uniformly, so nothing needs separate logic to handle differences between repos.
- Scalability tools: Bazel and Lerna exist precisely because naive monorepos don't scale on their own. They're what makes a large-scale repo workable at all.
- Multi-language flexibility: a monorepo can hold Python, Go, and TypeScript side by side, so teams pick the right language per component without fighting integration friction between repos.
The teams that gain the most are running many interdependent services, where a shared library touches several consumers on a regular basis. That's when killing the pull-request juggling act pays off fastest. Teams with genuinely independent, loosely coupled projects gain less, and they'll often find the shared build system adds overhead without a matching payoff. That gap, who benefits and who doesn't, decides whether the tooling investment above is even worth making. Which raises the real question: how do you actually decide?
What does this mean for a team deciding today?
The decision isn't monorepo versus multi-repo in the abstract. It's whether your team already has, or is willing to build, the tooling that makes a single large repository fast to work in. Sonar puts it plainly: a monorepo simplifies dependency management and streamlines tooling because the tooling stops needing to understand differences between separate repositories. That simplification is real, but it only shows up once CI, testing, and build systems are built to run selectively instead of rebuilding everything on every single commit.
Mintlify draws a distinction worth sitting with too: a monorepo is more than all your code in one repository. The deliberate sharing of build systems and dependency configuration is what separates a functioning monorepo from a folder of unrelated projects that happen to share a Git history.
- Trunk.io's framing: monorepos can hold libraries, microservices, frontend, backend, and infrastructure-as-code together, organized by directory instead of repo.
- Testing at scale: teams write test scripts that run against the entire codebase or target specific components, keeping test coverage consistent across projects.
- Build pipeline coordination: pipelines across multiple projects sync more easily when everything sits in one repo instead of triggering separate CI systems per project.
- Refactor timing: Mintlify's logging-library example, refactor once and deploy across multiple services in the same cycle, is probably the clearest real-world case for adopting one.
- Before committing to anything, check whether your language and build ecosystem actually has a Bazel- or Lerna-equivalent. Without one, repo size turns into a bottleneck instead of a benefit.
If your team is still running two or three repos with rare cross-project changes, the migration cost of a monorepo probably outweighs the benefit right now. If you're already fighting version mismatches between several internal packages, the math changes, and the tooling investment starts paying for itself the first time a shared refactor lands everywhere at once instead of piecemeal, one repo at a time. The anti-pattern was never the monorepo or the multi-repo setup by itself. It's adopting either one without the tooling and discipline the structure demands. Choose based on how often your teams actually touch shared code, not on which structure looks tidier from the outside.