{"componentChunkName":"component---src-components-blog-template-js","path":"/blog/2024-12-01-monorepo/","result":{"data":{"markdownRemark":{"frontmatter":{"title":"Monorepo","date":"2024-12-01"},"html":"<p>A monorepo is a single repository that contains multiple projects, packages, or applications. Instead of having separate repositories for the frontend, backend, and shared libraries, everything lives in one place. This approach is used by large companies like Google, Meta, and Microsoft, but it is also practical for smaller teams.</p>\n<h3>Why Monorepo</h3>\n<p>The main advantage is that code sharing becomes simple. If we have a shared utility library or type definitions used by both the frontend and backend, they live in the same repository. Changes to the shared code and the consuming projects can happen in a single commit, so things stay in sync.</p>\n<p>Cross-project refactoring is easier too. If we rename a function in a shared library, we can update all usages across the entire codebase in one pull request. In a multi-repo setup, this requires coordinating changes across multiple repositories and releases.</p>\n<p>Dependency management is also simpler. All projects use the same version of external dependencies, which avoids version conflicts and the confusion of having different parts of the system running different versions of the same library.</p>\n<h3>Tooling</h3>\n<p>A monorepo needs good tooling to work well. Without it, build times and test runs become painfully slow because every change triggers everything.</p>\n<p><strong>Turborepo</strong> and <strong>Nx</strong> are popular tools for JavaScript and TypeScript monorepos. They provide task orchestration with caching. When we run a build, these tools figure out which packages changed and only rebuild those. Results from previous builds are cached, so unchanged packages do not need to be rebuilt.</p>\n<p><strong>Workspaces</strong> in npm, Yarn, or pnpm allow us to manage multiple packages within a single repository. Each package has its own <code class=\"language-text\">package.json</code>, but dependencies are hoisted to the root to avoid duplication.</p>\n<p>A typical monorepo structure might look like this:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">packages/\n  shared/\n    package.json\n  web/\n    package.json\n  api/\n    package.json\npackage.json\nturbo.json</code></pre></div>\n<h3>Task Pipelines</h3>\n<p>Monorepo tools let us define task dependencies. For example, we can specify that <code class=\"language-text\">web</code> must be built after <code class=\"language-text\">shared</code> because <code class=\"language-text\">web</code> depends on it. The tool handles the ordering and runs independent tasks in parallel.</p>\n<p>In Turborepo, this is configured in <code class=\"language-text\">turbo.json</code>:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">{\n  \"pipeline\": {\n    \"build\": {\n      \"dependsOn\": [\"^build\"],\n      \"outputs\": [\"dist/**\"]\n    },\n    \"test\": {\n      \"dependsOn\": [\"build\"]\n    }\n  }\n}</code></pre></div>\n<p>The <code class=\"language-text\">^build</code> notation means the build of dependencies must complete before the current package builds.</p>\n<h3>Trade-Offs</h3>\n<p>Monorepos are not without challenges. The repository can grow large, and cloning it takes longer. CI pipelines need to be set up carefully to avoid running all tests on every change. Code ownership boundaries are less explicit than having separate repositories, which requires good conventions and clear directory structures.</p>\n<p>For small teams with a few closely related projects, a monorepo usually simplifies the workflow. For organisations with many independent teams working on unrelated projects, separate repositories with clear contracts might be a better fit.</p>"}},"pageContext":{"slug":"/2024-12-01-monorepo/"}},"staticQueryHashes":[]}