Nx Target Optimization
- Status: Accepted
- Deciders: [Team]
- Date: 2025-04-04
Context and Problem Statement
Our monorepo uses Nx to manage build and test processes. We've identified several inefficiencies:
- Duplication of target configurations across project.json files
- Inconsistent output paths and caching behavior
- Lack of standardized build processes
- Need for better DRY principles in configuration
- Lack of automatic backup mechanisms for critical configuration files
Decision Drivers
- Reduce duplication in project configuration
- Improve development experience
- Optimize build caching and performance
- Make project configurations easier to understand and maintain
- Ensure safety when making changes to critical config files
Considered Options
- Continue with per-project configurations
- Move common configurations to targetDefaults in nx.json
- Use Nx plugins more extensively
- Create project generators for standardized configurations
Decision Outcome
Chosen options 2 and 3, with elements of 4. We decided to:
- Optimize targetDefaults in nx.json to properly leverage Nx's DRY features
- Standardize outputs and caching behavior
- Utilize plugins for standardized build, test, and lint targets
- Create automatic backup tools for critical configuration files
- Simplify project.json files to only include project-specific configurations
- Consolidate dependency installation into a single centralized workspace-level target
Positive Consequences
- Reduced duplication across project configurations
- More consistent build and test processes
- Better caching = faster builds
- Simplified project files that are easier to maintain
- Safer configuration updates with built-in backup system
- Optimized task graph with centralized package installation
- Reduced build times by eliminating redundant installation steps
Negative Consequences
- Increased complexity in nx.json
- Learning curve for understanding targetDefaults
- Need to maintain backup mechanism
Implementation Details
Target Defaults
We optimized the nx.json's targetDefaults section to cover common patterns:
-
Common build targets:
-
Standardized build, build:tsc, and build:vite targets
- Consistent output paths and caching behavior
-
Memory allocation for large builds
-
Testing targets:
-
Common test configurations for Vitest and Jest
-
Standardized test file patterns and output paths
-
Formatting and linting:
-
Common format and lint targets using Biome and Oxlint
-
Consistent formatting rules across projects
-
Other utilities:
- deps-check for dependency validation
- typecheck for TypeScript validation
- validate for comprehensive project validation
Named Inputs
We also refined the namedInputs section to properly define input groups for:
- Package files (package.json, etc.)
- Formatting configurations
- Linting configurations
- Testing files and configurations
- TypeScript files and configurations
These input groups are used in target caching to properly invalidate cache when relevant files change.
Backup Mechanism
We implemented a backup script to create snapshots of critical configuration files:
- Automatic backup of nx.json, tsconfig files, and project.json files
- In-memory backup option for critical files
- Easy restoration process
Centralized Package Installation
We consolidated package installation into a single workspace-level target:
-
Single Source of Truth:
-
Package installation happens once at the workspace root level
- Individual projects reference the centralized install target
-
All dependency installation configurations are managed in one place
-
Task Graph Optimization:
-
Projects use
{ "projects": "self", "target": "^install" }
dependency format - This syntax ensures dependency on workspace-level installation
- Eliminates redundant installations that were previously happening per project
-
Significantly reduces the complexity of the task graph
-
Installation Configurations:
- Default: Standard installation with lockfile updates
- CI: Immutable installation for continuous integration
- Clean: Complete node_modules cleanup and fresh installation
- Production: Production-only dependencies installation