lerna vs turbo
Monorepo Management: Lerna vs Turbo
lernaturboSimilar Packages:

Monorepo Management: Lerna vs Turbo

lerna and turbo are both tools designed to manage JavaScript projects with multiple packages (monorepos), but they approach the problem from different angles. lerna is a veteran tool focused primarily on versioning and publishing multiple packages from a single repository. It excels at coordinating release cycles, handling changelogs, and managing inter-package dependencies during development. turbo (part of the Turborepo ecosystem) is a high-performance build system that focuses on speeding up tasks like building, testing, and linting across packages. It uses aggressive caching and parallel execution to minimize redundant work. While lerna traditionally handled task running, modern workflows often pair lerna (for releases) with turbo (for builds), or use turbo's newer release features to handle both concerns.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
lerna036,054608 kB29113 days agoMIT
turbo031,04157.9 kB157 days agoMIT

Lerna vs Turbo: Monorepo Architecture and Performance Compared

Both lerna and turbo solve the complexity of managing multiple packages in a single repository, but they target different layers of the workflow. lerna has historically been the go-to for release management and dependency linking, while turbo revolutionized the space by introducing remote caching and pipeline optimization. Let's break down how they handle common engineering challenges.

🚀 Task Execution: Sequential vs Parallel Pipelines

lerna traditionally runs tasks across packages. In its classic mode, it can run commands in parallel, but it lacks built-in awareness of task dependencies (e.g., knowing that build must finish before test).

# lerna: Run build in all packages
npx lerna run build --parallel

turbo defines a pipeline in a config file, explicitly stating which tasks depend on others. It automatically schedules them to run in parallel where possible and sequentially where required.

// turbo: turbo.json configuration
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"], // Run build in dependencies first
      "outputs": ["dist/**"]
    },
    "test": {
      "dependsOn": ["build"] // Run test only after build
    }
  }
}
# turbo: Execute the defined pipeline
npx turbo run build test

💾 Caching: Local Only vs Remote Cloud Caching

lerna does not have a built-in caching mechanism for task outputs. If you change one file, it typically re-runs the task for that package and potentially its dependents, recalculating everything from scratch every time.

# lerna: No cache flag available for task outputs
# Every run recompiles code even if nothing changed
npx lerna run build

turbo hashes your input files and configuration. If the hash matches a previous run, it restores the output from a cache instead of running the command. This works locally and can be shared across your team via a remote cache.

# turbo: Automatically checks cache before running
# "cache hit" means instant completion without execution
npx turbo run build

# Output: 
# • Packages to build: 12
# • Cached: 10 (instant)
# • Uncached: 2 (running...)

🔗 Dependency Linking: Symlinks vs Workspace Protocols

lerna created the standard for linking local packages together using symlinks (lerna bootstrap). While still supported, modern workflows often rely on native package manager features (like npm workspaces or yarn workspaces) which lerna can integrate with.

# lerna: Explicitly link packages together
npx lerna bootstrap

# Creates symlinks in node_modules pointing to local packages
# lrwxrwxrwx node_modules/pkg-a -> ../packages/pkg-a

turbo assumes you are using a modern package manager (npm, yarn, pnpm, bun) to handle dependency linking via the workspaces field in package.json. It does not manage symlinks itself; it focuses solely on orchestrating tasks on top of that existing structure.

// turbo: Relies on package.json workspaces
{
  "name": "my-monorepo",
  "private": true,
  "workspaces": ["packages/*"]
}
# turbo: No bootstrap command needed
# Uses native 'npm install' or 'pnpm install' to link
npm install

📦 Versioning and Releasing: Core Feature vs Emerging Feature

lerna is purpose-built for versioning. It supports "fixed" mode (all packages share one version) and "independent" mode (each package versions itself). It automates changelog generation, git tagging, and npm publishing in a single flow.

# lerna: Publish new versions
npx lerna publish

# Interactive prompts:
# ? Select a new version (current: 1.0.0) (Use arrow keys)
# ❯ Patch (1.0.1)
#   Minor (1.1.0)
#   Major (2.0.0)

# Automatically updates package.json, commits, tags, and publishes

turbo historically focused only on builds, but recent versions have introduced release capabilities. However, its release tooling is newer and less battle-tested than lerna's decade-long track record. Many teams still use lerna specifically for the publish command while using turbo for everything else.

