Mastering Monorepo: Boost Code Reuse and Collaboration in JavaScript Projects

This article explains the monorepo strategy, its advantages and drawbacks, and provides step‑by‑step guidance on setting up a project‑level monorepo using tools like Volta, Yarn workspaces, Lerna, scripty, and commitlint, helping developers streamline code reuse, dependency management, and version synchronization across multiple JavaScript packages.

Taobao Frontend Technology
Taobao Frontend Technology
Taobao Frontend Technology
Mastering Monorepo: Boost Code Reuse and Collaboration in JavaScript Projects

0. Introduction

During recent development I faced a situation where project A depends on an already released project B, but I often need to modify B’s code without publishing it. I needed a way to keep changes in B synchronized with A during development and to handle version upgrades gracefully after A goes live. The best solution I found is the monorepo strategy.

1. What is the monorepo strategy?

Monorepo is a software development strategy that stores the code of multiple projects in a single repository ("mono" meaning single, "repo" short for repository). Major companies such as Google, Facebook, and open‑source projects like Babel use monorepo to manage their code.

Babel uses the monorepo strategy to manage its code.

The article explores the benefits of monorepo for code managers and developers, and how to practice it in daily work.

2. Advantages and disadvantages of monorepo

A typical monorepo directory structure looks like this:

.
├── lerna.json
├── package.json
└── packages/ 
    ├── project_1/
    │   ├── index.js
    │   ├── node_modules/
    │   └── package.json
    ├── project_2/
    │   ├── index.js
    │   ├── node_modules/
    │   └── package.json
    ...

2.1 Advantages

Easy code reuse : With all projects in one repository, shared components or tools can be extracted and referenced via TypeScript, Lerna, or other tools.

Simplified dependency management : Because inter‑project references are internal, it is easy to track which projects are affected by a change and to automate version bumps.

Convenient refactoring : Developers can see the impact range of a change and run unified tests, encouraging continuous optimization.

Promotes open, transparent, and shared culture : Team members are encouraged to view and modify each other’s code, fostering responsibility for maintenance and testing, which improves overall code quality.

2.2 Disadvantages

Complex project‑level permission management : VCS systems often lack satisfactory solutions for fine‑grained permissions within a monorepo.

Higher onboarding cost for new developers : Newcomers must understand the relationships among many sub‑projects, which may require additional documentation effort.

Large‑scale monorepos need specialized VFS and refactoring tools : Companies like Google can afford dedicated infrastructure; smaller teams may find the overhead prohibitive.

2.3 Summary: How to choose?

There is no silver bullet. Successful monorepo adoption depends on team schedule, organizational culture, and individual influence. Even if a full monorepo is impractical, applying the strategy at the project level can still capture most benefits without the heavy cost of a company‑wide monorepo.

3. Monorepo implementation

3.1 Environment lock: Volta

Volta is a JavaScript tool manager that locks Node, npm, and Yarn versions per project. After installing Volta, run volta pin in the project root; the specified versions will be used automatically, even if the global versions differ.

3.2 Reusing packages: workspaces

Using monorepo yields two major benefits:

Avoid duplicate package installations, saving disk space and reducing build time.

Internal code can reference each other like regular npm modules.

These benefits are achieved with Yarn (>=1.0) or npm (>=7.0) workspaces feature.

Three steps are required:

Adjust the directory structure, placing related projects under a packages folder.

In the root package.json, add a workspaces field that points to the folder.

Set private: true in the root package.json to prevent accidental publishing.

After configuration, the directory looks like:

.
├── package.json
└── packages/
    ├── @mono/project_1/ 
    │   ├── index.js
    │   └── package.json
    └── @mono/project_2/
        ├── index.js
        └── package.json

Running npm install or yarn install creates a single node_modules folder at the root, containing both shared and project‑specific packages, allowing cross‑project imports.

3.3 Unified configuration: ESLint, TypeScript, Babel

To avoid repetition, place shared configuration files at the repository root and let each sub‑project extend them.

3.3.1 TypeScript

Create packages/tsconfig.setting.json with common settings, then in each sub‑project’s tsconfig.json use:

