GPT-5.6 Sol · Claude Opus 5 · Gemini 3.1 Pro
Round 1Monorepo or polyrepo for 15 engineers in 2026? Web app, API, and shared libraries; CI times starting to hurt; hiring will double the team in a year. Argue for a concrete choice, not "it depends".
Initial answers
Moderator summary
Adopt a monorepo now, with affected CI and independent application deployments
A monorepo is the stronger choice for this team and growth path: shared libraries and API/web contract changes make atomic changes, unified dependency management, and simpler onboarding more valuable than repository-level isolation.
CI remedy: Repository choice alone will not fix slow CI. Use dependency-graph analysis, affected-only tasks, and remote caching; otherwise a monorepo merely centralizes the existing bottleneck. Treat claims such as “sub-two-minute” CI or roughly 90% of PRs completing under two minutes as unverified model-reported examples, not commitments (Claude Opus 5).
Concrete stack: Sol recommends pnpm workspaces plus Nx; Claude recommends pnpm workspaces plus Turborepo, arguing for the lower-complexity migration path. Nx is the better fit if architectural boundaries, richer project metadata, distributed execution, or multiple languages become important; Turborepo is the leaner starting point for a small TypeScript-oriented workspace. This is the panel’s main unresolved implementation choice.
Operating model: Keep apps/web and apps/api independently deployable, with reusable code under packages/; enforce dependency boundaries, CODEOWNERS, and centralized dependency updates. A monorepo does not require lockstep releases. Start with only genuinely shared packages rather than creating dozens of small modules (Claude Opus 5).
Migration: Consolidate before hiring doubles: move shared libraries first, then the applications without redesigning them, preserve deployment pipelines, enable affected CI and remote caching, and measure PR p50/p95 duration and cache-hit rate (GPT-5.6 Sol). Validate cache inputs and outputs before trusting remote results (Claude Opus 5).
Disagreements
Tooling: GPT-5.6 Sol specifically chooses Nx, while Claude Opus 5 specifically chooses Turborepo as the initial task runner; Gemini 3.1 Pro leaves the choice open.
CI expectations: Claude Opus 5 presents remote caching and affected execution as capable of nearly flat CI time and cites an unverified small-repository example, while the other replies make the benefit qualitative rather than supplying a comparable performance claim.
Blind spots
Baseline diagnosis: No model establishes whether the current CI bottleneck is full-repository execution, dependency installation, tests, build serialization, runner capacity, or deployment steps; that measurement could change the tooling and migration sequence.
Boundary conditions: No model confirms the API language, repository size, access-control requirements, or compliance constraints. A non-TypeScript API or a requirement for strict code isolation could materially reduce the benefits of a single unified graph or require an exception repo.
GPT-5.6 Sol
Choose a monorepo now—specifically a pnpm-workspaces + Nx monorepo, with independently deployable applications.
Your combination of shared libraries, rising CI cost, and growth from 15 to roughly 30 engineers makes coordination the larger future problem. Staying polyrepo would require package publishing, synchronized version bumps, and multiple PRs whenever the web app and API consume shared changes.
Why this is the right choice
- Atomic cross-project changes: Update the API, generated client/types, shared library, and web app in one PR.
- Faster CI: Nx’s project graph, affected-task selection, and remote caching let CI test/build only impacted projects rather than rebuilding everything.
- Growth readiness: At 30 engineers, consistent commands, dependency rules, and ownership become valuable. Current guidance generally favors monorepos for 10–50-engineer teams with several related JS/TS projects (monorepovspolyrepo.com).
- Your sharing is real, not hypothetical: Shared libraries are a strong monorepo signal, especially when cross-cutting changes are common (pandev-metrics.com).
- Avoid overengineering: Nx provides affected builds and caching without committing you to the much larger Bazel/platform-engineering investment. Monorepo tooling should match team scale (hivecore.dev).
Concrete structure
repo/
├── apps/
│ ├── web/
│ └── api/
├── packages/
│ ├── api-client/
│ ├── domain-types/
│ ├── ui/
│ ├── config-eslint/
│ └── config-typescript/
├── nx.json
├── pnpm-workspace.yaml
└── package.jsonUse these rules:
- Deploy
webandapiindependently. One repository does not mean one release. - Run affected tasks in CI: lint, type-check, test, build, and deploy only what changed.
- Enable remote caching immediately. CI pain is already a trigger; do not postpone this.
- Enforce dependency boundaries: applications may consume packages; shared packages must not import applications.
- Add
CODEOWNERSfor the web app, API, and important shared packages. - Keep deployable services under
apps/; keep only genuinely reusable code underpackages/. - Use one lockfile and Renovate/Dependabot for centralized dependency upgrades.
- Do not force synchronized versions across independently deployed apps.
Migration order
- Create the workspace and move the shared libraries first.
- Move the web app and API without redesigning them.
- Preserve their existing deployment pipelines.
- Add Nx affected-task CI and remote caching.
- Measure p50/p95 PR duration and cache-hit rate.
- Only then introduce stricter module-boundary rules or custom generators.
A simple monorepo can provide most of the benefit without immediately adding custom executors or elaborate build infrastructure (abrarqasim.com).
Bottom line: adopt the Nx monorepo before the hiring wave. At 15 engineers the migration is manageable; at 30, converting several active repositories while teams keep shipping will be substantially more disruptive.
Claude Opus 5
Go monorepo. Single repo, pnpm workspaces + Turborepo, remote cache on day one.
At 15 engineers growing to 30, with a web app, an API, and shared libraries that all change together, the polyrepo tax is versioning coordination — and that tax grows exactly as fast as your headcount. Your CI pain is not a monorepo problem; it's a no-build-orchestration problem, and it's cheaper to fix than to split repos.
Why not polyrepo
The decisive factor is your shared libraries. In a monorepo, packages are imported directly via workspace protocols like workspace:*, so shared library changes take effect immediately, whereas polyrepos depend on published packages and semantic versioning, which introduces delays. In practice, polyrepo means every shared-lib change becomes: PR → version bump → publish → bump consumers → two more PRs → discover the break in staging. That's a 3-repo dance that a monorepo does in one atomic commit with one CI run and one revert.
The second killer is dependency drift. A monorepo has a single lockfile, giving consistent dependencies across projects; polyrepos manage dependencies separately, which offers flexibility but risks inconsistencies. With 30 engineers, "the API is on React Query v4 semantics and the web app is on v6" becomes a recurring incident category, not a curiosity.
Third: polyrepo doesn't actually fix your CI times. It just hides them. You still run the same total minutes, you just lose the ability to skip work — and you add cross-repo integration failures that no single pipeline catches.
The only real polyrepo arguments — independent deploy cadence, blast-radius isolation, per-service ownership — are all achievable inside a monorepo with per-app pipelines and CODEOWNERS. The reverse (atomic cross-cutting changes in a polyrepo) is not achievable at all.
Fixing CI: the actual work
Your CI hurts because you're almost certainly running everything on every commit. A monorepo without tooling is just a large repository — the value comes from shared tooling for incremental builds, task caching, and dependency graph analysis; without these you get the downsides (complexity, slow CI) without the upsides.
Concrete stack:
- pnpm workspaces as the package manager — the standard for monorepos, and its strict symlinked
node_modulesprevents phantom dependencies that make monorepos rot. - Turborepo as the task runner. The appeal is the on-ramp: drop it into an existing pnpm workspace in an afternoon, no BUILD DSL, no project graph to maintain, no test-runner migration — keep
tsc,vitest,next buildand let it orchestrate them. Migration cost is low and incremental. - Affected-only execution. Turborepo analyzes the dependency graph, determines which projects are affected by changes, and runs only those tasks, skipping unaffected ones. Change the API, the web app's tests don't run. Change a shared lib, both do.
- Remote caching, immediately. This is the single highest-leverage item. Combined with remote caching, monorepo CI can reach sub-two-minute times regardless of repo size — a cache hit on unchanged packages is just a cache restore taking seconds. One report of an 8-package repo: roughly 90% of PRs finish in under 2 minutes with remote cache and affected detection, versus 6–8 minutes without.
The critical property: without these, monorepo CI time scales with repo size; with them, CI time is flat regardless of how many packages you add. That flatness is what makes this survive a doubled team.
Why Turborepo and not Nx or Bazel
Nx is the more powerful platform — effectively language-agnostic via plugins, richer task graph, code generators, plus Nx Cloud for remote cache and distributed task execution that shards CI across runners. But the trade-off is complexity: more concepts (executors, generators, project graph, target defaults), longer onboarding, and the payoff shows up at scale — at 200 packages the affected-graph computation and distributed cache pay for themselves quickly. You will not have 200 packages. The consensus verdict for teams your size: Turborepo is the right starting point — 80% of the value for 20% of the complexity, with Nx worth it when you have multiple teams, several app types, architectural boundaries to enforce, or CI costs high enough to justify the extra concepts.
Bazel is off the table. The precision costs BUILD files, explicit dependency declarations, and a learning curve that humbles seasoned engineers for a month — teams without a dedicated build platform group often abandon migration halfway. You have no platform team.
Revisit Nx when any of these become true: CI exceeds ~10 minutes even with cache hits (you need distributed execution), you add a third language you need graphed, or you pass ~4 apps with real architectural boundaries to police. The pnpm-workspace layout transfers, so this isn't a dead end — npx nx@latest init auto-detects projects in an existing monorepo.
Concrete layout
apps/web # web app
apps/api # API
packages/shared # types, validation schemas, API client
packages/ui # components
packages/config # eslint, tsconfig — keep tiny and stableResist granularity. Start with 3–5 packages, not 50 — over-splitting is the most common self-inflicted monorepo wound. And watch the config package specifically: changes to shared config packages trigger full rebuilds across every package that uses them, so keep shared configs stable and minimal.
The guardrails you need before the team doubles
Monorepos fail socially, not technically. Put these in from the start:
- CODEOWNERS per top-level directory — required review keeps "anyone can edit anything" from becoming "nobody owns anything."
- Dependency boundary lint rules — forbid
apps/webimporting fromapps/apiinternals; enforce that all cross-cutting code lives inpackages/. Without this, your monorepo becomes a distributed monolith by month six. - Independent deploy pipelines per app, triggered by affected detection. Monorepo ≠ lockstep releases, and conflating the two is why people wrongly think they need polyrepo.
- Trunk-based development with short-lived branches, plus merge queue when merge conflicts on
mainstart showing up (they will, around 25 engineers). - Cache correctness discipline. Declare task inputs and outputs precisely, including env vars. Get outputs and environment inputs right before turning on the remote cache — a wrong cache is worse than no cache, because it ships stale artifacts and destroys trust in CI.
Sequencing (roughly 3 weeks, one engineer part-time)
| Week | Work |
|---|---|
| 1 | Consolidate repos with git subtree/history-preserving merge; get pnpm workspaces resolving; one green full build |
| 2 | Add turbo.json with build/test/lint/typecheck tasks; verify local cache hits; fix input/output declarations |
| 3 | Enable remote cache; switch PR CI to affected-only filtering; split deploy pipelines per app; add CODEOWNERS and boundary lints |
Do the repo consolidation before the hiring wave lands, not during. Onboarding 15 people into a coherent single repo is dramatically cheaper than onboarding them into three repos and then asking them all to migrate.
One honest caveat: if your API is in a different language than TypeScript, the shared-library argument weakens considerably, and I'd still keep one repo but treat the two stacks as separate build islands (Turborepo orchestrating a Makefile or similar for the non-JS side) rather than trying to unify the graph. And if you genuinely need hard security isolation — a repo that a subset of engineers must not read — that's the one requirement a monorepo can't cleanly satisfy, and it justifies carving out exactly that one repo and nothing more.
Gemini 3.1 Pro
Go with a monorepo. For a cohesive system consisting of a web app, API, and shared libraries scaling from 15 to 30 engineers, a monorepo is the superior choice. Splitting into a polyrepo will simply trade your current CI pain for a much more destructive dependency management nightmare.
Here is why a monorepo is the exact right move for your trajectory:
The shared code penalty In a polyrepo, updating a shared library is a tedious, multi-step process. You have to open a PR for the library, wait for CI, publish a new package version, open a PR in the API repo to bump the version, and repeat the process for the web app. With 30 engineers moving fast, this friction causes developers to abandon shared code entirely, leading to massive duplication and architectural drift.
Fixing your CI pain Your CI times are hurting because your current pipeline is likely rebuilding and retesting the entire repository on every commit. You do not need multiple repositories to fix this; you need a modern build orchestrator. Tools like Turborepo or Nx use change-detection and remote caching to ensure CI only runs on the specific modules affected by a commit. A cached monorepo CI run is significantly faster than orchestrating builds across multiple polyrepo pipelines.
Atomic cross-boundary changes When an API contract changes, the back-end and front-end must stay perfectly in sync. A monorepo allows an engineer to update the API, modify the shared TypeScript interfaces, and fix the web app consumers in a single, atomic commit. A polyrepo forces developers to juggle multiple PRs and coordinate merge timing to avoid breaking deployments.
Streamlined onboarding You are about to double your engineering team in 12 months. A monorepo provides a single source of truth for new hires. They clone one repository, run a single setup command, and can instantly trace code from the front-end down to the database. A polyrepo forces new hires to hunt down isolated repositories, figure out local linking, and understand fragmented tooling.
Keep the monorepo, implement aggressive CI caching, and keep your growing team aligned in a single codebase.
Follow-up 1
Moderator summary
Choose a monorepo with pnpm and Turborepo now; reserve Nx for measured complexity
The recommendation has narrowed: Sol conceded that the available facts do not justify Nx, while Claude conceded that caching cannot make an inherently serial pipeline fast.
Concrete choice: Consolidate the web app, API, and genuinely shared libraries into one monorepo using pnpm workspaces and Turborepo. Keep applications independently deployable, enforce package boundaries and CODEOWNERS, and avoid lockstep releases.
CI first: Repository consolidation is worthwhile only alongside affected-task execution, dependency-store caching, remote task caching, and parallelizable tests. Measure installation, builds, tests, Docker, environment setup, and deployment separately; p95 PR duration matters more than p50. If non-cacheable work exceeds half the pipeline, changing orchestrators is unlikely to solve the problem (Claude Opus 5).
Nx escalation rule: The panel now treats Nx as a conditional upgrade rather than the default. Sol proposes switching when a same-commit benchmark shows at least 25% better p95 or three minutes saved, or when the graph reaches roughly 10–15 independently taskable projects, meaningful polyglot orchestration, or a real need for dynamic distributed execution. Claude instead flags a warm-cache affected critical path above about 12 minutes, a non-TypeScript API that must share one cached graph, or frequent service generation as reasons to adopt Nx immediately. These are decision rules and estimates, not established performance facts.
Non-TypeScript and isolation: A Go, Python, Java, or Rust API does not automatically defeat a monorepo, but pnpm will not manage its native build. Use contracts such as OpenAPI, GraphQL, or Protobuf and treat the backend as a build island (Gemini 3.1 Pro). Strict read-access or compliance separation remains a genuine reason to split repositories.
Next action: Before hiring doubles, run a one-week CI baseline and a same-commit Turborepo-versus-Nx shadow benchmark, especially on a shared-library change and the warm-cache critical path.
Disagreements
Nx escalation threshold: Sol requires measured Nx-specific benefit or substantially greater graph complexity, while Claude would switch earlier when a warm-cache critical path exceeds about 12 minutes or when a non-TypeScript API must participate in one cached graph.
Non-TypeScript handling: Sol says a non-TypeScript API does not by itself rule out either tool; Claude treats it as a day-one Nx trigger when unified graphing is desired, while Gemini emphasizes native build islands and contract generation instead of sharing implementation code.
Blind spots
Measurement execution: No participant provides the team’s actual CI decomposition, cache-hit rate, shared-code change rate, or warm-cache critical path; those missing measurements could still change the Turborepo-versus-Nx choice.
Operating cost: The panel does not quantify remote-cache storage, distributed-agent, runner, or maintenance costs, so the claimed CI benefit has not been compared with its likely engineering and infrastructure cost.
GPT-5.6 Sol
Claude’s criticism is fair: the facts given do not yet justify Nx over Turborepo. With only two applications and roughly 3–5 shared packages, I would revise the tool choice to** pnpm workspaces + Turborepo now**, unless a short benchmark demonstrates one of the Nx-specific advantages below.
Headcount growth alone is not a reason to choose Nx.
Measurements that would justify Nx now
Instrument the existing CI by task and PR type for at least a week. Record p50/p95 duration, CPU-minutes, cache-hit rate, and critical-path time for:
- checkout and dependency installation
- lint and type-check
- unit tests
- web and API builds
- integration/E2E tests
- Docker builds and image pushes
- environment provisioning
- deployment
Then test Nx and Turborepo against the same commits, task inputs, runner sizes, and cache state.
I would select Nx immediately if the measurements show at least one of these:
Unaffected-project work is the primary cost
- At least 40% of CI compute is spent on projects unaffected by the change.
- Most PRs affect less than 30% of projects.
- Nx reduces p95 PR feedback by at least 25% or three minutes compared with an equivalently configured Turborepo pipeline.
This is Nx’s clearest advantage: native affected-project analysis. As of the cited Turborepo v2.7 comparison, Turborepo requires
git diff/--filterscripting rather than offering equivalent native affected detection. The comparison reports roughly 1–2 minutes for Nx versus 3–4 minutes for Turborepo in one 12-package scenario, though those numbers should be validated on your repository (toolchew.com).Distributed execution is needed today
- The pipeline contains dozens or hundreds of independent tasks.
- Single-runner p95 remains above ten minutes after correct caching.
- Static CI shards have less than 65–70% average utilization because one shard routinely straggles.
- Nx Agents’ dynamic scheduling improves wall-clock duration by at least 25% at an acceptable cost.
Nx has a structural advantage here: agents can pull available tasks dynamically, while Turborepo generally requires more manual/static sharding. Published multipliers are not independently audited, so the decision should rest on your own shadow runs, not vendor-style benchmarks (digitalapplied.com).
The repository is already more complex than “two apps and a few packages” implies
- At least 10–15 independently taskable projects already exist or are committed for the next two quarters.
- The dependency graph includes generated clients, multiple deployable services, several framework types, or non-JavaScript builds.
- Architectural boundary violations are recurring defects rather than a hypothetical concern.
- You need graph-aware generators, ownership metadata, or enforceable project tags immediately.
Cold-cache work dominates and Nx materially wins
- Cache misses are common because of ephemeral branches, dependency churn, or frequent shared-package changes.
- Cold-cache builds represent at least 30% of CI runs.
- A controlled benchmark shows Nx materially shortening the critical path—not merely saving a few seconds.
Available comparisons suggest only a modest cold-cache Nx advantage at ordinary scale; the dramatic graph benchmarks use synthetic repositories and have Nx-associated provenance, so they are directional rather than decisive (toolchew.com).
Measurements that would not support Nx
Nx will not fix CI dominated by:
- dependency installation without an effective package-store cache
- Docker image transfer
- integration tests waiting on databases or external services
- E2E tests that cannot be partitioned
- deployment/environment provisioning
- a frequently changed shared package that legitimately affects both applications
- incorrect cache inputs causing every task to miss
If those account for most of the critical path, changing orchestrators is optimization theater. Fix those bottlenecks first.
Likewise, if the workspace contains only five packages and most shared-library changes correctly invalidate both apps, Nx’s more sophisticated affected graph has little extra work to eliminate. Both tools provide local and remote task caching; industry reports commonly claim 60–80% CI reductions after caching, but repository-specific cacheability matters far more than the headline number (digitalapplied.com).
Repository facts that could override performance
Before consolidating, confirm two non-CI facts:
- API language: A non-TypeScript API does not rule out either tool, but it weakens the workspace-package benefit and may make Nx’s broader project graph more valuable only if you genuinely want cross-language orchestration.
- Access isolation: If compliance requires some engineers not to read API or regulated code, keep that component in a separate repository.
CODEOWNERScontrols approval, not read access.
Concrete decision
Given the information currently available, use pnpm workspaces + Turborepo, not Nx. Keep the package layout portable and collect the baseline above.
Adopt Nx now only if a same-commit benchmark demonstrates ≥25% p95 improvement or ≥3 minutes saved, or if distributed execution, cross-language graphing, or enforceable project architecture is already a present requirement. Otherwise, Nx is appropriately a future escalation—not the default for a two-app, small-package workspace.
Claude Opus 5
The honest answer: there are four thresholds, and one of them may already apply to you
Sol's justification for Nx doesn't discriminate between the tools. Atomic changes, affected-task selection, remote caching, dependency boundaries, independent deploys — Turborepo does all of those. Turborepo even ships tag-based Boundaries now, configured per-package in turbo.json. So "Nx gives you affected + caching + boundaries" is not an argument for Nx; it's an argument for any orchestrator. Nx is only worth its extra concepts when you need one of the specific things Turborepo structurally cannot do. The test is whether you genuinely need one of Turborepo's three missing capabilities — code generation, polyglot support, or built-in distributed execution; if you don't, adopting Nx mostly buys you configuration surface you won't use.
Here are the thresholds, in the order they're likely to bite you:
| Threshold | Trigger value | Why Turborepo can't cover it |
|---|---|---|
| Warm-cache critical path | Longest chain of affected tasks on a packages/shared change exceeds ~12 min wall clock |
Caching removes redundant work, not long work. Only sharding across machines shortens it. In a 2026 benchmark Nx Agents finished in 9:20 versus Turborepo's 19:18 on distributed CI — the gap comes from Turborepo's lack of dynamic task distribution |
| Non-TypeScript API | API in Go/Java/Python/.NET and you want it in one cached graph | Nx offers polyglot builds via plugins for .NET, Java/Maven or Go; Turborepo is JS/TS-only |
| Package count | Past ~20 packages | Turborepo is ideal for teams under roughly 20 packages; reach for Nx once you cross that plus need generation, polyglot, or Nx Agents |
| Service-creation rate | New deployable app or library more than ~monthly | Generators amortize; below that rate a template repo is cheaper |
The one that matters most for you is the first, and it's not a package-count question. Suppose a change to packages/shared forces: install → typecheck shared → build shared → build web → build api → e2e. Cache hits do nothing for that chain, because every link genuinely changed. If that serial path is 25 minutes on one runner, remote caching gets you to 25 minutes. That's when you need Nx Agents — and it's a threshold you can hit at 8 packages, not 80.
Conceding the flat-CI claim
The coordinator is right, and I overstated it. "CI time is flat regardless of repo size" is only true for the cacheable, parallelizable portion. The real floor is: dependency install + the critical path of what actually changed + everything you can't cache (Docker builds, e2e against a live environment, deploy steps, migrations). Nobody, including me, can promise you sub-two-minute PRs without seeing your pipeline.
So measure four numbers this week, before choosing anything:
- Warm-cache critical path on a
packages/sharedchange — simulate it by timing a full build with everything else cached. This is your Nx-vs-Turborepo decider. - Non-cacheable share of total pipeline minutes (install, e2e, container builds). If that's >50%, your problem is pipeline design, and neither tool fixes it.
- Fraction of PRs touching shared code. If it's 10%, affected-only filtering is a huge win. If it's 60%, filtering barely helps and you need distribution.
- p95 PR duration — not p50. The p95 is what engineers complain about.
What I'd change in my recommendation
Two conditions flip me to Nx on day one:
- The API isn't TypeScript. I suggested treating it as a separate build island with Turborepo shelling out to a Makefile. That works, but you lose cache correctness and affected detection across the language boundary — exactly where API-contract breakage lives. With a JVM or .NET API, Nx's first-party plugins make one graph real rather than aspirational, and I'd pay the complexity now. Nx 22 added first-party Maven and .NET plugins.
- Measurement #1 comes back above ~12 minutes today. Then you need distributed execution within months, and doing Turborepo→Nx during the hiring wave is worse than doing Nx now.
npx nx@latest initauto-detects projects in an existing monorepo, though full migration can take days for large repos.
On access control and compliance: I flagged hard read-isolation as the one genuine monorepo defeater, and that's unchanged — but note it's a repository constraint, not a tooling one. Neither Nx nor Turborepo helps; you carve out one repo for that code and nothing more.
Otherwise, Turborepo first. The 2026 default for most teams is still start with Turborepo, graduate to Nx when coordination becomes the bottleneck — and at 15-to-30 engineers with 4–6 packages, coordination is not yet your bottleneck. Your bottleneck is that nothing is orchestrating your builds at all.
Gemini 3.1 Pro
The panel is right to challenge the assumption that a JS-centric monorepo is a silver bullet. If your API is not written in JavaScript/TypeScript, or if your CI latency stems from un-cacheable integration steps, standard monorepo tooling will not save you. Depending on these factors, you may need to implement isolated build islands or abandon the monorepo entirely.
When CI latency resists caching Change detection and remote caching only accelerate CI if your tasks are highly modular and deterministic. If your critical path is dominated by monolithic processes, a monorepo will amplify the pain as the team grows.
- End-to-end testing: If your CI time is consumed by heavy browser-based tests (e.g., Playwright, Cypress) running against a fully booted database and API, caching provides little relief. Because E2E tests depend on the entire system, a change to either the API or the web app will invalidate the cache and force a full E2E run.
- Container build bottlenecks: If the slow step is a multi-stage Docker build that invalidates its cache layer whenever any source code changes, moving to a monorepo will not shorten the build time.
- Merge queue gridlock: If a full CI run (a total cache miss) takes more than 15–20 minutes, doubling your team to 30 engineers will cause severe congestion. Multiple engineers merging concurrently will force the CI queue to constantly re-evaluate, meaning you will have to invest in complex distributed task execution (like Nx Cloud or Bazel) rather than a simple Turborepo setup.
When API technology requires build islands
If your API is written in Go, Python, Java, or Rust, pnpm workspaces are useless for the backend. I concede that this drastically changes the implementation.
- Contracts instead of code: You cannot natively import shared JS/TS utilities into a non-JS backend. Your "shared libraries" must shift from shared application logic to shared API contracts, such as OpenAPI specifications, GraphQL schemas, or Protobuf definitions.
- Orchestrating build islands: You can still use a monorepo, but you must treat the web app and API as isolated environments. The JS/TS frontend will use native Node tooling, while the backend uses its own native toolchain (e.g.,
go buildorcargo). You will need a language-agnostic orchestrator to bridge them—for instance, configuring Nx to automatically run a TypeScript client generator whenever the backend's OpenAPI YAML file is modified.
The hard boundary: Access control Git natively lacks directory-level read permissions. If your hiring surge includes external contractors, outsourced teams, or compliance-bound roles that are strictly prohibited from viewing the API source code or database schemas, a monorepo is fundamentally disqualified. You must carve out the web app and client SDKs into a distinct polyrepo.