Monorepos are very popular in the tech industry, and many large companies like Microsoft and Google use them. But what exactly is a monorepo, and would it work for your project?
In this article, we are going to explore what the monorepo architecture is, and learn about some of the commonly used tools for monorepos.
Table of contents
What is a monorepo?
A monorepo is a single version controlled repository that contains multiple independent projects. Some monorepos might contain just 2 or 3 projects while others will contain hundreds of projects using different technologies. This differs from the polyrepo structure where the projects are spread out amongst multiple repositories. It is also important to note that a monorepo is not the same as a monolith. A monolith has multiple sub-projects inside one giant project while monorepos have multiple independent projects inside one repository.
Unified build process and CI/CD process
In a monorepo structure, all of CI/CD(Continuous integration/Continuous delivery) lives in the same repository. This provides teams with more flexibility, and allows you to deploy your services together. You can also choose to deploy your projects separately.
Increased cross team communication
In a polyrepo structure, you will have multiple teams working on different projects spread across many repositories. Often times these teams will not be aware of the status of other repositories outside of their main one. With a monorepo structure, teams are more aware of the changes being made to the projects used in the repo and might spot issues in another projects.
When all of your projects are under a single repository, it makes it easier to establish a consistent code style and set of guidelines across all projects. Some of the guidelines can include naming conventions, best practices for code reviews, branching policies, etc.
In situations where there are breaking changes in the main
branch, a monorepo can help identify where the breaking change came from and that team would be responsible for fixing it. A monorepo can also help teams discuss different versioning strategies for the projects.
Steeper learning curve and issues of inclusivity for newer developers
Inclusivity of all developers on a team is really important for the success and outcome of a software project. When you have many diverse perspectives and levels it will lead to a stronger finished product. But a monorepo structure could be seen as intimidating to novice developers. It might be their first time seeing a list of independent projects within the same repository and they might not know how to best contribute in a meaningful way especially if it is an open source situation. It can also be difficult for newer developers to understand the git history under a monorepo structure. If you are using a tool like git blame
,it will have to sift through a lot of unrelated commits just to generate that information for you. For these reasons, it is important for the team to help newer developers through the monorepo structure and create issues where all levels can meaningfully contribute.
A look into the starter.dev monorepo
Let's take a look at a couple of This Dot Labs open source monorepos and understand why the decision was made to use a monorepo.
Please note: We will not be exploring all of the possible tools used for monorepos. If you are interested in exploring more monorepo tools, then please checkout this curated list.
What is starter.dev?
starter.dev makes it easy to setup a new project with zero configuration involved. Once you install the starter.dev package(npm i @this-dot/create-starter
), you can run the npx @this-dot/create-starter
command and it will provide you with a list of starter kits to choose from. Each starter kit includes testing, linting, code examples and a basic configuration setup.
starter.dev is an open source project that consists of a documentation website repository and a GitHub showcases repository dedicated to all of the different starter kits.
Why did the team choose a monorepo structure?
When it came time to plan out the project, the team had to decide if they wanted to maintain each kit in isolation or create different folders for each kit under the same repository. The reason for using a monorepo structure is to provide the team the ability to build the CLI(command-line interface) with greater ease and allow the team to still build the kits in isolation.
What are Yarn Workspaces?
The documentation website repository uses Yarn Workspaces which allows you to setup multiple packages and use the yarn install
command to install them all at once.
In the root directory, we have starters
and packages
directories. If we look at the starters
directory, we will see all of the starter kits. Each starter kit will have its own package.json
, basic configuration, README and code examples setup.
Inside the root package.json
file, you will notice this "workspaces": [ "packages/*"]
key.
{
"name": "starter.dev",
"version": "0.1.0",
"private": true,
"scripts": {
"lint": "yarn workspace website lint && yarn workspace @this-dot/create-starter lint",
"build:cli": "yarn workspace @this-dot/create-starter build",
"build:website": "yarn workspace website build",
"start:cli": "yarn workspace @this-dot/create-starter start",
"start:website": "yarn workspace website preview",
"dev:website": "yarn workspace website dev",
"watch:cli": "yarn workspace @this-dot/create-starter watch"
},
"workspaces": [
"packages/*"
]
}
That will tell Yarn to import all of the packages that are listed inside of the packages folder.
Unified CI/CD pipeline with Amplify
The starter.dev GitHub showcases repository has a unified CI/CD and uses Amplify. Inside the root directory, there is an Amplify yaml file where each application has their own set of build commands. Each project listed in the monorepo has its own deployment process setup.
version: 1
applications:
- appRoot: next-react-query-tailwind
frontend:
phases:
preBuild:
commands:
- nvm install --lts=gallium
- yarn install
build:
commands:
- yarn run build
artifacts:
baseDirectory: .next
files:
- "**/*"
cache:
paths:
- node_modules/**/*
- appRoot: angular-apollo-tailwind
frontend:
phases:
preBuild:
commands:
- nvm install --lts=gallium
- yarn install
- yarn generate
build:
commands:
- node -v
- yarn run build
artifacts:
baseDirectory: dist/starter-dev-angular
files:
- "**/*"
cache:
paths:
- node_modules/**/*
How does the team manage issues?
If you look at the issues tab, you will notice that all of the developers are following this same naming convention for issues: [starter kit] title for issue
.
This works well for a variety of reasons:
- Any developer interested in contributing to the project, can see at a quick glance which issues fit their skill and interest level.
- It will be easier to track the status of the issues on a project board and see which areas need more attention.
Conclusion
We have explored the monorepo structure and talked about a few features. We have also compared it to other architectures like monoliths and polyrepos to see how if differs.
Then we took an in depth look into starter.dev and how it uses Yarn Workspaces to help organize the project.
I hope your enjoyed this article and best of luck on your programming journey.