{
  "extends": "../tsconfig.setting.json",
  "compilerOptions": {
    "composite": true,
    "outDir": "dist",
    "rootDir": "src"
  },
  "include": ["src"]
}

3.3.2 ESLint

Place a root .eslintrc and let each sub‑project extend it:

{
  "extends": "../../.eslintrc",
  "parserOptions": {
    "project": "tsconfig.json"
  }
}

3.3.3 Babel

Similarly, each sub‑project’s .babelrc can simply extend the root config: { "extends": "../.babelrc" } Resulting project layout:

.
├── package.json
├── .eslintrc
└── packages/
    ├── tsconfig.settings.json
    ├── .babelrc
    ├── @mono/project_1/
    │   ├── index.js
    │   ├── .eslintrc
    │   ├── .babelrc
    │   ├── tsconfig.json
    │   └── package.json
    └── @mono/project_2/
        ├── index.js
        ├── .eslintrc
        ├── .babelrc
        ├── tsconfig.json
        └── package.json

3.4 Unified scripts: scripty

Instead of duplicating scripts in every sub‑project, use scripty to define commands in separate files and reference them from package.json:

{
  ...
  "scripts": {
    "test": "scripty",
    "lint": "scripty",
    "build": "scripty"
  },
  "scripty": {
    "path": "../../scripts/packages"
 },
  ...
}

Scripts can be organized under scripts/packages (project‑level) and scripts/workspaces (global).

3.5 Unified package management: Lerna

Lerna simplifies managing many inter‑dependent packages. After npx lerna init, a lerna.json is created:

{
  "packages": ["packages/*"],
  "version": "0.0.0"
}

Typical adjustments:

{
  "packages": ["packages/*"],
  "npmClient": "yarn",
  "version": "independent",
  "useWorkspaces": true
}

Key Lerna commands: lerna bootstrap – links local packages and installs dependencies. lerna run – executes a script in all packages respecting dependency order. lerna exec – runs any command in each package. lerna publish – publishes changed packages. lerna add – adds a dependency between packages.

Advanced options such as --concurrency, --scope, and --stream provide more control.

3.5.2 Local npm registry: Verdaccio

Install Verdaccio globally ( npm install -g verdaccio) and run verdaccio to start a local npm proxy at http://localhost:4873. Configure .npmrc to point to this registry for publishing with Lerna.

3.6 Commit message formatting

Use commitlint to enforce conventional commit messages (e.g., feat:, fix:, chore:, refactor:, style:). Install with:

npm i -D @commitlint/cli @commitlint/config-conventional @commitlint/config-lerna-scopes commitlint husky lerna-changelog

Configure Husky in package.json:

{
  ...
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  },
  ...
}

Add commitlint.config.js with:

module.exports = {
  extends: [
    "@commitlint/config-conventional",
    "@commitlint/config-lerna-scopes"
  ]
};

This ensures clear, expressive commit logs across all sub‑projects.

4. Migrating from multirepo to monorepo

Use lerna import for local projects (preserving commit history). For remote repositories, use the tomono shell script: create a repos.txt listing repo URLs, names, and target paths, then run cat repos.txt | ~/tomono/tomono.sh.

// 1. Git repo URL 2. Sub‑project name 3. Destination path
[email protected]/backend.git @mono/backend packages/backend
[email protected]/frontend.git @mono/frontend packages/frontend
[email protected]/mobile.git @mono/mobile packages/mobile

5. Conclusion

The article covered what monorepo is, its pros and cons, and detailed best‑practice steps for setting up a project‑level monorepo with Volta, Yarn workspaces, Lerna, scripty, and commitlint. Even if a full monorepo is not feasible now, the tools and ideas presented can improve current workflows.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

MonorepoLernaYARNWorkspacescommitlint
Taobao Frontend Technology
Written by

Taobao Frontend Technology

The frontend landscape is constantly evolving, with rapid innovations across familiar languages. Like us, your understanding of the frontend is continually refreshed. Join us on Taobao, a vibrant, all‑encompassing platform, to uncover limitless potential.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.