Mono Repo; Turbo Repo vs Nx vs Lerna and Why Turbo?

Vivi
6 min readJan 14, 2024

--

As a developer, when adopting a Mono Repo, it’s crucial to ponder over which tool would be appropriately utilized for the product I’m developing. I’ve been through that process myself, and now I want to articulate the reasons behind my choice of Turbo Repo.

The first thing to consider is the definition and utility of a Mono Repo. Why does introducing a Mono Repo contribute to productivity? It’s crucial to contemplate this aspect.

In my case, drawing from my experience at a previous company, more than 10 frontend projects were operating in separate repositories, each in a different domain. In this scenario, let’s consider the need to introduce a new project, requiring the creation of a new repository.

After creating the project, the next step involves installing various frameworks and libraries for collaborative development with colleagues. However, issues can arise from this point onwards. The versions of libraries used in repository A may not align with those in the newly created repository B. Take Next.js as an example — with the release of version 13 last year, if developers in domain A use one version while those in domain B use a different version, it can easily lead to confusion within the team down the line.

Sure, let’s assume that big tools like Next.js can have aligned versions. However, the next hurdle often comes in the form of conventioning issues. What about TypeScript configuration or ESLint settings? Will you copy and paste files, or is there a more streamlined approach?

Up to this point, it might still seem manageable to you. But what if you need to migrate the design system used in project A to project B? Only now might you start to feel a bit of a headache.

So, should we compare and match versions for each repository one by one? Should we resort to using Cmd+C and Cmd+V when bringing in the same code? As you’ve probably sensed by now, it’s quite inefficient. Besides, forking the repository may not be a feasible solution either. This is where we can start thinking differently.

Is it possible to have a single top-level repository and create sub-repositories underneath, utilizing common libraries, lint settings, and other reusable code from the top-level? If that sounds intriguing, this article might be helpful for you.

The concept of Mono Repo, short for Monolithic Repository, originates from the following ideas:

In version-control systems, a monorepo (“mono” meaning ‘single’ and “repo” being short for ‘repository’) is a software-development strategy in which the code for a number of projects is stored in the same repository.

That’s right, it’s literally a development strategy for version control. Let’s consider this scenario: a major update happens to a C library commonly used across 10 repositories. The added features in this update would be incredibly beneficial for our developers. However, going through the process of deleting the library 10 times and installing the latest version can be quite tedious.

Now, in a Mono Repo? A single reinstall applies the update to all 10 repositories! If you’re working in a large-scale development group, adopting a Mono Repo can significantly enhance productivity. Now that we understand that Mono Repo is such a development strategy, how do we go about starting with it?

Similar to other frameworks and libraries, Mono Repo is also installable and usable. Here is a list of commonly used Mono Repo tools:

  • Nx : Nx is a build system with built-in tooling and advanced CI capabilities . It helps you maintain and scale monorepos, both locally and on CI.
  • Turbo Repo : Turbo is an incremental bundler and build system optimized for JavaScript and TypeScript, written in Rust.
  • Lerna : Lerna is a fast, modern build system for managing and publishing multiple JavaScript/TypeScript packages from the same repository.

So, which tool among these three should you choose?

As seen in the graph above, Nx has the highest market share over a considerable period.

In my case, I initially opted for Nx. The reason was trivial — the simple name appealed to me, but I won’t dwell on that. I chose Nx primarily because of its high market share. I thought that if I encountered any difficulties while building a Mono Repo, having the most substantial community support on the internet would be beneficial. Lerna, with the lowest market share and additional setup requirements for build and testing (though I haven’t personally used it), was excluded from my choices.

So, upon further investigation, Nx is known for its high productivity and compatibility with Visual Studio Code. Because there is a plug-in named Nx Console VSCode Plugin. However, considering that the team had no prior experience with Mono Repo and Nx, I found Nx to have a somewhat steep learning curve. So, while I went ahead with the installation for the project, we ultimately decided not to use it.

On the other hand, since my team is already using Vercel as the deployment tool and is highly satisfied with it, the suggestion came up to adopt Turbo Repo, a tool associated with Vercel. My decision-making process for this was as follows.

  • With Turbo Repo, it’s expected to be able to effectively manage or continue using the environment variables that were utilized in each Vercel domain.
  • The learning curve for Turbo Repo is anticipated to be lower compared to Nx.
  • Turbo Repo provides a more familiar interface for team members.
  • Thanks to performance optimizations such as caching and parallel builds, it is expected to be beneficial for frequent and fast deployments in specific internal domains.
  • Turbo Repo is deemed suitable for large-scale projects, facilitating efficient project management.

For these reasons, I have decided to adopt Turbo Repo.

Detailed configurations are kindly provided in the Turbo Repo Docs so I won’t cover the setup in this post.

Alright, now I’d like to share how I’ve been using Turbo. After referring to the official documentation for basic setup, here’s how things unfolded for me:

For my case, I identified the following aspects to pay attention to:

  1. Need to install eslint, previously managed as a package, to all repositories. Additionally, synchronization is needed for libraries requiring configuration.
  2. Ensuring that design components created in Turbo Repo can be used in new domains.
  3. Availability of utility functions and useful custom hooks in Turbo Repo.
  4. However, it’s essential to be able to carry out independent tasks and make changes within specific repositories as well.

Since the fourth point was the most crucial aspect, I decided to adopt git submodules alongside Turbo Repo. With some aspects being mutually manageable, I needed both the flexibility of independent utilization in a multi-repo format and the centralized management of a mono-repo format.

Initially, I added all the necessary components such as the eslint package, libraries, and TypeScript configurations to Turbo Repo. Then, using git submodules, I connected multiple domains to Turbo. This approach allows for maintenance of convention-needed common elements in Turbo Repo, but it’s not entirely dependent. As a result, specific domains can maintain their independence in terms of development, deployment, and testing.

Of course, git submodules are not a mandatory component. I applied them based on the optimal approach for the given situation, as every development element should be adopted depending on its specific context. I’d like to advise readers of this post that introducing both Turbo Repo and git submodules requires careful management. While I wrote this with the intention of assisting those looking to operate independent repositories in a mono-repo format, it should be considered as a reference and adapted as needed.

--

--