Glossary

Turborepo

Turborepo coordinates project tasks across packages in a monorepo. It does not install dependencies like a package manager or compile an application like a bundler; it schedules existing scripts, their order, and their caching.

Short definition

Task orchestration based on real relationships between workspace packages.

A monorepo keeps multiple applications and shared packages in one repository. Package manager workspaces define which directories are packages, install their external dependencies, and link internal packages. Turborepo uses this package graph to build a task graph, deciding which scripts can run concurrently and which must wait for a dependency to finish.

Tasks such as build, test, and lint are real scripts in each package’s package.json. The turbo.json file describes their dependencies, outputs, inputs, and caching behaviour. Turborepo is therefore not a package manager, bundler, or test framework; it coordinates tools the project already uses through the Node.js ecosystem.

The cache stores the result of a task for a particular input hash. When the hash matches, Turborepo can restore the logs and declared outputs instead of running the task again. This is an optimisation, not a correctness guarantee: an omitted environment variable, generated input, or incorrect outputs configuration can restore an artefact that does not belong to the current build.

The problem it solves

Changing a shared package should neither rebuild everything nor skip an affected application.

As a monorepo grows, blindly running every build and test becomes wasteful, while choosing affected steps by hand is unreliable. A task graph runs only the necessary work in the correct order.

  • build internal libraries before the applications that consume them
  • run independent tests, lint tasks, or builds in parallel
  • reuse the local cache when repeating the same task on one machine
  • optionally share results between developers and CI through remote caching
  • filter tasks by package, dependencies, dependents, or changes recorded in Git
  • limit work in CI to the relevant part of the repository while preserving real dependencies

Practical example

Two applications and two shared packages

A monorepo contains apps/web, apps/admin, packages/ui, and packages/config. Both applications declare an internal dependency on UI and, where needed, on the shared configuration. Package manager workspaces discover and link the packages; Turborepo builds a package graph from those relationships.

The configuration below says that a package’s build must wait for the builds of its internal dependencies. A change in packages/ui therefore builds that package first, then the affected applications. Web and admin can run in parallel once their shared dependencies have finished. An unchanged result is reused from the cache only when it matches the calculated input hash.

