Back to monorepo orchestration Target affected workspaces Configure turbo pipelines Compare the Nx approach

Fixing Nx 'Affected' Detecting All Projects as Changed

nx affected reports every project as affected on a trivial change, so you lose the speed-up entirely. This page explains why Nx widens the affected set and how to narrow it back to the real change.

Exact symptoms and error messages

A one-line change to a single library marks the whole graph affected:

Exact symptoms and error messages A one-line change to a single library marks the whole graph affected: Exact symptoms and error messages A one-line change to a single library marks the whole graph affected:
Exact symptoms and error messages — the core idea of this section at a glance.
> nx affected:build --base=origin/main

  Note: Nx affected calculated 37 projects to run...
  (expected 2)

Root cause analysis

Nx marks everything affected when a change touches a global input — the root package.json, the lockfile, nx.json, or a shared tsconfig.base.json — because those legitimately influence every project. It also widens the set when the base ref is unreachable (a shallow clone) or when namedInputs are configured so that unrelated files feed a project's hash. The mechanics are the same graph model described in Nx Workspace Architecture.

Root cause analysis Nx marks everything affected when a change touches a global input — the root package.json, the lockfile, nx.json, or a s Root cause analysis Nx marks everything affected when a change touches a global input — the root package.json, the lockfile, nx.json, or a shared tsconfig.base.json — because those
Root cause analysis — the core idea of this section at a glance.

Nx computes affected by hashing a set of named inputs per project and diffing against the base. Anything included in an input that many projects share — a root config, a workspace-wide glob — becomes a shared input, so touching it invalidates every project that lists it. That is correct when the file genuinely affects all projects, and wasteful when an input set is broader than it needs to be. Tuning namedInputs is really about telling Nx precisely which files change a project's output.

The base ref is the other half. Nx diffs your working tree against --base, and if that commit is missing from a shallow CI clone, it cannot compute a diff and conservatively marks everything affected. So an all-projects result on a trivial change is almost always one of two things: an input set that folds in unrelated files, or a base that does not resolve — and the affected graph tells you which.

Resolution and configuration patch

Scope namedInputs so formatting and docs changes do not invalidate builds, and ensure the base is reachable:

Resolution and configuration patch Scope namedInputs so formatting and docs changes do not invalidate builds, and ensure the base is reachable: Resolution and configuration patch Scope namedInputs so formatting and docs changes do not invalidate builds, and ensure the base is reachable:
Resolution and configuration patch — the core idea of this section at a glance.
// nx.json
{
  "namedInputs": {
    "default": ["{projectRoot}/**/*", "sharedGlobals"],
    "production": ["default", "!{projectRoot}/**/*.spec.ts", "!{projectRoot}/**/*.md"],
    "sharedGlobals": ["{workspaceRoot}/tsconfig.base.json"]
  }
}

Separate a lean production input from the default so that test-only and docs changes do not invalidate build and deploy targets:

// nx.json
{
  "targetDefaults": {
    "build": { "inputs": ["production", "^production"] }
  },
  "namedInputs": {
    "default": ["{projectRoot}/**/*", "sharedGlobals"],
    "production": ["default", "!{projectRoot}/**/*.spec.ts", "!{projectRoot}/**/*.md", "!{projectRoot}/**/*.stories.ts"],
    "sharedGlobals": ["{workspaceRoot}/tsconfig.base.json"]
  }
}

Now a change to a spec file re-runs tests but not builds, and sharedGlobals is kept deliberately small so only a genuinely global file invalidates the whole graph.

CLI validation and debug commands

CLI validation and debug commands CLI validation and debug commands in production JavaScript package workflows. CLI validation and debug commands CLI validation and debug commands in production JavaScript package workflows.
CLI validation and debug commands — the core idea of this section at a glance.
# Show exactly why a project is affected
nx affected:graph --base=origin/main
# Ensure the base commit is present
git fetch origin main --depth=100
# List the affected projects only
nx show projects --affected --base=origin/main

Prevention and CI guardrails

  • Fetch enough history that --base resolves to a real commit.
  • Exclude spec, markdown, and config-only files from the production input set.
  • Keep sharedGlobals minimal — every file listed there invalidates the whole graph.
  • Review nx affected:graph when the affected set looks too large.
Prevention and CI guardrails Prevention and CI guardrails in production JavaScript package workflows. Prevention and CI guardrails Prevention and CI guardrails in production JavaScript package workflows.
Prevention and CI guardrails — the core idea of this section at a glance.

Diagnosing affected with the project graph

When the affected set is wrong, guessing is slow; Nx can show you exactly why a project was included. The affected graph renders the change and the edges that propagated it, so you can see whether a project was pulled in by a real dependency or by a shared input.

Affected graph Render the change and the edges that propagated it. affected:graph render change trace the edge dependency or input? fix the cause trim or declare
The affected graph shows whether a project came in via a dependency or a shared input.
# Visualize what the change affected and why
nx affected:graph --base=origin/main

If a project is highlighted with no path back to your change, the culprit is a shared input, not a dependency edge — trim the input set. If it is highlighted through a chain of dependencies you did not expect, you have found real coupling worth knowing about. Either way, the graph turns 'Nx ran too much' from a mystery into a specific, fixable cause.

Keeping sharedGlobals honest

sharedGlobals is the most dangerous input to get wrong, because every file listed there invalidates the entire workspace. It is easy to add a root file 'just in case' and quietly convert every future change to that file into a full rebuild. Treat the list as a deliberate, reviewed set: the lockfile, the base tsconfig, and little else.

sharedGlobals audit Which root files belong in the global input set. File In sharedGlobals? Effect lockfile yes real global input tsconfig.base yes affects all builds docs / env config no needless full run
Only truly global files belong in sharedGlobals; the rest force needless full runs.

Audit it whenever affected starts running more than it should. A .env file, a formatting config, or a docs generator config sitting in sharedGlobals will force a full run on every change to it — usually not what you want. The discipline is to ask, for each entry, 'does changing this genuinely change every project's output?' If the honest answer is no, it does not belong in the global set, and moving it out restores affected's ability to keep changes small.

Frequently Asked Questions

Is it ever correct for Nx to mark everything affected?

Yes — a change to the lockfile, root tsconfig, or nx.json genuinely can change every project's output, so a full run is the safe result. The goal is to stop unrelated files from triggering it.

Why does the base branch matter so much?

Nx diffs your working tree against the base commit. If a shallow clone means that commit is absent, Nx cannot compute a diff and conservatively treats everything as changed.

How do I see why Nx marked a project affected?

Run nx affected:graph --base=<ref>. It renders the change and the edges that propagated it, so you can tell whether a project was pulled in by a real dependency or by a shared input — and fix the right thing.

What belongs in sharedGlobals?

Only files that genuinely change every project's output — typically the lockfile and the base tsconfig. Anything else there forces a full rebuild on every change to it, which is almost never intended.

Why do spec-file changes trigger builds?

Because the build target's inputs include the spec files. Define a lean production input that excludes *.spec.ts, *.md, and stories, and point build/deploy targets at it so only production sources invalidate them.

Related

CI/CD Pipeline Optimization for Monorepos