88 monorepo turborepo review
Skill FluxonLab/Skillry/plugins/optional-specialist/skills/88-monorepo-turborepo-review
Use when you need to review monorepo, Turborepo, workspace package, cache, build graph, and dependency boundaries.From its SKILL.md
npx -y skills add FluxonLab/Skillry --skill 88-monorepo-turborepo-reviewAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
12.5 KB, ~2.9k tokens by cl100k_base, as published. Nobody here has run it
Monorepo & Turborepo Review
Purpose
Review the structure, pipeline configuration, caching strategy, task dependency graph, shared package design, versioning, and build correctness of monorepos managed with Turborepo (and associated tools: pnpm/npm/yarn workspaces, Changesets, tsconfig path aliases).
When to use
- Reviewing a
turbo.jsonpipeline configuration and itsdependsOn,inputs,outputsdefinitions. - Auditing workspace package structure: shared UI libraries, utility packages, config packages.
- Evaluating cache hit rate issues: builds that should be cached but are not, or incorrect cache hits serving stale output.
- Reviewing Changesets versioning workflow for a monorepo publishing to npm.
- Checking
tsconfig.jsonpath aliases and composite project references in a monorepo. - Evaluating task ordering and circular dependency issues across packages.
When not to use
- Single-package repository — no workspace or build graph concerns apply.
- Nx-based monorepo — similar concepts but different config schema; note divergences.
- Docker/container build review unless specifically about how Docker interacts with the Turborepo build output.
Procedure
1. Workspace structure
- Root
package.jsonmust defineworkspaces(npm/yarn) orpnpm-workspace.yamlmust be present (pnpm) listing all package globs (e.g.,packages/*,apps/*). - Package naming convention: use a consistent scope prefix (
@myorg/ui,@myorg/config,@myorg/utils) — avoid plain unscoped names for internal packages. - Directory layout: separate
apps/(deployable applications) frompackages/(shared libraries) fromtooling/(shared configs: eslint, tsconfig, prettier). This separation clarifies what is published vs consumed internally. - Each package must have its own
package.jsonwithname,version, andprivate: truefor non-published packages. - Do not commit
node_modules/in any package — a single root install viapnpm install/npm installmust cover all workspaces.
2. turbo.json pipeline
turbo.jsonat the root defines global pipeline; package-levelturbo.json(v2) can extend or override.dependsOn: ["^build"]means: runbuildfor all upstream dependencies first. This is the most critical field — missing it causes stale dependency output to be used.- Task input/output definitions control cache correctness:
inputs: list file globs that, when changed, invalidate the cache. Default is all tracked files in the package — this is safe but conservative. Narrow inputs (e.g.,["src/**", "tsconfig.json"]) improve cache hit rate.outputs: list the files the task produces (e.g.,[".next/**", "dist/**"]). Missing outputs means Turborepo cannot restore them from cache.cache: falseon tasks likedev(watch mode) andtest:watch— these must never be cached.persistent: trueon long-running tasks (dev,start) — tells Turborepo not to wait for them to complete before running dependents.
3. Build dependency graph
- Run
turbo run build --dry=jsonto inspect the task graph. Review: - Is the execution order correct? Shared packages must build before apps that consume them.
- Are there any circular dependencies? Turbo will error on cycles — they must be resolved.
- Are tasks being parallelized where possible? Packages with no dependency on each other should run concurrently.
- Circular dependency detection: if a package A imports from package B and package B imports from package A, the build order is undefined. Use
madge --circularorturbo run build --dry=jsonto identify. - Implicit dependencies: if an app imports a shared package but the app's
package.jsondoes not list it as a dependency, the task graph does not know to build it first. Always declare explicitdependenciesinpackage.json, even for internal workspace packages.
4. Cache correctness
- False cache hit (serving stale output): occurs when
inputsis too narrow and misses a file that actually affects the output. Test by: change a file that should affect the build, then run — if Turborepo reports a cache hit, theinputsdefinition is too narrow. - Cache miss on every run (no hits): occurs when
inputsinadvertently includes files that always change (e.g., generated files,.envfiles, build timestamps written to source). Narrowinputsto only source and config files. - Environment variable handling: variables that affect the build (e.g.,
NODE_ENV,NEXT_PUBLIC_API_URL) must be listed inturbo.jsonunderglobalEnvor task-levelenv. Unlisted env vars are invisible to the cache key — changing them will not invalidate the cache, causing incorrect cached output to be served. turbo.jsonglobalDependencies: list files whose change should invalidate all task caches (e.g., roottsconfig.json, root.eslintrc,pnpm-lock.yaml).
5. Remote cache
- Vercel Remote Cache (or self-hosted): enables CI cache sharing across machines and branches.
- Authenticate with
TURBO_TOKENandTURBO_TEAMenvironment variables in CI. - Remote cache must be enabled in
turbo.json:"remoteCache": { "enabled": true }(Turborepo v2). - Cache signatures: by default the remote cache is unsigned. For security-sensitive builds, enable artifact signing with
--cache-dir+ signature verification to prevent cache poisoning. - Never store secrets in task outputs that are cached — cached artifacts are accessible to anyone with the cache token.
6. Shared packages
- Config packages (
@myorg/tsconfig,@myorg/eslint-config): must export their configs viapackage.jsonexportsfield, not just through file paths. Using direct paths breaks when packages are published. - UI packages (
@myorg/ui): decide on a compilation strategy — source distribution (consumer compiles) vs compiled distribution (pre-built). Source distribution is simpler for internal monorepos; compiled is required for external npm publishing. tsconfig.jsonin each package: shouldextendfrom a base config package (@myorg/tsconfig/base.json). Every package that is referenced by another must have"composite": truein its tsconfig for TypeScript project references to work.package.jsonexportsmap: if a package exports multiple entry points, define them in theexportsfield — do not rely on directdist/path imports which are fragile and break with bundler resolution.- Peer dependencies: shared packages should declare React, TypeScript, etc. as
peerDependencies, notdependencies, to avoid duplicate installations in consuming apps.
7. Versioning and publishing with Changesets
.changeset/directory must exist at the root; each PR that changes a publishable package adds a.mdchangeset file describing the change (patch/minor/major).changeset versionbumps package versions and updates changelogs based on accumulated changesets.changeset publishpublishes to npm — must only run in CI on a release branch afterchangeset versionhas committed the version bumps.private: truepackages must never be published — verify thepublishpipeline excludes allprivatepackages.- Linked packages: if packages share a version (e.g.,
@myorg/uiand@myorg/tokensalways release together), configure them aslinkedin.changeset/config.json. - Pre-release: Changesets supports pre-release mode (
changeset pre enter alpha) — verify it is exited (changeset pre exit) before the final release.
8. CI integration
- Turborepo prune: use
turbo prune --scope=@myorg/app --dockerto generate a minimal lockfile for Docker builds that includes only the transitive dependencies of one app — drastically reduces Docker layer size. --filter: useturbo run build --filter=@myorg/app...to run only the build for one app and its dependencies in CI when only that app changed.--since=origin/main:turbo run build --filter=...[origin/main]runs tasks only for packages changed since the base branch — enables affected-only CI.TURBO_CONCURRENCY: set to the number of available CPU cores in CI to maximize parallelism.- Verify the
turbobinary version is pinned indevDependencies— floatinglatestcauses unexpected behavior changes after Turborepo releases.
Checklist
Workspace:
-
workspacesorpnpm-workspace.yamldefined at root. - Consistent
@scope/naming for all internal packages. -
apps/andpackages/directories separated. - No
node_modules/committed.
turbo.json:
-
dependsOn: ["^build"]on all tasks that consume upstream output. -
inputsdefined and excludes generated/always-changing files. -
outputsdefined for all tasks that produce artifacts. -
envlists all environment variables that affect the build. -
globalDependenciesincludes root config files. -
cache: falseon watch/dev tasks.
Cache:
- Remote cache configured with
TURBO_TOKENandTURBO_TEAMin CI. - No secrets in cached task outputs.
- Cache invalidates correctly when env vars change.
Shared packages:
-
exportsfield defined in each shared package'spackage.json. -
"composite": truein tsconfig for referenced packages. - React/TypeScript in
peerDependencies, notdependencies.
Versioning:
-
.changeset/config.jsonpresent and configured. - CI publishes only after
changeset versioncommits. - All
private: truepackages excluded from publish.
CI:
-
turboversion pinned indevDependencies. -
--filter=[origin/main]used for affected-only runs. -
turbo pruneused for Docker builds.
Common issues & anti-patterns
- Missing
dependsOn: ["^build"]: app builds before its shared package dependency finishes building — uses stale or missing compiled output. The build appears to succeed in CI but deploys broken code. - Unlisted env var in
turbo.jsonenv:NEXT_PUBLIC_API_URLchanges between staging and production but is not inenv. Turborepo serves a cached build configured for staging to production. - Generated files in
inputs: a codegen step writes tosrc/generated/andinputsincludessrc/**. Every codegen run changes these files, busting the cache on every CI run. - Circular workspace dependency:
@myorg/uiimports from@myorg/hooks, and@myorg/hooksimports from@myorg/ui. Turborepo errors; the packages need to be restructured. - Implicit internal dependency: an app uses
import { Button } from '@myorg/ui'but@myorg/uiis not in the app'spackage.json. The task graph does not build@myorg/uifirst — works locally (hoisted) but breaks in CI with strict install mode. private: truepackage accidentally published: ifchangeset publishis run without a proper package filter, an internal config package gets published to npm.- No remote cache in CI: each CI run re-builds everything from scratch. A monorepo with 20 packages takes 15 min; with remote cache sharing, it takes 2 min on the second run.
tsconfigwithoutcomposite: truein referenced package: TypeScript project references fail to build incrementally —tsc -brebuilds the entire dependency chain instead of using cached.tsbuildinfofiles.
Required output
Return a structured report with:
- Summary: pass / needs fixes / blocked (build correctness, cache poisoning risk, publish safety).
- Task graph snapshot: key tasks, their
dependsOn, and whether the execution order is correct. - Cache analysis: inputs/outputs correctness, env var coverage, estimated cache hit rate impact.
- Findings table: severity (critical / high / medium / low / info), category (pipeline / cache / packages / versioning / CI), file + line, description, remediation.
- Shared package assessment: exports map, tsconfig composite, peer dependency status.
- Versioning safety: changeset config, publish guard status.
- Next handoff: run
turbo run build --dry=jsonto validate graph; test cache hit with a known-unchanged package; verify--filter=[origin/main]in CI pipeline.
Safety
- Do not run
changeset publishornpm publishduring review. - Do not modify
pnpm-lock.yamlorpackage-lock.json— lockfile changes affect all packages and must go through the normal PR process. - Do not clear the remote cache — it affects all team members' CI performance.
- If secrets are found in
turbo.jsonglobalEnvvalues (hardcoded, not just variable names) or in cached artifact directories, flag as critical.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.