Why Monorepo?
Every project I've started in the last two years is a monorepo. Not because it's trendy, but because the alternative — maintaining multiple repos with cross-dependencies — becomes a coordination nightmare the moment you have more than two packages that need to stay in sync.
@webhouse/cms is 8 packages. CodePromptMaker is 5. fysiodk-aalborg-sport has a web app and shared packages. In each case, a monorepo means one PR, one CI run, one version bump, and one mental model.
The Stack: pnpm + Turborepo
I use pnpm workspaces for package management and Turborepo for build orchestration. The combination gives you:
- Strict dependency isolation — pnpm's non-flat node_modules catches missing dependency declarations that npm and yarn silently allow
- Cached builds — Turborepo hashes inputs and skips unchanged packages. On a cold CI run, the CMS monorepo builds in ~45 seconds. On a warm cache, it's under 10.
- Parallel execution — turbo run build builds all packages in the correct dependency order, maximizing parallelism
Project Structure Pattern
Every monorepo follows the same layout:
```
packages/
core/ → shared logic
web/ → Next.js app
cli/ → command-line tools
shared/ → shared types and utilities
scripts/ → automation scripts
turbo.json → pipeline config
pnpm-workspace.yaml
The packages/ directory contains publishable or deployable units. The scripts/ directory contains automation — deploy scripts, code audits, migration helpers. Everything is TypeScript, everything is built with tsup, and every package has a consistent tsconfig.json that extends a root config.
Lessons Learned
- Version all packages together. Keeping independent versions per package sounds flexible but creates dependency hell. All @webhouse/cms-* packages share the same version number.tsup` for library packages. It handles CJS/ESM dual output, declaration files, and tree-shaking with zero config.
- Use
- Automate the boring parts. A single script bumps all versions, and GitHub Actions publishes everything via OIDC. No manual npm publish, ever.
- The monorepo is the documentation. When all related code lives together, the import graph IS the architecture diagram.