[{"data":1,"prerenderedAt":1517},["ShallowReactive",2],{"blog-polyrepo":3,"blog-polyrepo-favor-of":523},{"id":4,"title":5,"body":6,"cover_image":499,"date":500,"decidedDate":500,"decision":501,"decisionInFavorOf":502,"decisionReason":503,"description":504,"evaluatedScore":490,"extension":505,"link":499,"logoPath":499,"meta":506,"navigation":507,"path":508,"placements":509,"reviewTrigger":499,"satisfaction":499,"seo":513,"stage":514,"stem":515,"tags":516,"target":499,"type":521,"__hash__":522},"radar\u002Fradar\u002Fpolyrepo.md","Polyrepo",{"type":7,"value":8,"toc":482},"minimark",[9,14,23,35,38,43,46,68,74,76,80,83,88,91,113,117,125,155,159,176,180,187,191,194,196,200,203,212,217,239,259,262,264,268,392,394,398,401,421,429,431,435,438],[10,11,13],"h1",{"id":12},"polyrepo-architecture-why-we-rejected-the-multi-repository-pattern","Polyrepo Architecture: Why We Rejected the Multi-Repository Pattern",[15,16,17,18,22],"p",{},"In modern software engineering, repository architecture is frequently conflated with system deployment architecture. The classic convention—one Git repository per service or microservice—is commonly known as the ",[19,20,21],"strong",{},"Polyrepo (or Multi-Repo)"," pattern.",[15,24,25,26,34],{},"While the polyrepo model is often the instinctive default for teams embracing microservices, real-world operational friction reveals severe systemic costs as systems grow. On our Tech Radar, ",[19,27,28,29],{},"Polyrepo has been firmly rejected in favor of an orchestrated ",[30,31,33],"a",{"href":32},"\u002Fblog\u002Fmonorepo","Monorepo",".",[36,37],"hr",{},[39,40,42],"h2",{"id":41},"_1-the-core-promise-vs-the-real-world-reality","1. The Core Promise vs. The Real-World Reality",[15,44,45],{},"The traditional argument for polyrepos sounds compelling on paper:",[47,48,49,56,62],"ul",{},[50,51,52,55],"li",{},[19,53,54],{},"Isolated Code Ownership:"," Teams have full autonomy over their individual service repositories.",[50,57,58,61],{},[19,59,60],{},"Lean Git Clones:"," Developers only pull the specific codebase they actively maintain.",[50,63,64,67],{},[19,65,66],{},"Independent CI\u002FCD:"," Changing service A does not trigger tests or deployment builds for service B.",[15,69,70,71,34],{},"However, in fast-moving engineering environments where microservices frequently communicate over APIs, events, or shared domain contracts, this clean separation quickly deteriorates into what industry architects call a ",[19,72,73],{},"distributed monolith with multi-repo coordination tax",[36,75],{},[39,77,79],{"id":78},"_2-the-5-multi-repo-taxes-that-break-velocity","2. The 5 Multi-Repo Taxes That Break Velocity",[15,81,82],{},"When your software spans multiple repositories, simple changes become tedious cross-organizational projects. We identified five fatal friction points during our evaluation:",[84,85,87],"h3",{"id":86},"_1-the-synchronized-pr-tax-atomic-commits-are-impossible","1. The Synchronized PR Tax (Atomic Commits are Impossible)",[15,89,90],{},"When a backend service updates an API endpoint and the frontend web client must consume it:",[47,92,93,99,102],{},[50,94,95,96,98],{},"In a ",[19,97,5],{},", you must create PR #1 in the backend repo, merge it, release or tag a new version, wait for package publication, update dependencies in the frontend repo with PR #2, and carefully synchronize the staging rollout.",[50,100,101],{},"If a bug is caught during staging, rolling back requires orchestrating coordinated reverts across multiple Git histories.",[50,103,95,104,108,109,112],{},[19,105,106],{},[30,107,33],{"href":32},", this is a ",[19,110,111],{},"single atomic PR",": the backend schema change and frontend consumer update live in the exact same commit, reviewed and verified together.",[84,114,116],{"id":115},"_2-dependency-drift-and-version-chaos","2. Dependency Drift and Version Chaos",[15,118,119,120,124],{},"In a polyrepo setup, shared logic (such as logging formats, auth middlewares, or TypeScript interfaces) must be packaged and published to a private registry (like ",[30,121,123],{"href":122},"\u002Fblog\u002Fnpm","npm"," or PyPI).\nInevitably:",[47,126,127,130,133],{},[50,128,129],{},"Service A runs on v2.1.0 of the internal auth library.",[50,131,132],{},"Service B runs on v2.4.3.",[50,134,135,136,142,143,149,150,154],{},"Service C is stranded on v1.8.0 with known security vulnerabilities.\nKeeping 20+ independent repositories updated with shared library releases requires automated bots, endless dependency-bump PRs, and constant review fatigue. In contrast, tools like ",[19,137,138],{},[30,139,141],{"href":140},"\u002Fblog\u002Fpnpm","pnpm"," workspaces and ",[19,144,145],{},[30,146,148],{"href":147},"\u002Fblog\u002Fuv","uv"," workspaces link shared libraries directly from source via ",[151,152,153],"code",{},"workspace:*"," links.",[84,156,158],{"id":157},"_3-contract-breakage-at-runtime-rather-than-compile-time","3. Contract Breakage at Runtime Rather Than Compile Time",[15,160,161,162,168,169,175],{},"When contracts change in a polyrepo, integration testing between services usually happens late in the lifecycle (staging or end-to-end test clusters). With unified task orchestrators like ",[19,163,164],{},[30,165,167],{"href":166},"\u002Fblog\u002Fmoon","Moon"," or ",[19,170,171],{},[30,172,174],{"href":173},"\u002Fblog\u002Fturborepo","Turborepo",", cross-service schema generators and typecheckers run immediately on pre-commit and PR builds, catching breaking changes before code ever merges.",[84,177,179],{"id":178},"_4-cicd-duplication-and-drift","4. CI\u002FCD Duplication and Drift",[15,181,182,183,186],{},"Every polyrepo maintains its own ",[151,184,185],{},".github\u002Fworkflows\u002F",", Dockerfiles, lint configs, and deployment scripts. When modernizing security scanners or updating Node\u002FPython versions, engineers must duplicate the change across 30+ repositories.",[84,188,190],{"id":189},"_5-hindered-code-discovery-and-cross-team-refactoring","5. Hindered Code Discovery and Cross-Team Refactoring",[15,192,193],{},"Polyrepos create organizational silos. Searching for how an internal API is consumed requires searching across dozen of separate repositories. Automated refactoring tools (AST codemods, language server renames) cannot operate across Git boundaries.",[36,195],{},[39,197,199],{"id":198},"_3-the-deployment-fallacy-independent-deployment-does-not-require-independent-repos","3. The Deployment Fallacy: Independent Deployment Does Not Require Independent Repos",[15,201,202],{},"The most frequent misconception cited by polyrepo proponents is:",[204,205,206],"blockquote",{},[15,207,208],{},[209,210,211],"em",{},"\"We need separate repositories so we can deploy our microservices independently.\"",[15,213,214],{},[19,215,216],{},"Independent deployability is a CI\u002FCD pipeline capability, not a repository organization boundary.",[15,218,219,220,222,223,225,226,230,231,234,235,238],{},"Modern monorepo build systems (",[30,221,167],{"href":166},", ",[30,224,174],{"href":173},", and ",[30,227,229],{"href":228},"\u002Fblog\u002Fnx","Nx",") utilize ",[19,232,233],{},"Affected Change Analysis"," based on Git diffs and dependency graphs. When an engineer commits changes to ",[151,236,237],{},"apps\u002Fbilling-service",":",[240,241,242,245,251,256],"ol",{},[50,243,244],{},"The CI pipeline analyzes the dependency tree.",[50,246,247,248,250],{},"Only ",[151,249,237],{}," and its direct dependents are tested and built.",[50,252,247,253,255],{},[151,254,237],{}," is deployed to production.",[50,257,258],{},"All unrelated services are skipped entirely, saving minutes of CI runtime through local and remote computation caching.",[15,260,261],{},"You achieve 100% independent deployments while retaining all the developer experience advantages of a single codebase.",[36,263],{},[39,265,267],{"id":266},"_4-architectural-comparison-polyrepo-vs-monorepo","4. Architectural Comparison: Polyrepo vs. Monorepo",[269,270,271,288],"table",{},[272,273,274],"thead",{},[275,276,277,282,285],"tr",{},[278,279,281],"th",{"align":280},"left","Evaluation Dimension",[278,283,284],{"align":280},"Polyrepo (Multi-Repo)",[278,286,287],{"align":280},"Monorepo (Orchestrated)",[289,290,291,305,324,337,350,366,379],"tbody",{},[275,292,293,299,302],{},[294,295,296],"td",{"align":280},[19,297,298],{},"Atomic Cross-Service Changes",[294,300,301],{"align":280},"❌ Impossible (Requires sequenced PRs)",[294,303,304],{"align":280},"✅ Native (Single commit, single review)",[275,306,307,312,315],{},[294,308,309],{"align":280},[19,310,311],{},"Shared Library Management",[294,313,314],{"align":280},"⚠️ High friction (Private registry releases)",[294,316,317,318,320,321,323],{"align":280},"✅ Zero overhead (",[30,319,141],{"href":140}," \u002F ",[30,322,148],{"href":147}," workspaces)",[275,325,326,331,334],{},[294,327,328],{"align":280},[19,329,330],{},"Refactoring & Discovery",[294,332,333],{"align":280},"❌ Siloed across multiple Git URLs",[294,335,336],{"align":280},"✅ Instant global search and codemods",[275,338,339,344,347],{},[294,340,341],{"align":280},[19,342,343],{},"CI\u002FCD Maintenance",[294,345,346],{"align":280},"⚠️ Duplicated across dozens of repos",[294,348,349],{"align":280},"✅ Centralized, unified pipelines",[275,351,352,357,360],{},[294,353,354],{"align":280},[19,355,356],{},"Toolchain Consistency",[294,358,359],{"align":280},"❌ Version drift between teams",[294,361,362,363,365],{"align":280},"✅ Guaranteed hermetic runtimes (",[30,364,167],{"href":166},")",[275,367,368,373,376],{},[294,369,370],{"align":280},[19,371,372],{},"Git Clone Size",[294,374,375],{"align":280},"✅ Small per repo",[294,377,378],{"align":280},"⚠️ Larger initial clone (mitigated by shallow clone\u002Fsparse checkout)",[275,380,381,386,389],{},[294,382,383],{"align":280},[19,384,385],{},"Access Control Granularity",[294,387,388],{"align":280},"✅ Native per-repo GitHub permissions",[294,390,391],{"align":280},"⚠️ Requires CODEOWNERS or branch protections",[36,393],{},[39,395,397],{"id":396},"_5-when-does-polyrepo-still-make-sense","5. When Does Polyrepo Still Make Sense?",[15,399,400],{},"While we rejected polyrepo for our core product and infrastructure systems, there are specific scenarios where multi-repo separation remains legitimate:",[240,402,403,409,415],{},[50,404,405,408],{},[19,406,407],{},"Hard Organizational or Security Firewalls:"," Regulated financial systems or sensitive data modules that cannot be accessible to general engineering staff even in read-only mode.",[50,410,411,414],{},[19,412,413],{},"Third-Party Open-Source Packages:"," Standalone libraries published to the broader community that follow independent semantic versioning lifecycles and independent contributor communities.",[50,416,417,420],{},[19,418,419],{},"Completely Unrelated Products:"," Systems that share zero business logic, zero domain types, zero infrastructure, and zero team collaboration.",[15,422,423,424,428],{},"For any set of applications, microservices, and client apps that cooperate to deliver a cohesive product, a ",[19,425,426],{},[30,427,33],{"href":32}," is unequivocally the superior architectural choice.",[36,430],{},[39,432,434],{"id":433},"_6-related-architecture-reports-tooling","6. Related Architecture Reports & Tooling",[15,436,437],{},"To explore the tooling we selected to make the monorepo architecture thrive:",[47,439,440,448,457,468,475],{},[50,441,442,447],{},[19,443,444,238],{},[30,445,446],{"href":32},"Monorepo Architecture (Adopted)"," Complete strategy breakdown from native workspaces to task orchestration.",[50,449,450,454,455,34],{},[19,451,452,238],{},[30,453,141],{"href":140}," The disk-efficient package manager powering our TypeScript workspaces over ",[30,456,123],{"href":122},[50,458,459,463,464,34],{},[19,460,461,238],{},[30,462,148],{"href":147}," The high-speed Astral tool powering our Python workspaces over ",[30,465,467],{"href":466},"\u002Fblog\u002Fpip","pip",[50,469,470,474],{},[19,471,472,238],{},[30,473,167],{"href":166}," The polyglot orchestrator unifying Python, TypeScript, and Rust task graphs.",[50,476,477,481],{},[19,478,479,238],{},[30,480,174],{"href":173}," High-velocity remote caching for JavaScript\u002FTypeScript repositories.",{"title":483,"searchDepth":484,"depth":484,"links":485},"",2,[486,487,495,496,497,498],{"id":41,"depth":484,"text":42},{"id":78,"depth":484,"text":79,"children":488},[489,491,492,493,494],{"id":86,"depth":490,"text":87},3,{"id":115,"depth":490,"text":116},{"id":157,"depth":490,"text":158},{"id":178,"depth":490,"text":179},{"id":189,"depth":490,"text":190},{"id":198,"depth":484,"text":199},{"id":266,"depth":484,"text":267},{"id":396,"depth":484,"text":397},{"id":433,"depth":484,"text":434},null,"2026-08-08","reject","monorepo","Splitting related services and UI across repositories turns a single feature into multiple coordinated PRs, creates dependency drift, and forces ordering releases across repositories. A monorepo avoids that overhead entirely, since independent deployment is a CI\u002FCD pipeline capability, not a repository boundary concern.","Splitting microservices across independent repositories: why multi-repo architecture was rejected in favor of an orchestrated monorepo.","md",{},true,"\u002Fradar\u002Fpolyrepo",[510],{"category":511,"subCategory":512},"techniques","architecture",{"title":5,"description":504},"trial","radar\u002Fpolyrepo",[517,518,512,519,520,502],"polyrepo","multi-repo","cicd","devops","tech_report","5yZ4JaPcWE3eysSstmWUTH0JSTP9hGIlngJQ7jJ3UH0",{"id":524,"title":33,"body":525,"cover_image":499,"date":500,"decidedDate":500,"decision":1503,"decisionInFavorOf":499,"decisionReason":1504,"description":1505,"evaluatedScore":1045,"extension":505,"link":499,"logoPath":499,"meta":1506,"navigation":507,"path":1507,"placements":1508,"reviewTrigger":499,"satisfaction":499,"seo":1510,"stage":514,"stem":1511,"tags":1512,"target":499,"type":521,"__hash__":1516},"radar\u002Fradar\u002Fmonorepo.md",{"type":7,"value":526,"toc":1481},[527,531,534,544,551,558,560,564,570,573,577,594,631,635,647,656,703,705,709,712,716,734,738,752,755,765,789,791,795,798,802,826,829,845,849,870,873,896,898,902,908,912,915,931,939,947,950,1147,1149,1153,1330,1332,1336,1393,1395,1399,1402,1477],[10,528,530],{"id":529},"monorepo-architecture-from-native-workspaces-to-polyglot-build-systems","Monorepo Architecture: From Native Workspaces to Polyglot Build Systems",[15,532,533],{},"One repository for related services, libraries, and applications, rather than scattering them across a dozen disconnected repositories.",[15,535,536,537,540,541],{},"The most common objection to monorepos—",[209,538,539],{},"\"How do we maintain independent deployability?\"","—conflates code organization with artifact delivery. ",[19,542,543],{},"Independent deployment is a CI\u002FCD pipeline concern, not a repository layout problem.",[15,545,546,547,550],{},"With path-based triggers, containerized builds, and targeted deployment pipelines, services in a monorepo deploy just as independently as polyrepo services. The difference is developer velocity: when a cross-cutting feature spans an API contract, two backend microservices, and a frontend client, it lives in a ",[19,548,549],{},"single atomic Pull Request",". It is reviewed together, type-checked together, and merged without the fragile ceremony of semantic version bumping, registry publishing, and multi-repo deployment sequencing.",[15,552,553,554,557],{},"See also: ",[30,555,5],{"href":556},"\u002Fblog\u002Fpolyrepo",", the alternative this was weighed against.",[36,559],{},[39,561,563],{"id":562},"_1-the-foundation-native-package-manager-workspaces","1. The Foundation: Native Package Manager Workspaces",[15,565,566,567,34],{},"Before reaching for heavy monorepo build orchestrators, every modern monorepo starts with ",[19,568,569],{},"native package manager workspaces",[15,571,572],{},"Workspaces solve the fundamental code-sharing problem: allowing internal packages to depend on one another locally via symlinks without publishing intermediate packages to npm or PyPI.",[84,574,576],{"id":575},"pnpm-workspaces-typescript-nodejs","pnpm Workspaces (TypeScript \u002F Node.js)",[15,578,579,580,585,586,589,590,238],{},"In the JavaScript and TypeScript world, ",[19,581,582],{},[30,583,584],{"href":140},"pnpm workspaces"," (configured via ",[151,587,588],{},"pnpm-workspace.yaml",") is our adopted foundation, replacing legacy ",[19,591,592],{},[30,593,123],{"href":122},[47,595,596,611,621],{},[50,597,598,601,602,606,607,610],{},[19,599,600],{},"Strict symlink isolation:"," Unlike ",[19,603,604],{},[30,605,123],{"href":122}," or Yarn classic which flatten ",[151,608,609],{},"node_modules"," and invite phantom dependency bugs, pnpm's content-addressable store creates strict, isolated symlinks.",[50,612,613,616,617,620],{},[19,614,615],{},"Workspace protocol:"," Using ",[151,618,619],{},"\"@org\u002Fshared-ui\": \"workspace:*\""," ensures internal dependencies always link directly to local source code during development.",[50,622,623,626,627,630],{},[19,624,625],{},"Recursive commands:"," Commands like ",[151,628,629],{},"pnpm --filter .\u002Fapps\u002Fweb build"," allow scoped execution across projects.",[84,632,634],{"id":633},"uv-workspaces-python","uv Workspaces (Python)",[15,636,637,638,642,643,646],{},"Historically, Python was notoriously painful in monorepos. Legacy tools like ",[19,639,640],{},[30,641,467],{"href":466}," and requirements.txt (as well as Poetry and Pipenv) lacked native workspace semantics, forcing brittle ",[151,644,645],{},"pip install -e ."," hacks or complex path manipulation.",[15,648,649,650,655],{},"Astral's ",[19,651,652],{},[30,653,654],{"href":147},"uv workspaces"," changed the game completely:",[47,657,658,676,688],{},[50,659,660,663,664,667,668,671,672,675],{},[19,661,662],{},"Single unified lockfile:"," A root ",[151,665,666],{},"pyproject.toml"," declares ",[151,669,670],{},"[tool.uv.workspace]",", resolving a single, deterministic ",[151,673,674],{},"uv.lock"," across all microservices, shared libraries, and CLI tools.",[50,677,678,681,682,684,685,34],{},[19,679,680],{},"Shared virtual environment:"," Rather than creating 15 separate virtual environments that each re-download PyTorch, FastAPI, or Pandas, ",[151,683,148],{}," shares packages from its global cache and links internal packages instantly using ",[151,686,687],{},"{ workspace = true }",[50,689,690,693,694,698,699,34],{},[19,691,692],{},"Blazing performance:"," Written in Rust, ",[19,695,696],{},[30,697,148],{"href":147}," resolves dependencies 10–100× faster than legacy ",[19,700,701],{},[30,702,467],{"href":466},[36,704],{},[39,706,708],{"id":707},"_2-workspaces-vs-full-monorepo-tooling-when-to-transition","2. Workspaces vs. Full Monorepo Tooling: When to Transition?",[15,710,711],{},"A common architectural mistake is adopting heavy build orchestration tools too early—or waiting until CI pipelines grind to a halt before realizing native workspaces are not enough.",[84,713,715],{"id":714},"what-native-workspaces-do-well","What Native Workspaces Do Well:",[47,717,718,721,724],{},[50,719,720],{},"Local package linking and resolution.",[50,722,723],{},"Dependency deduplication and unified lockfiles.",[50,725,726,727,222,730,733],{},"Basic filtered command running (",[151,728,729],{},"pnpm --filter ...",[151,731,732],{},"uv run --package ...",").",[84,735,737],{"id":736},"where-native-workspaces-hit-the-wall","Where Native Workspaces Hit the Wall:",[15,739,740,741,744,745,168,748,751],{},"Native package managers have ",[19,742,743],{},"no concept of build graphs or computation caching",". When you run ",[151,746,747],{},"pnpm -r test",[151,749,750],{},"uv run pytest",", the package manager naively runs tests in every package—even if only a single markdown file changed.",[753,754],"monorepo-transition-card",{},[15,756,757,758,222,760,222,762,764],{},"Dedicated monorepo engines (",[30,759,167],{"href":166},[30,761,174],{"href":173},[30,763,229],{"href":228},") introduce three transformative capabilities:",[240,766,767,777,783],{},[50,768,769,772,773,776],{},[19,770,771],{},"Affected \u002F Impact Analysis:"," Using Git diffs and dependency graphs to execute tasks ",[209,774,775],{},"only"," on packages affected by a commit.",[50,778,779,782],{},[19,780,781],{},"Computation Caching (Local & Remote):"," Hashing file inputs, environment variables, and dependencies. If the hash hasn't changed, the tool restores build and test outputs from cache in milliseconds.",[50,784,785,788],{},[19,786,787],{},"Task Pipeline Parallelism:"," Executing tasks topologically according to a defined graph (e.g. build shared UI before building web app, while running lint in parallel).",[36,790],{},[39,792,794],{"id":793},"_3-homogeneous-stacks-pure-typescript-vs-pure-python","3. Homogeneous Stacks: Pure TypeScript vs. Pure Python",[15,796,797],{},"When your entire monorepo lives within a single language ecosystem, your tooling choice simplifies considerably.",[84,799,801],{"id":800},"scenario-a-pure-typescript-javascript","Scenario A: Pure TypeScript \u002F JavaScript",[47,803,804,812],{},[50,805,806,809,810],{},[19,807,808],{},"Base:"," ",[151,811,584],{},[50,813,814,809,817,821,822],{},[19,815,816],{},"Orchestrator Choices:",[19,818,819],{},[30,820,174],{"href":173}," vs. ",[19,823,824],{},[30,825,229],{"href":228},[15,827,828],{},"For pure TypeScript\u002FJavaScript monorepos (e.g., Next.js\u002FNuxt web apps, Node\u002FFastify microservices, shared UI components, shared TS schemas):",[47,830,831,840],{},[50,832,833,835,836,839],{},[19,834,174],{}," is the recommended default. It is lightweight, configuration-minimal (",[151,837,838],{},"turbo.json","), written in Rust, and focuses squarely on fast caching and task orchestration. It integrates seamlessly with Vercel and self-hosted remote caches without altering how your packages build.",[50,841,842,844],{},[19,843,229],{}," is a heavier, enterprise-grade alternative. It provides deep AST analysis, project visualizers, and code generators. However, it introduces significant abstraction that can feel intrusive for teams wanting standard build scripts.",[84,846,848],{"id":847},"scenario-b-pure-python","Scenario B: Pure Python",[47,850,851,857],{},[50,852,853,809,855],{},[19,854,808],{},[151,856,654],{},[50,858,859,809,862],{},[19,860,861],{},"Tooling:",[19,863,864,866,867],{},[151,865,148],{}," + ",[151,868,869],{},"ruff",[15,871,872],{},"For a pure Python monorepo (e.g., several FastAPI microservices, shared data models, and Celery workers):",[47,874,875,886,893],{},[50,876,877,879,880,882,883,34],{},[151,878,654],{}," paired with ",[151,881,869],{}," for linting and formatting is so ridiculously fast that ",[19,884,885],{},"many Python teams do not even need a secondary monorepo build orchestrator",[50,887,888,889,892],{},"Tests can be scoped via ",[151,890,891],{},"uv run pytest --package service-a",", and dependency installation takes under 2 seconds.",[50,894,895],{},"You only need a monorepo task runner in pure Python if you have heavy, multi-stage artifact builds (e.g. compiling Cython\u002FC-extensions, generating PyInstaller binaries, or baking complex multi-stage Docker images in CI) where distributed remote caching pays dividends.",[36,897],{},[39,899,901],{"id":900},"_4-the-polyglot-challenge-multi-language-monorepos","4. The Polyglot Challenge: Multi-Language Monorepos",[15,903,904,905,34],{},"The real trial begins when a monorepo combines different language runtimes: ",[19,906,907],{},"Python backend services + TypeScript web applications + Rust\u002FGo microservices or WASM modules",[84,909,911],{"id":910},"why-js-first-tools-struggle-with-polyglot","Why JS-First Tools Struggle with Polyglot",[15,913,914],{},"Both Turborepo and Nx originated in the JavaScript\u002FNode.js ecosystem:",[47,916,917,926],{},[50,918,919,921,922,925],{},[19,920,174],{}," expects ",[151,923,924],{},"package.json"," files everywhere. It does not understand Python virtual environments, Cargo workspaces, or Go modules natively. Attempting to shoehorn Python tasks into Turborepo requires wrapping everything in custom npm scripts.",[50,927,928,930],{},[19,929,229],{}," supports Python through community plugins, but it can feel bolted on. Crucially, Nx assumes the developer’s local machine and CI runner already have the correct Python, Poetry, or Go versions installed and activated.",[84,932,934,935,938],{"id":933},"moon-moonrepo-the-true-polyglot-architecture","Moon (",[151,936,937],{},"moonrepo","): The True Polyglot Architecture",[15,940,941,942,946],{},"This is where ",[19,943,944],{},[30,945,167],{"href":166}," stands apart as the front-runner in our evaluation.",[15,948,949],{},"Moon was designed from day one as a language-agnostic, polyglot monorepo build system written in Rust:",[240,951,952,980,1125,1134],{},[50,953,954,961,962,964,965,968,969,222,972,975,976,979],{},[19,955,956,957,960],{},"Hermetic Toolchain Management (",[151,958,959],{},"proto","):","\nInstead of assuming Node 20, Python 3.12, and Rust 1.80 are pre-installed on every developer's machine and CI container, Moon manages language runtimes natively via its toolchain system (",[151,963,959],{},"). It automatically downloads, pins, and executes the exact runtime specified in ",[151,966,967],{},".moon\u002Ftoolchain.yml",". No ",[151,970,971],{},"nvm",[151,973,974],{},"pyenv",", or ",[151,977,978],{},"rustup"," configuration drift.",[50,981,982,985,986,988,989,320,991,993,994,997,998,1001,1002],{},[19,983,984],{},"First-Class Tier Support:","\nMoon treats JavaScript\u002FTypeScript (",[151,987,141],{},"), Python (",[151,990,148],{},[151,992,467],{},"), Rust (",[151,995,996],{},"cargo","), and Go as first-class citizens. Tasks are declared declaratively in ",[151,999,1000],{},"moon.yml"," files per project:\n",[1003,1004,1008],"pre",{"className":1005,"code":1006,"language":1007,"meta":483,"style":483},"language-yaml shiki shiki-themes github-light github-dark","# apps\u002Fanalytics-api\u002Fmoon.yml (Python service)\ntype: application\nlanguage: python\n\ntasks:\n  test:\n    command: uv run pytest\n    inputs:\n      - \"src\u002F**\u002F*\"\n      - \"tests\u002F**\u002F*\"\n\n  lint:\n    command: uv run ruff check .\n","yaml",[151,1009,1010,1019,1033,1043,1049,1058,1066,1077,1085,1094,1102,1107,1115],{"__ignoreMap":483},[1011,1012,1015],"span",{"class":1013,"line":1014},"line",1,[1011,1016,1018],{"class":1017},"sJ8bj","# apps\u002Fanalytics-api\u002Fmoon.yml (Python service)\n",[1011,1020,1021,1025,1029],{"class":1013,"line":484},[1011,1022,1024],{"class":1023},"s9eBZ","type",[1011,1026,1028],{"class":1027},"sVt8B",": ",[1011,1030,1032],{"class":1031},"sZZnC","application\n",[1011,1034,1035,1038,1040],{"class":1013,"line":490},[1011,1036,1037],{"class":1023},"language",[1011,1039,1028],{"class":1027},[1011,1041,1042],{"class":1031},"python\n",[1011,1044,1046],{"class":1013,"line":1045},4,[1011,1047,1048],{"emptyLinePlaceholder":507},"\n",[1011,1050,1052,1055],{"class":1013,"line":1051},5,[1011,1053,1054],{"class":1023},"tasks",[1011,1056,1057],{"class":1027},":\n",[1011,1059,1061,1064],{"class":1013,"line":1060},6,[1011,1062,1063],{"class":1023},"  test",[1011,1065,1057],{"class":1027},[1011,1067,1069,1072,1074],{"class":1013,"line":1068},7,[1011,1070,1071],{"class":1023},"    command",[1011,1073,1028],{"class":1027},[1011,1075,1076],{"class":1031},"uv run pytest\n",[1011,1078,1080,1083],{"class":1013,"line":1079},8,[1011,1081,1082],{"class":1023},"    inputs",[1011,1084,1057],{"class":1027},[1011,1086,1088,1091],{"class":1013,"line":1087},9,[1011,1089,1090],{"class":1027},"      - ",[1011,1092,1093],{"class":1031},"\"src\u002F**\u002F*\"\n",[1011,1095,1097,1099],{"class":1013,"line":1096},10,[1011,1098,1090],{"class":1027},[1011,1100,1101],{"class":1031},"\"tests\u002F**\u002F*\"\n",[1011,1103,1105],{"class":1013,"line":1104},11,[1011,1106,1048],{"emptyLinePlaceholder":507},[1011,1108,1110,1113],{"class":1013,"line":1109},12,[1011,1111,1112],{"class":1023},"  lint",[1011,1114,1057],{"class":1027},[1011,1116,1118,1120,1122],{"class":1013,"line":1117},13,[1011,1119,1071],{"class":1023},[1011,1121,1028],{"class":1027},[1011,1123,1124],{"class":1031},"uv run ruff check .\n",[50,1126,1127,1130,1131],{},[19,1128,1129],{},"Cross-Language Dependency Graph:","\nMoon allows cross-language task dependencies. For example, a TypeScript frontend build task can declare a dependency on a Python OpenAPI schema generation task or a Rust WASM compilation task:\n",[1132,1133],"monorepo-dag-chart",{},[50,1135,1136,1139,1140,168,1143,1146],{},[19,1137,1138],{},"Universal Unified CI:","\nRunning ",[151,1141,1142],{},"moon run :lint",[151,1144,1145],{},"moon run :test"," runs the appropriate linter and test suite across TypeScript, Python, and Rust in topological order, parallelizing where possible and caching every output.",[36,1148],{},[39,1150,1152],{"id":1151},"_5-architectural-decision-matrix","5. Architectural Decision Matrix",[269,1154,1155,1177],{},[272,1156,1157],{},[275,1158,1159,1162,1169,1171,1173],{},[278,1160,1161],{"align":280},"Dimension",[278,1163,1164,320,1166,1168],{"align":280},[151,1165,141],{},[151,1167,148],{}," Workspaces",[278,1170,174],{"align":280},[278,1172,229],{"align":280},[278,1174,934,1175,365],{"align":280},[151,1176,937],{},[289,1178,1179,1198,1219,1240,1259,1276,1309],{},[275,1180,1181,1186,1189,1192,1195],{},[294,1182,1183],{"align":280},[19,1184,1185],{},"Primary Scope",[294,1187,1188],{"align":280},"Package linking & lockfiles",[294,1190,1191],{"align":280},"JS\u002FTS Task Orchestrator",[294,1193,1194],{"align":280},"Enterprise JS\u002FTS Ecosystem",[294,1196,1197],{"align":280},"Polyglot Repository Orchestrator",[275,1199,1200,1205,1208,1211,1214],{},[294,1201,1202],{"align":280},[19,1203,1204],{},"Language Support",[294,1206,1207],{"align":280},"Single language (JS or Python)",[294,1209,1210],{"align":280},"JS \u002F TS native (others via npm hacks)",[294,1212,1213],{"align":280},"JS \u002F TS native (Python via plugins)",[294,1215,1216],{"align":280},[19,1217,1218],{},"Node, Python, Rust, Go, Bun (Native)",[275,1220,1221,1226,1229,1231,1233],{},[294,1222,1223],{"align":280},[19,1224,1225],{},"Toolchain Management",[294,1227,1228],{"align":280},"None (host managed)",[294,1230,1228],{"align":280},[294,1232,1228],{"align":280},[294,1234,1235],{"align":280},[19,1236,1237,1238,365],{},"Built-in hermetic toolchains (",[151,1239,959],{},[275,1241,1242,1247,1250,1253,1256],{},[294,1243,1244],{"align":280},[19,1245,1246],{},"Computation Caching",[294,1248,1249],{"align":280},"❌ No",[294,1251,1252],{"align":280},"✅ Local & Remote (Vercel\u002FS3)",[294,1254,1255],{"align":280},"✅ Local & Remote (Nx Cloud)",[294,1257,1258],{"align":280},"✅ Local & Remote (AWS\u002FGCP\u002Fmoonbase)",[275,1260,1261,1266,1268,1271,1274],{},[294,1262,1263],{"align":280},[19,1264,1265],{},"Affected Detection",[294,1267,1249],{"align":280},[294,1269,1270],{"align":280},"✅ Git-based",[294,1272,1273],{"align":280},"✅ Git + AST graph",[294,1275,1270],{"align":280},[275,1277,1278,1283,1291,1296,1303],{},[294,1279,1280],{"align":280},[19,1281,1282],{},"Config Overhead",[294,1284,1285,1286,1288,1289,365],{"align":280},"Minimal (",[151,1287,924],{},"\u002F",[151,1290,666],{},[294,1292,1293,1294,365],{"align":280},"Low (",[151,1295,838],{},[294,1297,1298,1299,1302],{"align":280},"Medium-High (",[151,1300,1301],{},"nx.json"," + generators)",[294,1304,1305,1306,365],{"align":280},"Medium (",[151,1307,1308],{},".moon\u002F*.yml",[275,1310,1311,1316,1319,1322,1325],{},[294,1312,1313],{"align":280},[19,1314,1315],{},"Best Fit For",[294,1317,1318],{"align":280},"Small or pure single-stack repos",[294,1320,1321],{"align":280},"High-velocity pure TS\u002FJS repos",[294,1323,1324],{"align":280},"Large enterprise Angular\u002FReact setups",[294,1326,1327],{"align":280},[19,1328,1329],{},"Polyglot Python + TS + Rust architectures",[36,1331],{},[39,1333,1335],{"id":1334},"_6-summary-radar-verdict","6. Summary & Radar Verdict",[47,1337,1338,1347,1363],{},[50,1339,1340,809,1343,1346],{},[19,1341,1342],{},"Architecture Decision:",[19,1344,1345],{},"Adopt Monorepo."," The developer experience of unified PRs, atomic cross-service refactors, and synchronized contracts far outweighs the multi-repo coordination overhead.",[50,1348,1349,1352,1353,1357,1358,1362],{},[19,1350,1351],{},"Phase 1 Foundation:"," Always start with native workspaces—",[19,1354,1355],{},[151,1356,584],{}," for TypeScript, ",[19,1359,1360],{},[151,1361,654],{}," for Python. Keep things simple until build times demand orchestration.",[50,1364,1365,1368],{},[19,1366,1367],{},"Phase 2 Scaling:",[47,1369,1370,1382],{},[50,1371,1372,1373,1376,1377,1381],{},"If the repo remains ",[19,1374,1375],{},"purely TypeScript",", adopt ",[19,1378,1379],{},[30,1380,174],{"href":173}," for zero-friction caching.",[50,1383,1384,1385,222,1388,1392],{},"If the repo is a ",[19,1386,1387],{},"polyglot hybrid (Python + TypeScript + Rust)",[19,1389,1390],{},[30,1391,167],{"href":166}," is the clear front-runner, providing hermetic multi-runtime toolchains and true cross-language task pipelines.",[36,1394],{},[39,1396,1398],{"id":1397},"_7-connected-deep-dives-tech-reports","7. Connected Deep Dives & Tech Reports",[15,1400,1401],{},"Explore the individual technology evaluations that form our monorepo web:",[47,1403,1404,1412,1420,1428,1436,1444,1452,1460,1468],{},[50,1405,1406,1411],{},[19,1407,1408,238],{},[30,1409,1410],{"href":556},"Polyrepo (Rejected)"," Why multi-repo architectures failed our velocity tests and were rejected.",[50,1413,1414,1419],{},[19,1415,1416,238],{},[30,1417,1418],{"href":140},"pnpm (Adopted Standard)"," Content-addressable package management and workspace linking for Node\u002FTS.",[50,1421,1422,1427],{},[19,1423,1424,238],{},[30,1425,1426],{"href":122},"npm (Rejected)"," Detailed analysis of phantom dependencies and flat node_modules liabilities.",[50,1429,1430,1435],{},[19,1431,1432,238],{},[30,1433,1434],{"href":147},"uv (Adopted Standard)"," The high-speed Rust toolchain powering our Python workspaces.",[50,1437,1438,1443],{},[19,1439,1440,238],{},[30,1441,1442],{"href":466},"pip (Rejected)"," Why pip and requirements.txt were retired in favor of uv.",[50,1445,1446,1451],{},[19,1447,1448,238],{},[30,1449,1450],{"href":173},"Turborepo (Adopted for TS)"," Task orchestration and remote caching for pure TypeScript monorepos.",[50,1453,1454,1459],{},[19,1455,1456,238],{},[30,1457,1458],{"href":166},"Moon (Adopted for Polyglot)"," Polyglot monorepo build system unifying Python, TypeScript, and Rust.",[50,1461,1462,1467],{},[19,1463,1464,238],{},[30,1465,1466],{"href":228},"Nx (Hold \u002F Assess)"," Enterprise-scale monorepo tool with deep AST code analysis and generators.",[50,1469,1470,1476],{},[19,1471,1472,238],{},[30,1473,1475],{"href":1474},"\u002Fblog\u002Fbun","Bun (Trial)"," Fast JavaScript runtime, bundler, and package manager evaluated for edge services.",[1478,1479,1480],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":483,"searchDepth":484,"depth":484,"links":1482},[1483,1487,1491,1495,1500,1501,1502],{"id":562,"depth":484,"text":563,"children":1484},[1485,1486],{"id":575,"depth":490,"text":576},{"id":633,"depth":490,"text":634},{"id":707,"depth":484,"text":708,"children":1488},[1489,1490],{"id":714,"depth":490,"text":715},{"id":736,"depth":490,"text":737},{"id":793,"depth":484,"text":794,"children":1492},[1493,1494],{"id":800,"depth":490,"text":801},{"id":847,"depth":490,"text":848},{"id":900,"depth":484,"text":901,"children":1496},[1497,1498],{"id":910,"depth":490,"text":911},{"id":933,"depth":490,"text":1499},"Moon (moonrepo): The True Polyglot Architecture",{"id":1151,"depth":484,"text":1152},{"id":1334,"depth":484,"text":1335},{"id":1397,"depth":484,"text":1398},"adopt","Preferred mainly because independent deployment is still fully achievable through proper CI\u002FCD, while a feature spanning multiple modules (say, two microservices and a UI) stays a single PR: easy to review, and deployed together without coordinating release order across repositories. Deployment is a CI\u002FCD problem, not a code organization problem.","Single-repository architecture, adopted in favor of splitting services across multiple repos.",{},"\u002Fradar\u002Fmonorepo",[1509],{"category":511,"subCategory":512},{"title":33,"description":1505},"radar\u002Fmonorepo",[502,512,519,520,1513,1514,1515],"tooling","python","typescript","YlO4m1SL1bxbKo3xcspMeLb6HYbRRW0jSKXiKvQmDec",1790263542579]