# Plane Monorepo Architecture: How pnpm Workspaces and Turbo Power the Codebase

> Explore the Plane monorepo architecture. Discover how pnpm workspaces and Turbo manage dependencies and accelerate builds for efficient codebase development.

- Repository: [Plane/plane](https://github.com/makeplane/plane)
- Tags: architecture
- Published: 2026-06-18

---

**The Plane monorepo uses pnpm workspaces to organize apps and packages into a unified dependency graph, orchestrated by Turbo for cached, parallel task execution across the entire repository.**

The `makeplane/plane` repository manages multiple frontend applications and shared libraries within a single codebase using a monorepo architecture. By combining **pnpm workspaces** for dependency management and **Turbo** for build orchestration, the project ensures consistent tooling and fast incremental builds across all workspaces.

## pnpm Workspace Configuration

The monorepo’s workspace boundaries are defined in **[pnpm-workspace.yaml](https://github.com/makeplane/plane/blob/preview/pnpm-workspace.yaml)** at the repository root. This file declares which directories participate in the workspace while explicitly excluding backend-specific folders.

```yaml
packages:
  - apps/*
  - packages/*
  - "!apps/api"
  - "!apps/proxy"

```

The exclusion of `apps/api` and `apps/proxy` keeps the Django backend services outside the Node.js dependency graph, preventing pnpm from managing Python dependencies. The same file contains a **catalog** section that pins exact versions for external dependencies—such as `react@18.3.1`, `turbo@2.9.18`, and `@atlaskit/pragmatic-drag-and-drop@1.7.4`—ensuring every workspace package uses identical dependency versions.

## Turbo Task Orchestration and Caching

Task execution and caching logic reside in **[turbo.json](https://github.com/makeplane/plane/blob/preview/turbo.json)**. Turbo constructs a directed acyclic graph (DAG) of tasks based on the `dependsOn` field, enabling parallel execution where possible while respecting dependency order.

Key configuration patterns include:

- **`^build` notation** – When a task specifies `dependsOn: ["^build"]`, Turbo guarantees that the `build` task of every direct dependency completes first.
- **Global dependencies** – The `globalEnv` and `globalDependencies` arrays define files and environment variables (like `VITE_API_BASE_URL`) that invalidate cached results when changed.
- **Output caching** – Tasks declare output globs such as `dist/**`, `build/**`, and `.react-router/**`, allowing Turbo to store and reuse build artifacts from previous runs.

```json
{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "inputs": ["$TURBO_DEFAULT$", ".env*"],
      "outputs": ["dist/**", "build/**", ".react-router/**"]
    },
    "dev": {
      "cache": false,
      "dependsOn": ["^build"],
      "persistent": true
    }
  }
}

```

The `dev` task disables caching (`cache: false`) and marks the process as persistent, enabling live reloads while still ensuring dependencies are built beforehand.

## Monorepo Structure: Apps and Packages

Source code divides into two primary groups under the repository root:

| Group | Location | Purpose |
|-------|----------|---------|
| **Apps** | `apps/*` | Standalone frontend applications (Web, Space, Live, Admin) |
| **Packages** | `packages/*` | Reusable libraries consumed by multiple apps |

### Core Packages in `packages/*`

Each package maintains its own [`package.json`](https://github.com/makeplane/plane/blob/main/package.json) and extends the monorepo’s base TypeScript configuration. Key packages include:

- **`packages/ui`** – Shared React components with Storybook integration (`@plane/ui`)
- **`packages/shared-state`** – MobX stores for cross-application state management
- **`packages/i18n`** – Internationalization resources and locale helpers
- **`packages/logger`** – Centralized logging utilities
- **`packages/utils`** – Type guards, date utilities, and helper functions
- **`packages/hooks`** – Custom React hooks for data fetching and theme handling
- **`packages/editor`** – ProseMirror/Tiptap editor integration
- **`packages/constants`** – Shared enums and static values
- **`packages/propel`** – Data-layer abstraction for API communication
- **`packages/decorators`** – TypeScript decorators used across the codebase
- **`packages/codemods`** – Automated code transformation scripts

### Frontend Applications in `apps/*`

Applications are standalone Next.js projects that consume the shared packages:

| App | Path | Technology |
|-----|------|------------|
| **Web** | `apps/web` | Next.js primary user interface |
| **Space** | `apps/space` | Next.js workspace-focused UI |
| **Live** | `apps/live` | Real-time collaboration interface |
| **Admin** | `apps/admin` | Administrative console with Storybook |

The root **[package.json](https://github.com/makeplane/plane/blob/preview/package.json)** delegates commands to Turbo via scripts like `"dev": "turbo run dev --concurrency=18"`, which launches development servers for all apps simultaneously.

## How Dependencies and Tasks Flow

**Dependency Resolution** – When `apps/web` imports from `@plane/ui`, pnpm creates a symlink in `node_modules` pointing to the source folder. This allows TypeScript to resolve the package as if it were published to npm, eliminating the need for separate publish steps during development.

**Task Execution** – Turbo reads each workspace’s [`package.json`](https://github.com/makeplane/plane/blob/main/package.json) scripts, builds a dependency graph based on `dependsOn` declarations, and schedules tasks to maximize parallelism. Running `pnpm dev` from the root triggers Turbo to start all applications in watch mode, rebuilding only packages that changed.

**Caching and Invalidation** – Task outputs are stored in the local `.turbo` folder. Subsequent runs reuse these artifacts unless inputs (source files, environment variables, or `.env*` files) change. The `globalEnv` list ensures that changes to variables like `VITE_API_BASE_URL` correctly invalidate stale caches.

**Lint and Format** – The root `lint-staged` configuration runs `oxfmt` and `oxlint` on staged files, enforcing consistent style across JavaScript and TypeScript code without manual intervention.

## Common Development Workflows

### Running the Entire Monorepo in Development Mode

```bash
pnpm install          # installs all workspace dependencies

pnpm dev              # starts Turbo, launching dev servers for every app

```

Turbo concurrently starts the Next.js development servers for `apps/web`, `apps/space`, `apps/live`, and `apps/admin`.

### Building a Single Package

```bash
pnpm -F @plane/ui build

```

The `-F` (filter) flag targets only the `@plane/ui` workspace. Turbo automatically builds any dependencies of this package first, then executes the build script.

### Adding a New Shared Package

```bash
mkdir packages/my-new-lib
pnpm init -w          # creates a workspace package.json

# edit package.json to set name: "@plane/my-new-lib"

pnpm add -w @plane/shared-state   # add a workspace dependency if needed

```

After creation, the package automatically joins the Turbo task graph because it matches the `packages/*` pattern.

### Running Lint Across the Repository

```bash
pnpm check:lint

```

This executes `oxlint --fix --deny-warnings` on every workspace source file, ensuring consistent code quality.

### Importing Workspace Packages

```tsx
import { Button } from '@plane/ui'

export const MyPage = () => (
  <div>
    <Button>Click me</Button>
  </div>
)

```

Because `@plane/ui` is a workspace package, the import resolves to the local source without requiring a separate npm publish cycle.

## Summary

- **pnpm workspaces** manage dependencies across `apps/*` and `packages/*`, excluding backend folders (`apps/api`, `apps/proxy`) from the Node.js graph.
- **Turbo** orchestrates tasks using `dependsOn` to build dependencies first, caches outputs like `dist/**` and `build/**`, and respects `globalEnv` for cache invalidation.
- The **catalog** in [`pnpm-workspace.yaml`](https://github.com/makeplane/plane/blob/main/pnpm-workspace.yaml) pins exact versions of React, Turbo, and other externals to ensure consistency.
- **Workspace imports** resolve via symlinks, allowing apps to consume shared packages as if they were published modules.
- Root scripts delegate to Turbo with flags like `--concurrency=18`, enabling parallel development across all frontend applications.

## Frequently Asked Questions

### What is the purpose of the catalog section in pnpm-workspace.yaml?

The catalog section pins exact versions for external dependencies—such as `react@18.3.1`, `turbo@2.9.18`, and `@atlaskit/pragmatic-drag-and-drop@1.7.4`—ensuring that every workspace package uses identical versions. This eliminates version drift and "works on my machine" issues across the monorepo.

### Why are apps/api and apps/proxy excluded from the pnpm workspace?

These directories contain Django backend services that do not participate in the Node.js/pnpm ecosystem. Excluding them prevents pnpm from attempting to manage Python dependencies or include backend code in the JavaScript build graph, keeping frontend and backend tooling strictly separated.

### How does Turbo handle caching for development servers?

The `dev` task in [`turbo.json`](https://github.com/makeplane/plane/blob/main/turbo.json) sets `cache: false` and `persistent: true`, which prevents Turbo from caching live server outputs while still requiring dependencies to build first via `dependsOn: ["^build"]`. This ensures hot reloading works correctly without stale cache interference.

### Can I build a single package without building the entire monorepo?

Yes. Using the pnpm filter flag (`-F` or `--filter`), you can target a specific workspace package. For example, `pnpm -F @plane/ui build` triggers Turbo to build only the UI package and its dependencies, skipping unrelated apps and packages to save time.