{"id":2975,"date":"2024-12-08T03:07:03","date_gmt":"2024-12-08T03:07:03","guid":{"rendered":"https:\/\/www.aviator.co\/blog\/?p=2975"},"modified":"2025-09-23T12:42:05","modified_gmt":"2025-09-23T12:42:05","slug":"monorepo-a-hands-on-guide-for-managing-repositories-and-microservices","status":"publish","type":"post","link":"https:\/\/www.aviator.co\/blog\/monorepo-a-hands-on-guide-for-managing-repositories-and-microservices\/","title":{"rendered":"Monorepo: Hands-On Guide for Managing Repositories and Microservices"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img fetchpriority=\"high\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/cover-monorepo-1024x576.png\" alt=\"\" class=\"wp-image-3431\" srcset=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/cover-monorepo-1024x576.png 1024w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/cover-monorepo-300x169.png 300w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/cover-monorepo-768x432.png 768w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/cover-monorepo-1536x864.png 1536w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/cover-monorepo.png 1600w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>TL;DR<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Monorepos<\/strong> group multiple microservices and libraries into a single repository, enabling atomic commits, easier large-scale refactors, and shared tooling but introduce challenges in build times, CI\/CD scaling, and code review complexity.<\/li>\n\n\n\n<li>Tools like Bazel, Nx, and Turborepo help manage builds, cache results, and speed up pipelines, making monorepos practical even at large scale.<\/li>\n\n\n\n<li>Best practices include modular project structure, strict code ownership rules, dependency graphs to isolate changes, and using selective build\/test pipelines to avoid slow full builds.<br>Monorepos support cross-service refactors and consistent linting\/testing, while polyrepos allow service-level autonomy, smaller repos, and independent deployment cycles.<\/li>\n\n\n\n<li><strong>Migration isn\u2019t trivial: <\/strong>consolidating repos can break existing CI\/CD, create merge conflicts, and require new tooling so gradual adoption, automated checks, and clear ownership policies are essential to keep teams productive.<br><\/li>\n<\/ul>\n\n\n\n<p>The biggest challenge for a developer working on a large project with many components or services is managing a codebase with several tech stacks and framework dependencies. Maintaining a clear sense of ownership among team members for each component is also essential to ensuring accountability and effective growth.<\/p>\n\n\n\n<p>A <a href=\"https:\/\/www.aviator.co\/blog\/what-is-a-monorepo-and-why-use-one\/\" target=\"_blank\" rel=\"noopener\" title=\"Monorepo \">Monorepo <\/a>can address these problems by putting everything into a shared repository. Teams can enforce uniform procedures and shared libraries and monitor changes throughout the codebase using this configuration. This monorepo structure facilitates code organization, dependency management, and compatibility testing, which makes it easier for developers to collaborate and uphold accountability. This strategy is used by even major tech firms like Google and Facebook to handle their extensive projects efficiently.<\/p>\n\n\n\n<p>Depending on your particular use case, we will go into great detail on what Monorepos are, how they operate, and the many build tools you may use for your projects. We will also go over the difficulties of dealing with a monolithic repository and walk you through a practical sample project so you can grasp how to use a Monrepo and incorporate it into your workflow.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What are Monorepos<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>A monorepo is a single repository with several separate services, modules, or components, some of which may have been developed using a different framework or language.<\/p>\n\n\n\n<p>Exempli Gratia: Take the example of a large e-commerce site where the user interface is build in React, the backend services is built in with either Python or Node.js, and a product recommendation system uses a machine learning model made in python. Now imagine all these have their own files in a monorepo. By keeping everything in one place, this setup facilitates the coordination of updates, the sharing of common dependencies, and the incident-free monorepo management of changes throughout the whole system.<\/p>\n<\/blockquote>\n\n\n\n<p>This stability in a Monorepo code structure is maintained by a single-build tool that maintains all projects, ensuring overall consistency and efficiency. Meanwhile, a Polyrepo or multiple repository approach frequently relies on multiple build tools, each tailored to the individual services or APIs, giving teams flexibility but resulting in inconsistent configurations such as repositories with different versions of the same dependency, as well as varying code standards and linting rules.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"1024\" height=\"680\" src=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/monorepo-dir.png\" alt=\"\" class=\"wp-image-3433\" srcset=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/monorepo-dir.png 1024w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/monorepo-dir-300x199.png 300w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/monorepo-dir-768x510.png 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Monorepo architecture vs Polyrepo architecture<\/figcaption><\/figure>\n\n\n\n<p>A monorepo\u2019s unified directory structure, where related code is organized in shared folders, contrasts with a polyrepo\u2019s isolated project setup, where each project operates independently.<\/p>\n\n\n\n<p>Create polyrepo architecture which come with specific challenges, especially when it comes to sharing code across projects. Managing dependencies can be complicated since they often require maintaining separate repos or shared libraries. While this approach can offer flexibility, it also has downsides, like difficulties in version control and collaboration as projects grow. Knowing these factors helps choose the best setup for managing large projects effectively.<\/p>\n\n\n\n<p>Similarly, in a monorepo, strict conventions and thorough testing are needed to prevent issues when project changes occur. As the monorepo grows, managing scalability can also become challenging, especially with larger teams and multiple projects, but this isn\u2019t as much of an issue in polyrepo setups.<\/p>\n\n\n\n<p>For additional context on the background and evolution of Monorepos, check out Aviator&#8217;s blog <a href=\"https:\/\/www.aviator.co\/blog\/what-is-a-monorepo-and-why-use-one\/\" target=\"_blank\" rel=\"noopener\" title=\"\">here<\/a>, which analyzes Monorepo and why you should use one for your projects.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How to Manage Microservices in Monorepo<\/h2>\n\n\n\n<p>So far, we know that a monorepo directory structure makes collaboration easier because teams don\u2019t need to switch between separate repositories to find the features they\u2019re working on. Instead, everything is organized in one place, with clear folder structures for each project or service.<\/p>\n\n\n\n<p>Let\u2019s say you\u2019re working in a team with a Python project folder for backend monorepo services and a JavaScript folder for the frontend and dashboard, which require regular updates and fixes to keep them running smoothly. In a monorepo, each service is organized within its folder, but everyone can see the code structure at once. This setup makes it easy for teams to share utility libraries, keep updates consistent across services, and avoid redundancy. For example, if a shared configuration or API client needs to be updated, all services can access the latest version simultaneously.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"632\" src=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/monorepo-directory-1024x632.png\" alt=\"\" class=\"wp-image-3434\" srcset=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/monorepo-directory-1024x632.png 1024w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/monorepo-directory-300x185.png 300w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/monorepo-directory-768x474.png 768w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/monorepo-directory-1536x948.png 1536w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/monorepo-directory.png 1600w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Infographic illustrating directory structure in a monorepo project<\/figcaption><\/figure>\n\n\n\n<p>For example, if multiple projects use the same authentication library, such as JSON web tokens, OAuth, and AWS Cognito SDK, monorepo allows teams to update or improve this library in one location, instantly applying those changes across all projects that rely on it. This reduces duplication and keeps code consistent throughout the system.<\/p>\n\n\n\n<p>Monorepos also helps with dependency management. Rather than each project handling its dependencies, which can lead to dependency version conflicts, a monorepo keeps all dependencies in one place, ensuring that compatible versions are used across projects. This setup minimizes the risk of dependency issues and simplifies updating libraries, as all projects are built and tested with identical dependency versions.<\/p>\n\n\n\n<p>Another significant benefit is having a single CI\/CD pipeline. It\u2019s not like you can\u2019t use CI\/CD pipelines with polyrepos as well. Still, In a monorepo, you can set up one continuous integration and delivery process to automatically test, build, and deploy changes across all services under the same pipeline otherwise, you need to take care of multiple pipelines for multiple services. This unified pipeline saves time and reduces complexity, as you don\u2019t need to create and maintain separate pipelines for each project. With a single source of truth for building and deploying code, developers can track changes more efficiently and keep everything in sync, which is crucial for maintaining efficiency in larger projects.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Difficulties in Managing Microservices within a Monorepo<\/h2>\n\n\n\n<p>As discussed above, microservices in monorepo is a better choice if you deal with multiple projects simultaneously. But, one should also be aware of the challenges you may face while working with monorepos as managing a monorepo comes with its own set of issues.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Complex Setup<\/strong><\/h3>\n\n\n\n<p>Monorepos are known for having <strong>Complex Setup<\/strong>, as getting each consumer project running locally can be daunting, often requiring you to navigate outdated documentation, fix database issues, and manage feature flags, among other setup challenges.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Dependency Management<\/strong><\/h3>\n\n\n\n<p>Another significant issue is <strong>dependency management<\/strong>. Now, we have discussed above how Monorepo helps to mitigate dependency version conflicts for us, and that gives it a substantial edge over polyrepos. Still, since all code is in one place, it also becomes easy for dependencies to overlap, causing conflicts or even breaking projects.<\/p>\n\n\n\n<p>Additionally, large monorepos code bases can lead to tightly coupled code, where changes in one service affect others, making updates more complex. This coupling often requires strict conventions to keep code modular and avoid unintended dependencies across projects.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Too Many Commits in a Single Repository<\/strong><\/h3>\n\n\n\n<p>A high number of commits in one repository can slow down Git operations like git log and git blame since Git has to handle an extensive history of changes. This can make the repository sluggish, mainly as more projects and developers contribute to the same repo.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Performance Bottlenecks<\/strong><\/h3>\n\n\n\n<p>Performance issues also arise, particularly with the number of tracked commits, branches, and files. A large volume of commits can slow down operations like <em>git log<\/em> or<em> git blame<\/em>, as Git has to navigate a complex history graph. With many unrelated projects in a single repository, performance can degrade significantly.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Testing Hurdles and Optimizing the CI\/CD Pipeline<\/strong><\/h3>\n\n\n\n<p>Running tests, builds, and deployments for a large monorepo can become slow and resource-heavy, especially as the codebase grows. Many monorepos lack adequate code coverage, making testing changes thoroughly difficult. If you&#8217;re responsible for breaking changes, you might need to test numerous dependent projects, which can be time-consuming manually.<\/p>\n\n\n\n<p>To keep things efficient, teams often need automated pipelines to run selective builds only for affected parts of the code, saving time and resources. Using a modern build system like Bazel or Gradle can help managing remote caching and perform an affected targets based distributed tests.<\/p>\n\n\n\n<p>Modular configurations are also recommended, which structure configurations by projects or services within a monorepo, allowing each module or service to maintain its settings while remaining part of the shared repository. This eventually helps keep <a href=\"https:\/\/www.aviator.co\/blog\/how-to-scale-release-management-for-monorepos\/\" target=\"_blank\" rel=\"noopener\" title=\"monorepos manageable at scale\">monorepos manageable at scale<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Code Ownership<\/strong><\/h3>\n\n\n\n<p>As we often hear the phrase, <em>\u201cToo many cooks in the kitchen can burn the food<\/em>\u201d. This also proves true for developers when working on large monorepos. Since a monorepo encourages shared ownership, setting clear guidelines for code contributions and review processes is critical. Without this, projects can quickly become messy, and it\u2019s easy for <a href=\"https:\/\/www.aviator.co\/blog\/technical-debt-and-the-role-of-refactoring\/\" target=\"_blank\" rel=\"noopener\" title=\"technical debt \">technical debt <\/a>to build up. By implementing CI\/CD pipelines, version control, and consistent coding standards, teams can stay organized and avoid many challenges.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Build Tools for Managing Monorepos<\/h2>\n\n\n\n<p>Monorepo tools are a significant part of what makes the monorepos operational. Without them, managing the projects, dependencies, build control, and version control can become manageable.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"853\" src=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/monorepo-tools-1024x853.png\" alt=\"\" class=\"wp-image-3435\" srcset=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/monorepo-tools-1024x853.png 1024w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/monorepo-tools-300x250.png 300w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/monorepo-tools-768x640.png 768w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/monorepo-tools-1536x1280.png 1536w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/monorepo-tools.png 1600w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Infographic table for comparing bazel, lerna, turborepo, and nx<\/figcaption><\/figure>\n\n\n\n<p>For monorepos containing multiple frontend frameworks, TurboRepo and Nx optimize performance with features like intelligent caching and incremental builds, speeding up development cycles. They excel in managing dependencies and maintaining consistent configurations, reducing redundancy, and minimizing build times. This ensures frontend components remain compatible and up-to-date across the repository.<\/p>\n\n\n\n<p>Bazel and Buck2 are ideal for managing services and libraries for backend systems. Bazel supports parallel execution, remote caching, and a robust dependency system, reducing redundant builds and speeding up processes. Buck2 focuses on fine-grained caching and efficient dependency tracking, offering faster incremental builds and lower storage needs, especially for large, complex codebases.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">When to Use a Monorepo<\/h2>\n\n\n\n<p>Some of the most common and well-known applications include:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Microservices Architecture<\/strong><\/h3>\n\n\n\n<p>In a microservices setup, each service handles a specific function within a more extensive application, such as user management, payments, or notifications. Managing these in a monorepo enables developers to keep all related services in one place, making it easier to share code, such as standard authentication modules or utility functions.<\/p>\n\n\n\n<p>For example, if a change is made to the authentication logic, teams can immediately update all services that depend on it, maintaining consistency across the system without needing to track changes across multiple repositories.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Cross-team Collaboration<\/strong><\/h3>\n\n\n\n<p>Monorepos improves team collaboration as all the code is centralized. It allows teams to easily access, review, and integrate changes from other services. This setup reduces interference to cross-team code sharing and consistency, which is especially useful for enterprises managing multiple services or APIs.<\/p>\n\n\n\n<p>For instance, a frontend and backend team working on the same application can easily align and coordinate changes. This avoids the challenges of merging code from separate repositories and ensures a more seamless development process across project boundaries.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Rapid Development Cycles<\/strong><\/h3>\n\n\n\n<p>For teams focusing on rapid development cycles, monorepos enable faster updates by centralizing the codebase and making managing changes across projects easier.<\/p>\n\n\n\n<p>For example, if an organization frequently updates its app with new features, a monorepo setup allows changes to be implemented across the system more efficiently without the overhead of navigating multiple repositories. The ability to run a single, unified CI\/CD pipeline further streamlines the process, speeding up testing deployment and ensuring all components remain compatible.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Hands-on Example with Bazel<\/strong><\/h2>\n\n\n\n<p>Now, to get a better understanding of how you can create a Monorepo with two or more services and with a mainstream build tool, we will go through a hands-on example of where we will be making a root directory, and within it, we will have a frontend project created using react project, and a backend project created using Flask in Python.<\/p>\n\n\n\n<p>Build tools like Bazel and Turborepo ensure smooth codebase growth by handling builds and dependencies efficiently. They rebuild only changed parts of the code, preventing slowdowns as the codebase expands. By enforcing consistent build rules, they maintain compatibility across projects, making adding new features or services easier without delays or clutter.<\/p>\n\n\n\n<p>We will use Bazel as the build tool for this demonstration, as it is pretty versatile and compatible with most tech stacks.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Installing Bazelisk<\/strong><\/h3>\n\n\n\n<p>We first need to install Bazelisk on our development machine to get started. <span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\">Bazelisk is a version manager for Bazel, much like how&nbsp;<em><strong>Python Version Manager (<\/strong><\/em><a href=\"https:\/\/github.com\/pyenv\/pyenv\" target=\"_blank\"><em><strong>pyenv<\/strong><\/em><\/a>) and&nbsp;<em><strong>Node Version Manager<\/strong><\/em>&nbsp;(<a href=\"https:\/\/github.com\/nvm-sh\/nvm\" target=\"_blank\"><em><strong>nvm<\/strong><\/em><\/a>) work.<\/span><\/p>\n\n\n\n<p>Follow the installation instructions on <a href=\"https:\/\/github.com\/bazelbuild\/bazelisk#installation\"><strong><em>Bazelisk\u2019s official github repository<\/em><\/strong><\/a> to install <span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\">it on your machine, or you can also use&nbsp;<a href=\"https:\/\/github.com\/bazelbuild\/bazelisk\/releases\" target=\"_blank\"><em><strong>Bazelisk\u2019s Releases page<\/strong><\/em><\/a>&nbsp;to install it&nbsp;<\/span>for your desired operating system.<\/p>\n\n\n\n<p>After your installation is completed, you can run the following command to check your Bazel version:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>baselisk --version<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Setting Up the Root Directory<\/strong><\/h3>\n\n\n\n<p>We will now set up our mono repository structure. For this, we will now create a folder called <strong><em>\u201cMonorepo-demo\u201d<\/em>. You<\/strong> can name it as per your preference.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir Monorepo-demo<\/code><\/pre>\n\n\n\n<p>Next will create two more folders inside the root monorepo directory, the first one will be for the backend service which we will be running on Python and the second one will be for a frontend service which we will be creating using React.js.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd Monorepo-demo\nmkdir backend-python\nmkdir frontend-react<\/code><\/pre>\n\n\n\n<p>The folder structure will look something like this:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">.<br>\u251c\u2500\u2500 backend-python<br>\u2514\u2500\u2500 frontend-react<\/pre>\n\n\n\n<p>Now, we have taken the first step towards creating a monolithic repository.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Creating a React Application<\/strong><\/h3>\n\n\n\n<p>We will now start with the setup and configuration of our react project. First of all, we need to navigate to the React project directory to install the <a href=\"https:\/\/create-react-app.dev\/docs\/getting-started\"><strong><em>Create React App Template (CRA)<\/em><\/strong><\/a><strong><em>.<\/em><\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd frontend-react<\/code><\/pre>\n\n\n\n<p>Now, for the following step to work as intended, ensure you have installed Node.js on your machine. You can check Node\u2019s version using the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>node -v<\/code><\/pre>\n\n\n\n<p>To use Bazel, we need to define &#8220;rules.&#8221; These rules are essential to Bazel and determine how projects or services are built within the monorepo. Rules allow Bazel to incorporate new tools for creating and testing different languages and frameworks, making them a crucial part of Bazel&#8217;s functionality.<\/p>\n\n\n\n<p>For React.js, we will be using Aspect\u2019s Rules. These rules are third-party helper rules written by engineers at Aspect Build. They are open-sourced and are recommended by Bazel\u2019s community themself.<\/p>\n\n\n\n<p>Also, one thing to note is that Aspect\u2019s Rules for javascript have been created to be used with the pnpm package manager. So, for this demonstration, we will work with pnpm as our default package manager. You can read more about why Aspect uses pnpm <a href=\"https:\/\/docs.aspect.build\/rulesets\/aspect_rules_js\/docs\/pnpm\">here<\/a>.<\/p>\n\n\n\n<p>To use pnpm, simply use the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install --global pnpm<\/code><\/pre>\n\n\n\n<p>This command will install pnpm globally on your machine so that you can access it from any folder.<\/p>\n\n\n\n<p>Now, we will install the Create React App template using the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pnpm create react-app .<\/code><\/pre>\n\n\n\n<p>This will create your React starter project folder it will look something like this:<br><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">.<br>\u251c\u2500\u2500 node_modules<br>\u251c\u2500\u2500 package-lock.json<br>\u251c\u2500\u2500 package.json<br>\u251c\u2500\u2500 public<br>\u251c\u2500\u2500 README.md<br>\u2514\u2500\u2500 src<\/pre>\n\n\n\n<p>You can use the start script to check if your react app is running successfully:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pnpm start<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">Compiled successfully!<br>You can now view frontend-react in the browser.<br>&nbsp;&nbsp;http:\/\/localhost:3000<br>Note that the development build is not optimized.To create a production build, use npm run build.<br>webpack compiled successfully<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Creating Bazel Configuration Files<\/strong><\/h3>\n\n\n\n<p>Now that the Monorepo file structure and React application setup are complete. We can move forward and create our Bazel Workspace configuration files, which Bazel would need to generate a build for the applications.<\/p>\n\n\n\n<p>First of all, we will create a file named <strong>WORKSPACE<\/strong>. This file is responsible for defining dependencies and handling modules within the project. But, in the latest version of Bazel, the approach has shifted to using<strong> MODULE.bazel<\/strong> as the main configuration file for defining dependencies and managing the project&#8217;s external modules. An important point to note is that <strong>WORKSPACE<\/strong> is still required, even if blank, for backward compatibility within the project structure.<\/p>\n\n\n\n<p>We will also create a <strong>BUILD.bazel<\/strong> file to define the build constraints in the monorepo directory.<\/p>\n\n\n\n<p>Next, we wil define the workspace Bazel configuration in the <strong>.bazelrc<\/strong> file.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><strong>.bazelrc<\/strong><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>common <em>--enable_bzlmod<\/em>\nbuild <em>--enable_runfiles<\/em><\/code><\/pre>\n\n\n\n<p>In this .bazelrc setup, <strong>common &#8211;enable_bzlmod<\/strong> turns on Bazel\u2019s new system for managing dependencies, called bzlmod, for all commands. This change means Bazel will use the <strong>MODULE.bazel <\/strong>file instead of the older <strong>WORKSPACE<\/strong> file, which helps organize dependencies more straightforwardly. The build <strong>&#8211;enable_runfiles<\/strong> line ensures that Bazel includes runfiles when building on Windows. This is important for rules like rules_js, which need access to specific files to run correctly. Enabling runfiles here ensures everything works smoothly on Windows, where runfiles aren\u2019t always set up by default.<\/p>\n\n\n\n<p>Now, to let Bazel configure a workspace in a project managed by pnpm, we will list those project directories in the <strong>pnpm-workspace.yaml <\/strong>file.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><strong>pnpm-workspace.yaml<\/strong><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>packages:\n&nbsp; - frontend-react<\/code><\/pre>\n\n\n\n<p>With the PNPM workspace folders defined, we need to create a <strong>package.json<\/strong> file to handle configurations specific to the PNPM package manager and manage missing dependencies.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><strong>package.json<\/strong><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n&nbsp; \"name\": \"test-monorepo\",\n&nbsp; \"version\": \"0.1.0\",\n&nbsp; \"dependencies\": {\n&nbsp; &nbsp; \"react\": \"^18.2.0\",\n&nbsp; &nbsp; \"react-dom\": \"^18.2.0\",\n&nbsp; &nbsp; \"react-scripts\": \"5.0.1\",\n&nbsp; &nbsp; \"web-vitals\": \"^2.1.4\"\n&nbsp; },\n&nbsp; \"devDependencies\": {\n&nbsp; &nbsp; \"@types\/react\": \"^18.2.15\",\n&nbsp; &nbsp; \"@types\/react-dom\": \"^18.2.7\",\n&nbsp; &nbsp; \"eslint\": \"^8.45.0\",\n&nbsp; &nbsp; \"eslint-plugin-react\": \"^7.32.2\",\n&nbsp; &nbsp; \"eslint-plugin-react-hooks\": \"^4.6.0\",\n&nbsp; &nbsp; \"eslint-plugin-react-refresh\": \"^0.4.3\"\n&nbsp; },\n&nbsp; \"pnpm\": {\n&nbsp; &nbsp; \"\/\/packageExtensions\": \"Fix missing dependencies in npm packages, see https:\/\/pnpm.io\/package_json#pnpmpackageextensions\",\n&nbsp; &nbsp; \"packageExtensions\": {\n&nbsp; &nbsp; &nbsp; \"@typescript-eslint\/eslint-plugin\": {\n&nbsp; &nbsp; &nbsp; &nbsp; \"peerDependencies\": {\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \"eslint\": \"*\"\n&nbsp; &nbsp; &nbsp; &nbsp; }\n&nbsp; &nbsp; &nbsp; },\n&nbsp; &nbsp; &nbsp; \"postcss-loader\": {\n&nbsp; &nbsp; &nbsp; &nbsp; \"peerDependencies\": {\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \"postcss-flexbugs-fixes\": \"*\",\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \"postcss-preset-env\": \"*\",\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \"postcss-normalize\": \"*\"\n&nbsp; &nbsp; &nbsp; &nbsp; }\n&nbsp; &nbsp; &nbsp; }\n&nbsp; &nbsp; }\n&nbsp; }\n}<\/code><\/pre>\n\n\n\n<p><span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\">We will now configure the project module for this Bazel repository with the&nbsp;<strong>MODULE.bazel<\/strong>&nbsp;file.<\/span><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">MODULE.bazel<\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>module(name = \"test-monorepo\", version = \"1.0\")\n\n<em>#javascript<\/em>\n\nbazel_dep(name = \"aspect_rules_js\", version = \"1.32.5\")\n\n<em>####### Node.js version #########<\/em>\n<em># By default you get the node version from DEFAULT_NODE_VERSION in @rules_nodejs\/\/nodejs:repositories.bzl<\/em>\n<em># Optionally you can pin a different node version:<\/em>\nbazel_dep(name = \"rules_nodejs\", version = \"5.8.2\")\nnode = use_extension(\"@rules_nodejs\/\/nodejs:extensions.bzl\", \"node\")\nnode.toolchain(node_version = \"16.14.2\")\n<em>#################################<\/em>\n\nnpm = use_extension(\"@aspect_rules_js\/\/npm:extensions.bzl\", \"npm\", dev_dependency = True)\n\nnpm.npm_translate_lock(\n&nbsp; &nbsp; name = \"npm\",\n&nbsp; &nbsp; bins = {\n&nbsp; &nbsp; &nbsp; &nbsp; \"react-scripts\": &#91;\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \"react-scripts=.\/bin\/react-scripts.js\",\n&nbsp; &nbsp; &nbsp; &nbsp; ],\n&nbsp; &nbsp; },\n&nbsp; &nbsp; data = &#91;\n&nbsp; &nbsp; &nbsp; &nbsp; \"\/\/:package.json\",\n&nbsp; &nbsp; &nbsp; &nbsp; \"\/\/:pnpm-workspace.yaml\",\n&nbsp; &nbsp; &nbsp; &nbsp; \"\/\/:frontend-react\/package.json\",\n&nbsp; &nbsp; ],\n&nbsp; &nbsp; npmrc = \"\/\/:.npmrc\",\n&nbsp; &nbsp; pnpm_lock = \"\/\/:pnpm-lock.yaml\",\n&nbsp; &nbsp; verify_node_modules_ignored = \"\/\/:.bazelignore\",\n&nbsp; &nbsp; update_pnpm_lock = 1,\n)\n\nuse_repo(npm, \"npm\")\nbazel_dep(name = \"aspect_bazel_lib\", version = \"1.35.0\")\npnpm = use_extension(\"@aspect_rules_js\/\/npm:extensions.bzl\", \"pnpm\", dev_dependency = True)\n\nuse_repo(pnpm, \"pnpm\")<\/code><\/pre>\n\n\n\n<p>In the <strong>MODULE.bazel<\/strong> file, we define the project module for Bazel, managing dependencies for the monorepo named test-monorepo with version 1.0. It includes the aspect_rules_js dependency (v1.32.5) to set up JavaScript build rules and <strong>rules_nodejs<\/strong> (v5.8.2) to ensure Node.js support, specifically using version 16.14.2.<\/p>\n\n\n\n<p>The file then imports the NPM extension from aspect_rules_js, treating it as a development dependency. This translates the package.json and lock file for Bazel and links necessary scripts like react-scripts. It also registers aspect_bazel_lib (v1.35.0) for additional Bazel tools and the pnpm extension as a dev dependency to complete the setup. This configuration organizes dependencies and keeps JavaScript tools in sync across the monorepo.<\/p>\n\n\n\n<p>Remember the <strong>BUILD.bazel<\/strong> file, did we create it in the root directory? We\u2019ll use it to link all necessary packages, including the workspace\u2019s NPM packages, packages from the Bazel binary, and the virtual store for<strong> aspect_rules_js<\/strong>. This setup works because pnpm-lock.yaml is at the root of the pnpm workspace, allowing us to manage dependencies effectively.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">BUILD.bazel<\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>load(\"@aspect_rules_js\/\/js:defs.bzl\", \"js_binary\")\nload(\"@npm\/\/:defs.bzl\", \"npm_link_all_packages\")\n\nnpm_link_all_packages(\n&nbsp; &nbsp; name = \"node_modules\",\n)<\/code><\/pre>\n\n\n\n<p>This snippet loads two Bazel rules to manage JavaScript packages. The<strong> js_binary<\/strong> rule from <strong>aspect_rules_js<\/strong> is loaded first, which allows us to create runnable JavaScript files within the project.<\/p>\n\n\n\n<p>Then,<strong> npm_link_all_packages<\/strong> from the npm dependency is loaded and calls the node_modules, linking all npm packages from <strong>package.json <\/strong>into Bazel. This setup ensures all Node.js dependencies are accessible within the Bazel environment, keeping everything organized and ready to use.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Creating the Build File for React Application Folder<\/strong><\/h3>\n\n\n\n<p>We have set up the entire workspace to work with Bazel. All we need to do is create a BUILD.bazel file in the React project folder so that Bazel can build the frontend.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">frontend-react\/BUILD.bazel<\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>load(\"@aspect_rules_js\/\/js:defs.bzl\", \"js_run_devserver\")\nload(\"@npm\/\/:defs.bzl\", \"npm_link_all_packages\")\nload(\"@npm\/\/:react-scripts\/package_json.bzl\", cra_bin = \"bin\")\n\nnpm_link_all_packages()\n\nCRA_DEPS = &#91;\n&nbsp; &nbsp; \"\/\/packages\/my-app\/src\",\n&nbsp; &nbsp; \"\/\/packages\/my-app\/public\",\n&nbsp; &nbsp; \":node_modules\/react-dom\",\n&nbsp; &nbsp; \":node_modules\/react-scripts\",\n&nbsp; &nbsp; \":node_modules\/react\",\n&nbsp; &nbsp; \":node_modules\/web-vitals\",\n&nbsp; &nbsp; \":package.json\",\n]\n\ncra_bin.react_scripts(\n&nbsp; &nbsp; <em># Note: If you want to change the name make sure you update BUILD_PATH below accordingly<\/em>\n&nbsp; &nbsp; <em># https:\/\/create-react-app.dev\/docs\/advanced-configuration\/<\/em>\n&nbsp; &nbsp; name = \"build\",\n&nbsp; &nbsp; srcs = CRA_DEPS,\n&nbsp; &nbsp; args = &#91;\"build\"],\n&nbsp; &nbsp; chdir = package_name(),\n&nbsp; &nbsp; env = {\"BUILD_PATH\": \".\/build\"},\n&nbsp; &nbsp; out_dirs = &#91;\"build\"],\n)\n\njs_run_devserver(\n&nbsp; &nbsp; name = \"start\",\n&nbsp; &nbsp; args = &#91;\"start\"],\n&nbsp; &nbsp; chdir = package_name(),\n&nbsp; &nbsp; command = \"node_modules\/.bin\/react-scripts\",\n&nbsp; &nbsp; data = CRA_DEPS,\n)<\/code><\/pre>\n\n\n\n<p>This code sets up Bazel to build and run a Create React App (CRA). It loads <strong>js_run_devserver<\/strong> to run a local server, <strong>npm_link_all_packages<\/strong> to link npm packages, and react-scripts commands for CRA.<\/p>\n\n\n\n<p>First, <strong>npm_link_all_packages(<\/strong>) makes npm dependencies available to Bazel. The list <strong>CRA_DEPS<\/strong> includes source files, public files, and essential npm packages for the app. Then, <strong>cra_bin.react_scripts<\/strong> runs <strong>react-scripts<\/strong> build to build the app, with output in the build folder. Finally, <strong>js_run_devserver<\/strong> starts the app in development mode using<strong> react-scripts<\/strong> start, allowing you to test the app locally. This setup keeps build and run steps organized within Bazel.<\/p>\n\n\n\n<p>Before we can build out the application, one last thing to do is prevent npm from &#8220;hoisting&#8221; dependencies to the root <strong>node_modules<\/strong> folder.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><strong>.npmrc<\/strong><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>hoist=false<\/code><\/pre>\n\n\n\n<p>Now, we are all set to build or run the React application. To Build the application, use the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>bazel run <em>\/\/frontend-react:build<\/em><\/code><\/pre>\n\n\n\n<p>To start the development server, use the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>bazel run <em>\/\/frontend-react:start<\/em><\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Creating a Flask Application<\/strong><\/h3>\n\n\n\n<p>Now, just like the frontend, we will need to set up our backend for Bazel as well.<\/p>\n\n\n\n<p>Navigate to the backend folder using the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd ..\/backend-python<\/code><\/pre>\n\n\n\n<p>For starters, we would create a simple flask application to display a message on our development server. To do this, you must first ensure that you have Python version 3 installed on your machine.<\/p>\n\n\n\n<p>After that, install the flask dependency on your machine using the pip:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>python -m pip install flask<\/code><\/pre>\n\n\n\n<p>Once the flask command is installed, we will make a requirement.txt and an <strong>App.py<\/strong> file in the backend-python folder:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><strong>requirement.txt<\/strong><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>Flask==2.0.2<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\"><strong>App.py<\/strong><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>from flask import Flask\n\napp = Flask(__name__)\n\n@app.route('\/')\ndef hello_world():\n&nbsp; &nbsp; return 'Backend with Flask on a Monorepo!'<\/code><\/pre>\n\n\n\n<p>We will run the Flask app to start the development server and check if the backend works.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>python -m flask --app .\/App.py run<\/code><\/pre>\n\n\n\n<p>This command will start your development server at port 5000, and you can access it from http:\/\/localhost:5000\/.<\/p>\n\n\n\n<p>Since we are done with the basic structure of our backend, we can now start configuring it to work with Bazel.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Configuring Build File for Python<\/strong><\/h3>\n\n\n\n<p>Since we have taken care of most of the workspace configuration files above, we just need to create a BUILD.bazel file in the Python project directory, and make some changes to the MODULE.bazel file to accommodate Python.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><strong>BUILD.bazel<\/strong><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>load(\"@python_deps\/\/:requirements.bzl\", \"requirement\")\n\npy_binary(\n&nbsp; name = \"main\",\n&nbsp; srcs = &#91;\"main.py\"],\n&nbsp; deps = &#91;\n&nbsp; &nbsp; requirement(\"Flask\")\n&nbsp; ],\n)<\/code><\/pre>\n\n\n\n<p><span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\">The main program here is defined using the&nbsp;<strong>py_binary<\/strong>&nbsp;function in this Ba<\/span>zel configuration. This program uses main.py as its source file, which contains the main logic for execution. Additionally, it specifies a dependency on the Flask web framework through the requirement function, ensuring that Flask is automatically included during the build process. This setup allows for efficient management of dependencies and organization of the Python project within Bazel.<\/p>\n\n\n\n<p>Now, add the following line to your<strong> MODULE.bazel<\/strong> file.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><strong>MODULE.bazel<\/strong><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>bazel_dep(name = \"python_deps\", version = \"3.9.0\")<\/code><\/pre>\n\n\n\n<p>This adds Python as a dependency that Bazel manages.<\/p>\n\n\n\n<p>To run this, simply use the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>bazel run <em>\/\/backend-python:main<\/em><\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Using Backstage for Managing Monorepos<\/strong><\/h3>\n\n\n\n<p>Backstage provides a streamlined way to organize and manage a monorepo by centralizing all code components, services, and libraries into a catalog. With the <a href=\"https:\/\/www.aviator.co\/blog\/what-is-a-service-catalog-and-why-use-one\/\" title=\"\">catalog service<\/a>, you can use a single catalog for the entire repo or split it into individual projects, depending on team structure and how tightly components are connected. One catalog entry is sufficient for a single-team setup where everything is interdependent. This keeps things simple and centralizes all dependencies and relationships in one place.<\/p>\n\n\n\n<p>For larger setups with multiple teams managing different parts of the monorepo, dividing the catalog helps by allowing each team to control its entry. This approach ensures that each project or service has its catalog-info.yaml file, simplifying updates, dependency tracking, and customization for specific needs without overwhelming a single catalog file.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Developer Portal for Documentation and Navigation<\/h4>\n\n\n\n<p>One of Backstage\u2019s most robust features is its TechDocs integration, which makes documentation accessible directly within the portal. Instead of sifting through separate documentation sources, Backstage docs makes it easy to find detailed, context-specific information while navigating the catalog.<\/p>\n\n\n\n<p>TechDocs allows documentation for each component or service in markdown format. <a href=\"https:\/\/www.aviator.co\/blog\/getting-started-with-an-internal-developer-portal\/\" title=\"The portal\">The portal<\/a> also allows quick access to dependencies and critical project information, creating a more efficient workflow across projects and enabling new team members to get familiar with the repo faster. However, managing internal developer portals can be <a href=\"https:\/\/www.aviator.co\/blog\/why-some-companies-fail-to-adopt-internal-developer-portal\/\" title=\"a tedious task\">a tedious task<\/a>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">CI\/CD Pipeline Management<\/h4>\n\n\n\n<p>Backstage can connect with various CI\/CD tools (like Jenkins and GitHub Actions), providing a single interface to monitor all builds, tests, and deployments across the monorepo. Teams can view ongoing builds, trigger tests, or review deployment logs within Backstage, offering clear visibility into how each project is performing. This visibility is invaluable in a monorepo setup, where multiple services often rely on shared resources and dependencies. It is essential to catch issues early before they cascade through the codebase.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Dependency Management and Change Tracking<\/h4>\n\n\n\n<p>Backstage\u2019s dependency tracking helps teams see relationships and dependencies between different parts of the monorepo. For instance, when a component or library is updated, Backstage provides insights into any other components that might be affected. This helps avoid unexpected issues from code changes, as developers can evaluate the impact of each update.<\/p>\n\n\n\n<p>To set up a monorepo effectively, consider a split catalog if multiple teams own parts of the code. For centralized CI\/CD and simplified deployment visibility, it is recommended to integrate Backstage with your pipelines to get a clear overview of your deployments and services in one place.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What are the Best Practices for Managing Dependencies in a Monorepo<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Organizing Code<\/strong><\/h3>\n\n\n\n<p>Organizing code effectively in a monorepo is essential for smooth collaboration and scalability. A good structure should separate different projects or services into distinct folders, ideally following a logical hierarchy that mirrors the system\u2019s architecture.<\/p>\n\n\n\n<p>Strict linting rules can be enforced to ensure coding standards across the monorepo. Setting up tools like ESLint with custom rules tailored for monorepos helps detect issues early and keeps code consistent. Automated code formatters like Prettier maintain a uniform style throughout the codebase, essential when multiple teams contribute to the same repository.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Efficient Deployments<\/h3>\n\n\n\n<p>Automating deployments is crucial in monorepos, where individual services or components may need to be deployed independently. Tools like <a href=\"https:\/\/www.aviator.co\/releases\" title=\"\">Aviator Releases<\/a> are beneficial for handling these deployments. They allow for coordinated, automated releases of different parts of the monorepo, enabling teams to roll out updates without manually deploying each component. With this setup, you can deploy updates in a way that minimizes downtime and reduces the risk of errors.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Distributed Merge Queue<\/h3>\n\n\n\n<p>Managing code merges in a monorepo can become challenging as multiple teams work on different parts of the codebase. <a href=\"https:\/\/www.aviator.co\/merge-queue\" title=\"\">Aviator MergeQueue<\/a>, combined with selective targeting, helps manage these merges more efficiently. By only merging the parts of the code that are affected by specific changes, Aviator allows distributed merges, minimizing conflicts and reducing waiting time for other teams. This setup keeps code integration smooth and avoids unnecessary bottlenecks.<\/p>\n\n\n\n<p>You can learn more about merge queues <a href=\"https:\/\/www.aviator.co\/blog\/merge-queues-for-large-monorepos\/\">here<\/a>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/www.aviator.co\/merge-queue\" target=\"_blank\" rel=\" noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"1940\" height=\"500\" src=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/aviator-mergequeue-blog-service-page-cta-photo-min.png\" alt=\"\" class=\"wp-image-4848\" srcset=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/aviator-mergequeue-blog-service-page-cta-photo-min.png 1940w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/aviator-mergequeue-blog-service-page-cta-photo-min-300x77.png 300w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/aviator-mergequeue-blog-service-page-cta-photo-min-1024x264.png 1024w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/aviator-mergequeue-blog-service-page-cta-photo-min-768x198.png 768w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/aviator-mergequeue-blog-service-page-cta-photo-min-1536x396.png 1536w\" sizes=\"(max-width: 1940px) 100vw, 1940px\" \/><\/a><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Dependency Management<\/h3>\n\n\n\n<p>Centralizing dependencies in a monorepo is essential for preventing &#8220;dependency drift,&#8221; where different parts of the code use incompatible versions of the same library. Using version constraints and automated update tools aligns dependencies across all projects, ensuring compatibility and stability. Setting up a central dependency file and automating updates ensures that libraries are always up-to-date, saving time and avoiding compatibility issues.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Ensuring Backward Compatibility<\/h3>\n\n\n\n<p>Backward compatibility is essential in a shared codebase, as updates to one service or library could impact other system parts. Use semantic versioning and transparent deprecation practices to avoid breaking changes. Isolated testing of updates is also critical, allowing teams to verify that changes won\u2019t disrupt other services. This practice helps maintain a stable system and minimizes the risk of unexpected issues.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Horizontal Migrations<\/h3>\n\n\n\n<p>Horizontal migrations can be challenging to coordinate across multiple teams in a monorepo. By defining the scope of the migration and using incremental updates, teams can minimize the impact on the system. Automation tests help detect issues early, and fallback strategies are essential for rolling back in case of any problems. This approach keeps migrations manageable and ensures stability.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Codeowners Support for Monorepos<\/h3>\n\n\n\n<p>In monorepos, assigning code ownership helps teams understand who is responsible for different parts of the code. GitHub\u2019s Codeowners file lets you specify ownership using wildcards to match directories or files. This setup streamlines code review processes by automatically routing changes to the appropriate team members.<\/p>\n\n\n\n<p>For more guidance, check out Aviator\u2019s blog, which explains Codeowners and provides a better understanding of how to get started and incorporate Codeowners into your services.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>The monorepo approach offers significant advantages for managing large-scale projects and monorepo microservices architectures. By consolidating all code into a single shared repository, developers can benefit from improved collaboration, consistent dependency management, and streamlined CI\/CD pipelines. While there are challenges to overcome, such as complex setup, performance considerations, and team management, the long-term benefits of a monorepo often outweigh the initial hurdles.<\/p>\n\n\n\n<p>As demonstrated in the hands-on example, tools like Bazel, Lerna, Turborepo, and Nx can greatly facilitate the management and scaling of a monorepo, providing features like intelligent caching, incremental builds, and modular configurations.<\/p>\n\n\n\n<p>By adopting best practices around code organization, deployment automation, and dependency management, teams can effectively leverage the power of a monorepo to build efficient and scalable software systems. Overall, the monorepo model has proven its value in the industry and is a compelling choice for organizations seeking to improve collaboration, consistency, and productivity across their codebase.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/docs.aviator.co\/mergequeue\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"125\" src=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/12\/aviator-mergequeue-blog-banner-photo-min-1-1024x125.png\" alt=\"\" class=\"wp-image-1794\" srcset=\"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/12\/aviator-mergequeue-blog-banner-photo-min-1-1024x125.png 1024w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/12\/aviator-mergequeue-blog-banner-photo-min-1-300x37.png 300w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/12\/aviator-mergequeue-blog-banner-photo-min-1-768x94.png 768w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/12\/aviator-mergequeue-blog-banner-photo-min-1-1536x188.png 1536w, https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2023\/12\/aviator-mergequeue-blog-banner-photo-min-1-2048x250.png 2048w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">FAQ<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Does Google Use a Single Repo?<\/h3>\n\n\n\n<p>Google&#8217;s monolithic repository provides a common source of truth for tens of thousands of developers worldwide. Early Google employees decided to work with a shared codebase managed through a centralized source control system.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Is Monorepo a Good Idea?<\/h3>\n\n\n\n<p>One of the most compelling benefits of a monorepo is its ability to simplify version control. In a traditional multirepo setup, each project or component has its repository, often leading to versioning conflicts and making it difficult to keep track of changes across projects.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What is the Difference Between Monorepo and Monolithic?<\/h3>\n\n\n\n<p>A Monorepo is a version control strategy where multiple projects reside in a single repository but can be independently developed and deployed. A Monolithic architecture, on the other hand, is a tightly integrated application where all components are built and deployed together. Monorepo is about code organization, while Monolithic is about application structure.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Where to Deploy Monorepo?<\/h3>\n\n\n\n<p>A monorepo can be deployed on platforms like GitHub, GitLab, Bitbucket, Google Cloud Source Repositories, or AWS CodeCommit. For efficient builds and CI\/CD, tools like Bazel, Nx, Turborepo, and Jenkins help manage deployments effectively.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Managing large monorepos can be a daunting task. This guide will walk you through the considerations and the steps involved in managing a large repository.<\/p>\n","protected":false},"author":38,"featured_media":3431,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":"[]"},"categories":[78],"tags":[93,29],"class_list":["post-2975","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-monorepo"],"blocksy_meta":[],"acf":[],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/www.aviator.co\/blog\/wp-content\/uploads\/2024\/12\/cover-monorepo.png","post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/posts\/2975","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/users\/38"}],"replies":[{"embeddable":true,"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/comments?post=2975"}],"version-history":[{"count":34,"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/posts\/2975\/revisions"}],"predecessor-version":[{"id":4849,"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/posts\/2975\/revisions\/4849"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/media\/3431"}],"wp:attachment":[{"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/media?parent=2975"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/categories?post=2975"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.aviator.co\/blog\/wp-json\/wp\/v2\/tags?post=2975"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}