Skip to main content

Monorepo Standards

Last reviewed: 2026-04-21
Maintained by: Engineering

This repo uses a pnpm workspace model. The goal of these rules is to keep dependencies, scripts, and shared code predictable.

Adding New Packages

Root devDependencies

Add packages to the root only if the tooling is shared across the whole repo, for example:

  • ESLint
  • TypeScript tooling
  • repo-wide CLI tools

Example:

pnpm add -D eslint

Workspace Dependency

If a dependency is used by only one app, add it only to that workspace:

pnpm --filter @zgrid/api add zod
pnpm --filter @zgrid/manager-desk add clsx

Rule:

  • an apps/api dependency should not go into root if the web app does not use it
  • a Manager Desk dependency from apps/manager-desk should not go into root if the backend does not use it

Shared Packages in packages/*

If something is used by multiple apps, extract it into packages/*, for example:

  • shared types
  • shared validation schema
  • generated API client
  • shared config helpers

Do not copy the same utility or type into multiple apps if it already belongs in shared code.

Rules for pnpm-lock.yaml

  • never edit it manually
  • every dependency change must include the lockfile diff
  • use pnpm add, pnpm add -D, or pnpm --filter ... add
  • do not mix npm install and pnpm add in this repo

If the lockfile looks unexpected:

  • check whether you added the dependency to the correct workspace
  • check whether you accidentally used root instead of --filter

Script Rules

Root Scripts

Root package.json should contain only:

  • common developer entrypoints
  • standard check, migrate, and seed commands
  • the most common team commands

Examples:

  • dev:api
  • dev:manager-desk
  • check:api
  • check:manager-desk
  • db:migrate

Workspace Scripts

Each workspace package.json should contain app-specific scripts.

Examples:

  • dev
  • build
  • check
  • lint

If a script only makes sense in one app, do not promote it to root without a good reason.

Shared Code Rules

Shared code belongs in packages/* when:

  • it is used by more than one app
  • it represents a stable contract
  • it is worth versioning inside the monorepo

Examples of good shared code:

  • DTO/type contract
  • validation schema
  • generated API client

Examples of code that should not be shared too early:

  • a single helper used in one place only
  • a UI-specific formatter used only by Manager Desk
  • a backend-only DB helper that does not make sense outside the API

New Environment Variables

When you add a new environment variable, always update:

Rule:

  • an env change is not complete until all of the above are updated

Dependency Hygiene

Before adding a new package, check:

  • whether the same or a similar package already exists in the repo
  • whether an existing tool can be reused
  • whether the dependency should really be runtime instead of devDependency

Do not add a package if:

  • it solves a trivial problem we can already solve with the existing stack
  • it introduces vendor lock-in without a clear reason
  • it is only needed for an experimental flow without a real product need

Source of Truth

When you change monorepo rules or tooling, keep these aligned: