Monorepo vs polyrepo is a question of which boundary you need. A monorepo puts many packages in one repository, so one change can cross all of them. A polyrepo gives each project its own repository, so each one can have its own access, CI and release.
We run both on purpose. Every product we build is a Turborepo monorepo. The company itself is a polyrepo: every client, every service and every confidential area is its own repository, tied together with git submodules. An AI agent works in both every day.
What is the difference between a monorepo and a polyrepo?
The short version: a monorepo draws a code boundary around packages, a polyrepo draws an access boundary around repositories.
Monorepo
Polyrepo
One change across many parts
One commit, one pull request
One pull request per repository
Shared code
A workspace package, imported directly
A published package or a copy
Who can read what
Everyone with access sees everything
Access per repository
CI
One pipeline, a build tool skips what did not change
One pipeline per repository
Dependency versions
One lockfile, one version per package
Each repository drifts on its own
Context for an AI agent
The whole system in one checkout
Only what the task needs, the rest by navigation
Most articles on this topic argue for one column. We think both are right, for different units. The question is what you are drawing the line around.
Why is every product a monorepo?
A product is code that ships together. Our template and wizrd, our internal Claude Code plugin and CLI (not public), look the same: one app and the packages it is made of.
apps/web is the app. packages/ holds api, db, auth, infra, ui, env and a few more. The app imports them as workspace:*, so there is nothing to publish. The catalog pins shared versions once, so the API and the app never run two different versions of the same library.
^build means: build what I depend on first. Builds, type checks and lint are cached. Deploys and database pushes never are. The cache is the reason the Turborepo docs give for using it at all:
Turborepo uses caching to speed up builds, ensuring you never do the same work twice.
The real win is not speed, though. It is the single pull request. When the database schema changes, the API, the types and the screen change in the same commit, and check-types fails if one of them was missed. For an AI agent that is gold: the type checker tells it what it broke before a person has to.
A client's online marketplace runs the same shape, with its crawler as one more app in the monorepo.
Why is the company a polyrepo?
Because access is the boundary that matters most to us, and GitHub grants access per repository. There is no read permission for a single folder. So everything that needs a different audience gets its own repository:
Client repositories for each client, readable by the team that works for that client. A freight forwarder we built and run a system for has its own client repository with its service repositories inside it as submodules.
Rooms for confidential areas such as finance and brand, each readable only by its own GitHub team.
Service repositories for our own tools, such as the plugin and harness our agents use.
The company repository at the root ties them together with git submodules. Nobody gets the whole tree by default. A session without access to finance cannot read finance, whatever its prompt says.
This is the part a monorepo cannot give you. A monorepo with a "please do not open the finance folder" rule is a rule. A separate repository with its own team is a lock.
How does an AI agent work across both?
The agent follows the same few rules on a person's laptop and on its own machine:
01
Navigate, never clone
Every repository is a submodule in the tree. The agent goes to clients/<x>/ or services/<y>/ instead of cloning a second copy somewhere else.
02
One worktree per task
Each task gets its own git worktree. A client service is nested inside a worktree of its client repository, so the session loads every CLAUDE.md up the chain.
03
Work in one repository
Commits and pull requests go to the repository the change belongs to: client work to the client, service work to the service.
04
Pointers move separately
A task branch never moves a submodule pointer. A separate sync step does that, after the work is merged.
The last rule is enforced by a script, not by trust. Our gate fails a task branch that changes a gitlink, the git object behind a submodule pointer:
Without it, an agent that touched two repositories in one task would quietly move the parent's pointer to an unmerged commit. Everyone else would then check out code that only exists on a branch.
The nested worktree is the other detail that took us a while. A plain worktree of a service would miss the client's CLAUDE.md one folder up, so the agent would work without the client's rules. Nesting the service inside a worktree of its client keeps the whole chain of context.
What does a polyrepo cost?
It is not free, and we would not pretend otherwise:
Changes across repositories take several pull requests. A rule that changes for every client means a pull request per client repository.
Submodules have rough edges. A fresh checkout needs git submodule update --init, and a stale pointer shows old code without any warning.
CI is configured many times. Each repository needs its own workflows.
We solved the last one by not copying CI. Every repository calls thin shared actions from one tool repository, pinned to a moving major tag. A new validation check lands in one place and reaches every repository on its next run. They all run on one self-hosted GitHub runner for the whole org.
Monorepo or polyrepo: how to decide for your team?
Go through what is true for your code today:
Turborepo vs Nx comes up next. Honest answer: we use Turborepo because our monorepos are TypeScript apps on Bun workspaces and a short turbo.json is all they need. We have not run Nx in production, so we will not rank them.
We set up the repository layout, the shared CI and the agent rules right in a team's codebase as part of AI-native SDLC for development teams. Is your team short of hands rather than structure? See a senior developer for your team. AI writes all of our code today, and a senior is accountable for the result. Since 2023 we have delivered 15+ production projects.
We get back to you within two business days, and together we pick what to fix first.
What is the difference between a monorepo and a polyrepo?
A monorepo keeps many projects or packages in one git repository with one history. A polyrepo gives each project its own repository. In a monorepo one commit can change several packages at once. In a polyrepo each repository has its own access, its own CI and its own release.
When is a monorepo the better choice?
When the parts ship together and share code: a web app, its API, its database schema and its UI components. One pull request changes all of them, and the types catch what no longer fits before anything is deployed.
When is a polyrepo the better choice?
When different people may see different parts. GitHub grants access per repository, not per folder, so client code, finance and internal tools that need different audiences belong in different repositories.
Turborepo or Nx?
We use Turborepo with Bun workspaces, because our monorepos are TypeScript apps and Turborepo only needs a small turbo.json on top of the workspaces. We have not run Nx in production, so we cannot compare them from experience.
Can an AI agent work in a polyrepo with git submodules?
Yes, if it has rules. Ours navigates into the submodule instead of cloning a copy, works in its own worktree per task, and a gate blocks any task branch that moves a submodule pointer.
Ask AI about this article
The AI also draws on our other articles and experience from real projects.
Claude Code pricing from Anthropic's public list (October 2026): Pro, Max, Team seats, Enterprise and API tokens. Which one fits developers, which one an always-on agent and a CI reviewer run on, and the guardrails that stop a background agent from burning the budget.
We did not replace Vercel, we split the work. Next.js sites stay on Vercel, new apps run on Cloudflare Workers and D1, declared in TypeScript with Alchemy. Pricing with sources, our config, and the deploy block an AI agent hit on Vercel.
Better Auth vs Clerk comes down to where your users and sessions live. We moved from NextAuth to Better Auth in every new app and never picked Clerk. Our real config, the plugins we run, Clerk pricing and when Clerk is the better call.