The outputs field must match the real outputs of the tools in use. dist/** might belong to a library and .next/** to a Next.js application; .next/cache/** is excluded from the resulting application artefact. Another bundler or framework needs different paths, and some tasks may produce no cacheable files at all.

turbo.json

{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**", "!.next/cache/**"]
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"]
    }
  }
}

Task graph diagram

Change in packages/ui → build ui → build web and admin.

This simplified flow assumes both applications declare a genuine internal dependency on UI. The task graph combines the package graph with the rules in turbo.json, and caching is decided independently for each node.

  1. Change in packages/ui A UI source file contributes to the task hash. Packages outside the affected part of the graph do not need to run merely because they live in the same repository.
  2. Package graph The package manager’s workspace configuration and internal dependencies show that apps/web and apps/admin use packages/ui. A directory name alone does not establish that relationship.
  3. Build ui The dependsOn: ["^build"] rule schedules the internal dependency’s build before the dependent package’s build. If the cache has no valid result, Turborepo runs the corresponding package script.
  4. Build web and admin Once UI is complete, Turborepo can run the two independent application builds in parallel. Each node has its own state, log, and cache key.
  5. Local or remote cache On a cache hit, execution is skipped and the declared outputs are restored. Remote caching is an optional shared layer; without it, the cache remains local to that machine.

Key concepts

The package graph describes packages; the task graph describes concrete work.

The accuracy of both graphs determines ordering, parallelism, and whether cached results can be trusted.

Apps and packages

apps directories commonly contain deployable applications, while packages holds shared libraries or configuration. This is a useful convention, not the definition of a monorepo or a required Turborepo architecture.

Package graph

The graph comes from the workspace structure and internal dependencies managed by the package manager. Turborepo uses it to understand package relationships but does not install dependencies itself.

Task graph

Each package-and-script combination is a node, and dependsOn adds ordering relationships between nodes. The ^ prefix selects the same task in internal dependencies, while an unprefixed task name can express a dependency within the same package.

Inputs, outputs, and environment

The hash must cover everything that changes the result: source files, configuration, the lockfile, and relevant environment variables. Outputs identify the generated files to restore on a cache hit.

Local and remote caching

The local cache speeds up repeated work on one machine. A remote cache shares artefacts with the team and CI; it can be provided by a service or a compatible HTTP server and requires access controls.

Filters and incremental builds

A filter can limit execution to a package, directory, dependencies, dependents, or a changed part of the repository. Incremental work here means selecting the necessary nodes and reusing valid results, not automatically performing a partial compilation inside every underlying tool.

Benefits, limitations, and common mistakes

Speed improves only when the graph is truthful and tasks declare inputs and side effects accurately.

Benefits

  • the graph allows independent tasks to run safely in parallel
  • caching avoids repeating expensive builds and tests with identical inputs
  • remote caching can share completed work between local development and CI
  • consistent filters help target checks at a changed package and its dependents
  • task-graph configuration is versioned alongside the applications

Limitations and common mistakes

  • an omitted input or environment variable can produce an incorrect cache hit
  • incorrect outputs mean a required artefact will not be restored on a cache hit
  • deployments and writes to external services should not be cached indiscriminately like pure builds
  • inaccurate internal dependencies break both ordering and affected-task selection
  • a remote cache holds build artefacts and logs, so it needs appropriate access and security rules
  • the overhead of configuration and a monorepo may offer little value to a small project

When it makes sense

For JavaScript and TypeScript monorepos with shared code and expensive tasks.

Turborepo makes sense when several applications genuinely share packages, the team needs consistent builds and tests, and parallelism or caching can save meaningful time. It can coordinate libraries and Next.js applications, but Next.js does not require Turborepo, and Turborepo is neither a web framework nor a bundler.

A package manager such as npm, pnpm, or Yarn remains responsible for installation, the lockfile, and workspaces. Turborepo does not require Yarn, and adopting it does not create a well-designed monorepo by itself. Separate repositories may be simpler when projects share neither code, release cadence, nor ownership. A monorepo is not the same as a monolithic application: one repository can hold multiple independently deployed services, while one monolith can consist of a single package.

What to consider

A cache is correct only when its key captures every meaningful input.

Before optimising for speed, verify package boundaries, real dependencies, and the reproducibility of each script.

  • declare internal dependencies in package manifests instead of relying on directory placement
  • include relevant outputs, environment variables, and non-standard inputs in the task configuration
  • do not cache deployments and other externally stateful tasks like ordinary pure builds
  • verify a cache hit in a clean working directory where a missing output cannot be masked by a local file
  • use one chosen package manager and a committed lockfile for reproducible installation
  • restrict access to the remote cache and keep secrets out of outputs and logs
  • measure not only CI runtime but also cache hit rate, failures, and the correctness of produced artefacts

Common questions

Turborepo without common misconceptions

Is Turborepo a package manager?

No. A package manager installs dependencies, manages the lockfile, and defines workspaces. Turborepo schedules tasks, parallel execution, and caching over packages and their scripts.

Does Turborepo require Yarn?

No. It works with supported package manager workspaces, so a project can use npm, pnpm, or Yarn according to its configuration. What matters is consistently using one chosen lockfile and workflow.

Is Turborepo a bundler or part of Next.js?

No. A bundler produces frontend output, and Next.js is a React framework. Turborepo can run and cache their scripts, but does not define components, routing, or the resulting bundle.

Does the cache guarantee a correct build?

No. The cache trusts declared and automatically detected inputs. If the configuration omits a meaningful file or environment variable, the key may be incomplete and the restored result may be wrong.

Is remote caching required?

No. Turborepo can use only a local cache. Remote caching adds sharing between machines and CI, but also requires authentication, rules for sensitive outputs, and a trusted provider.

How I design development systems

Monorepo tooling should reflect real relationships between applications.

When designing projects, I keep the roles of packages, builds, and CI distinct so automation accelerates a verified process without obscuring its inputs.

Request a call

I will call you on the next working day between 9:00 and 17:00.

You can also call me directly.

+420 605 181 728

Leave your phone number and send a callback request.

By sending, you agree to processing your data in order to handle your request.