Why a monorepo helps Claude Code specifically
An agent does not have peripheral vision. It sees the files you put in its context and reasons over those - and when everything lives in one undifferentiated pile, it will happily edit a shared utility to fix a feature, or refactor a component three other apps depend on without realizing it. The result is the same kind of collision a large human team has, except the agent moves faster and apologizes less.
A monorepo fixes this with structure as a constraint. When code is split into apps/* and packages/* with explicit dependencies between them, the boundaries are real, not aspirational. The agent can see that the web app imports a shared ui package, which tells it the package is shared and a change there is load-bearing. Clear ownership is the cheapest guardrail you can give an agent, and it is most of what a good Claude Code project setup is buying you.
Turborepo and pnpm workspaces
The standard substrate is pnpm workspaces for dependency management and Turborepo for the task graph. pnpm installs each package’s dependencies once and links them, which keeps a multi-package repo from ballooning, and its workspace protocol (workspace:*) makes internal packages first-class instead of published artifacts you have to version by hand.
Turborepo sits on top and understands how your packages depend on each other. When you run turbo run build, it builds packages in the right order, runs independent tasks in parallel, and caches results so unchanged packages are not rebuilt. For an agent this matters more than it looks: a fast, incremental build and test loop means Claude gets honest feedback in seconds, which is the whole point of a toolchain that is green from commit one. The same task graph that speeds up local work is what your CI runs, so “it passed locally” and “it passed in CI” finally mean the same thing.
The layered CLAUDE.md pattern
A monorepo is where CLAUDE.md best practices earn their keep, because you get to layer them. Claude reads the root CLAUDE.md first - repo-wide context that is true everywhere: the toolchain, the package layout, the conventions every workspace shares, the commands that always work. Keep this lean. It is loaded for every session in the repo, so every line you waste here is context spent before any real work begins.
Then each package or app gets its own scoped CLAUDE.md at its root, holding only what is true for that surface - this app’s routing model, that package’s public API, the one gotcha a newcomer always hits. When the agent works inside apps/web, Claude reads the root file for the shared picture and the local file for the specifics, and the two compose. The discipline is the same as the broader best practices: write down what is true at the altitude where it is true, and nowhere else, so nothing has to be repeated and nothing goes stale in two places at once.
A sane structure to start from
A baseline that holds up is deliberately boring:
apps/*- the deployable surfaces (a web app, an API, a worker). Each one is a consumer; it depends on packages but nothing depends on it.packages/*- the shared building blocks (auilibrary, aconfigpackage with shared ESLint and TypeScript settings, a typeddbclient). These are imported, never the importers.- A root that owns the toolchain - one pnpm workspace file, one Turborepo pipeline, shared lint and type config that every workspace extends rather than redefines.
The one rule worth enforcing is that dependencies point in one direction: apps depend on packages, packages depend on other packages, and nothing points back the other way. That single constraint is what keeps the dependency graph legible to both the agent and the next human.
When you do not need a monorepo
Honestly: often. A monorepo is a tool for managing multiple surfaces that share code. If you are building one genuinely single-surface app - a marketing site, a single service, a CLI - a monorepo adds Turborepo config, workspace indirection, and a layered docs system to solve a coordination problem you do not have yet. The boundaries you are paying for are boundaries between things that, for now, are just one thing.
In that case a single, well-configured package is the right call, and you can promote it to a monorepo the day a second surface actually appears. The thing that travels with you either way is not the workspace layout - it is the discipline: a lean root CLAUDE.md, real gates, and conventions Claude can read. Reach for the monorepo when the structure earns its keep, not as a reflex.
The one-command version
Standing this up by hand - pnpm workspaces, the Turborepo pipeline, shared config every package extends, and a layered CLAUDE.md that actually composes - is a real afternoon, and easy to get subtly wrong. claudesetup scaffolds the whole monorepo substrate in one command, wired so agents and humans do not collide, and ships the reasoning for each choice alongside the files - so you can see why the structure is shaped the way it is, and own all of it.