# turbo: Release functionality (newer)
npx turbo release

# Note: Feature set is evolving; may lack some advanced 
# changelog customization found in lerna

🛠️ Configuration Style: CLI Flags vs Declarative Config

lerna relies heavily on CLI flags and a lerna.json file for global settings. Complex logic often ends up in shell scripts or npm scripts to coordinate specific behaviors.

// lerna: lerna.json
{
  "version": "1.0.0",
  "npmClient": "npm",
  "useWorkspaces": true
}
# lerna: Logic often passed via CLI
npx lerna run test --since main --include-dependents

turbo uses a single, declarative turbo.json file to define the entire build graph. This makes the build behavior explicit, version-controllable, and easier to reason about for new team members.

// turbo: turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "dev": {
      "cache": false,
      "persistent": true
    },
    "lint": {
      "dependsOn": ["^build"]
    }
  }
}

🌐 Real-World Scenarios

Scenario 1: A Suite of React UI Libraries

You maintain 20 components published as separate npm packages. They all share the same version number. When you fix a bug in the core utility, you need to bump the version for all packages and publish them together.

  • Best choice: lerna
  • Why? Its "fixed mode" versioning and automated publishing flow are unmatched for this specific workflow.
# Lerna handles the coordination
npx lerna publish --conventional-commits

Scenario 2: A Large Full-Stack Application

You have a monorepo with a web app, mobile app, shared UI kit, and backend API. Builds take 15 minutes. Developers need instant feedback when changing a shared file.

  • Best choice: turbo
  • Why? Remote caching means if a colleague already built the shared UI kit, your machine downloads the artifact instantly instead of rebuilding it.
// Turbo skips unchanged tasks
// turbo.json ensures only affected apps rebuild
{
  "pipeline": {
    "build": { "outputs": [".next/**", "dist/**"] }
  }
}

Scenario 3: The Hybrid Approach (Industry Standard)

Many mature teams use both. They rely on turbo for daily development (linting, testing, building) to get speed, and keep lerna installed solely for the monthly release process.

// package.json scripts
{
  "scripts": {
    "build": "turbo run build",
    "test": "turbo run test",
    "release": "lerna publish --yes"
  }
}

📊 Summary: Key Differences

Featurelernaturbo
Primary FocusVersioning & PublishingBuild Performance & Caching
Task RunningBasic parallel executionIntelligent pipeline with dependencies
CachingNone (recalculates every time)Local + Remote Cloud Caching
Dependency Linkinglerna bootstrap (legacy) or WorkspacesNative Package Manager Workspaces
Configurationlerna.json + CLI flagsDeclarative turbo.json
Learning CurveModerate (many CLI options)Low (convention over configuration)

💡 The Big Picture

lerna is the seasoned release manager 📦. It knows exactly how to bump versions, write changelogs, and push to npm without breaking things. If your biggest headache is "how do I publish 10 packages at once?", lerna is your answer.

turbo is the performance engineer ⚡. It cares about how fast you can ship code. It eliminates waiting times by remembering what you've already built. If your biggest headache is "why does my CI take 40 minutes?", turbo is your solution.

Final Thought: You don't always have to choose just one. The most robust setup often combines turbo for the daily grind of building and testing, while retaining lerna for the critical, less-frequent task of releasing versions to the world. However, if you are starting a new project today and want a simpler toolchain, turbo is increasingly adding release features that may eventually make lerna optional for many teams.

How to Choose: lerna vs turbo

  • lerna:

    Choose lerna if your primary pain point is coordinating version numbers and publishing multiple packages to npm simultaneously. It is the industry standard for 'fixed mode' versioning where all packages share the same version number, and it offers robust tools for generating changelogs and managing git tags during releases. It is ideal for teams maintaining a suite of libraries that need to be released in lockstep.

  • turbo:

    Choose turbo if your main goal is to speed up local development and CI pipelines by caching build outputs and running tasks in parallel. It is best suited for large monorepos where rebuild times are a bottleneck, as it intelligently skips tasks that haven't changed since the last commit. It is the preferred choice for teams prioritizing developer experience and fast feedback loops over complex release versioning strategies.

README for lerna

Lerna

Lerna is a fast, modern build system for managing and publishing multiple JavaScript/TypeScript packages from the same repository.

NPM Status CI Status

Usage

Check out our docs site here.