惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

雷峰网
雷峰网
MongoDB | Blog
MongoDB | Blog
D
Docker
Martin Fowler
Martin Fowler
人人都是产品经理
人人都是产品经理
GbyAI
GbyAI
Jina AI
Jina AI
酷 壳 – CoolShell
酷 壳 – CoolShell
M
MIT News - Artificial intelligence
腾讯CDC
阮一峰的网络日志
阮一峰的网络日志
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
Netflix TechBlog - Medium
B
Blog RSS Feed
云风的 BLOG
云风的 BLOG
Blog — PlanetScale
Blog — PlanetScale
Vercel News
Vercel News
The Cloudflare Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
有赞技术团队
有赞技术团队
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
I
InfoQ
U
Unit 42

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
How an Unrelated App Broke Our Nx Deployment
Ango Jeffrey · 2026-04-28 · via DEV Community

Introduction

Ever had a build pass perfectly on your local machine only to watch it crumble in the CI/CD pipeline? I recently hit a wall at work while deploying a web app from an Nx monorepo. The culprit was not even the code I was deploying. Instead, it was a configuration mismatch in a completely different mobile app within the same workspace.

Wait, What is Nx and a Monorepo?

Before we dive into the error, let’s talk about the setup. A monorepo is a single repository that houses multiple projects. In my case at work, I have a monorepo setup that houses our web and mobile apps. Nx is a powerful build system designed to manage these monorepos efficiently.

To give you a visual, here is a simplified look at the folder structure:

my-monorepo/
├── apps/
│   ├── web/                # The app we were trying to deploy
│   │   └── project.json
│   └── mobile/             # The project that broke the build
│       ├── eslint.config.js
│       └── project.json
├── package.json            # Root dependencies (ESLint 8 lived here)
├── nx.json                 # Global Nx configuration
└── package-lock.json

Enter fullscreen mode Exit fullscreen mode

One of its primary features is the Project Graph. Nx automatically scans your entire codebase to understand how projects relate to one another. This allows it to perform "affected" commands, only testing or building the parts of the repo that actually changed.

Logically Unrelated, but Toolchain-Coupled
It is easy to think of a mobile app and a web app as "unrelated" because they don't share code. However, in an Nx workspace, they are toolchain-coupled. Because Nx evaluates plugins, configs, and inferred targets across the whole workspace, a configuration error in one project can actually break the deployment for another.

In this post, I’ll break down why the Nx Project Graph failed, how ESLint version skew caused it, and how to harden your CI workflow to prevent it from happening again.

The Problem: The "Ghost" of Other Projects

Even if you're working on one app in the repo and you build that app locally and everything passes fine, Nx must compute that workspace-wide project graph. If any project in your repo has a broken configuration or an incompatible plugin, the entire graph fails to initialize.

Our deployment was failing with a cryptic error:

NX Failed to process project graph

Enter fullscreen mode Exit fullscreen mode

Digging into the verbose logs, we found the real villain:

apps/mobile/eslint.config.js: Package subpath './config' is not defined by "exports" in .../node_modules/eslint/package.json

Enter fullscreen mode Exit fullscreen mode

The Root Cause: ESLint Version Skew

We were running a "split" toolchain across the monorepo:

  • Root/Web: Resolving to ESLint 8.

  • Mobile: Using ESLint 9 with the Flat Config style.

Because Nx uses plugin inference to build the graph, it tried to evaluate the mobile app's ESLint config using the ESLint version resolved in the CI environment. The mobile config used import patterns incompatible with the CI's ESLint version, which caused the graph and the entire deployment to crash.

The issue was compounded by a non-deterministic CI strategy. We were deleting the lockfile in the pipeline and using npm install --legacy-peer-deps, which increased the risk of version drift.

The Fix: Isolation and Hardening

When faced with this toolchain break, there were a few paths we could have taken. However, our choice was driven by the reality of our team structure and technical constraints.

Why we chose Isolation

We ultimately decided to isolate the mobile app from Nx plugin inference. Here is why the other common paths didn't work for us:

  • Why not align toolchains? Upgrading everything to ESLint 9 would have been the "cleanest" path, but our web app (Next.js) had potential version clashes with the new ESLint flat config.
  • Why not separate workspaces? Moving the mobile app to its own repo would offer true isolation, but we chose the monorepo approach specifically to leverage shared resources. A huge portion of our core infrastructure—including our APIs, API wrappers, shared state logic, and utility functions are shared by both web and mobile. Splitting them would have meant duplicating code or managing complex private NPM packages.
  • The Team Factor: In our case, the mobile and web apps are owned by different teams. Forcing a global upgrade would have required cross-team coordination that would have delayed our deployment further.

By isolating the mobile app, we allowed both teams to move at their own pace without breaking the global toolchain.

1. Isolate Plugin Inference

To do this, we told Nx to stop trying to "infer" the mobile app's configuration during graph creation. In nx.json, we excluded the mobile app from the ESLint plugin:

"@nx/eslint/plugin": {
  "exclude": ["apps/mobile/**"]
}

Enter fullscreen mode Exit fullscreen mode

2. Restore Functionality with Explicit Targets

By excluding the project from the plugin, we lost the automatic lint target. We restored this by adding an explicit target in apps/mobile/project.json using run-commands:

"lint": {
  "executor": "nx:run-commands",
  "options": {
    "cwd": "apps/mobile",
    "command": "npm run lint"
  }
}

Enter fullscreen mode Exit fullscreen mode

This keeps the mobile linting as a first-class citizen in Nx without breaking the global graph.

3. Deterministic CI Installs

Stop deleting your lockfile in CI. We switched from npm install to npm ci to ensure the environment exactly matches the local development state.

Key Takeaways for Monorepo Maintainers

Keep Nx Versions Aligned: Ensure nx and all @nx/* packages stay on the same version.

Use npm ci: Determinism is your best friend in a pipeline.

Isolate Boundaries: If you maintain split toolchains, use the exclude property in nx.json to prevent cross-contamination during graph builds.

Debug with Verbosity: When the graph fails, use npx nx report and npx nx build [app name] --verbose to find the hidden error.

Wrap Up
Monorepos offer incredible power but they require a "global" mindset. A failure in a project you are not even touching can block your entire pipeline. By hardening your CI and isolating plugin inference, you can ensure your deployments stay green.

Have you encountered Nx graph issues? Let’s discuss in the comments!