<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sakthikumaran Navakumar</title>
    <description>The latest articles on DEV Community by Sakthikumaran Navakumar (@sakthicodes22).</description>
    <link>https://dev.to/sakthicodes22</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3765235%2Fe0f10403-ca82-4885-8a49-56f60d5aac0e.jpeg</url>
      <title>DEV Community: Sakthikumaran Navakumar</title>
      <link>https://dev.to/sakthicodes22</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sakthicodes22"/>
    <language>en</language>
    <item>
      <title>Angular Schematics Deep Dive — Part 1: Understanding the Architecture</title>
      <dc:creator>Sakthikumaran Navakumar</dc:creator>
      <pubDate>Mon, 09 Mar 2026 02:53:25 +0000</pubDate>
      <link>https://dev.to/sakthicodes22/angular-schematics-deep-dive-part-1-understanding-the-architecture-46i4</link>
      <guid>https://dev.to/sakthicodes22/angular-schematics-deep-dive-part-1-understanding-the-architecture-46i4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This is Part 1 of a 6-part series on Angular Schematics.&lt;/strong&gt; The complete roadmap is listed at the end of this article. Future parts cover custom generators (&lt;code&gt;ng generate&lt;/code&gt;), installation schematics (&lt;code&gt;ng add&lt;/code&gt;), migration schematics (&lt;code&gt;ng update&lt;/code&gt;), testing with Angular DevKit, and advanced patterns including Nx integration.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Every time you run &lt;code&gt;ng generate component&lt;/code&gt;, &lt;code&gt;ng add @angular/material&lt;/code&gt;, or &lt;code&gt;ng update&lt;/code&gt;, something remarkably sophisticated is happening beneath the surface. Angular's CLI isn't simply copying template files — it is executing a structured, transactional, and fully testable code transformation pipeline. That pipeline is powered by &lt;strong&gt;Angular Schematics&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For many Angular developers, schematics remain a black box — something the framework uses internally, something Nx and Angular Material ship with, something you might glance at and walk away from. That's a missed opportunity. Understanding schematics is the difference between a developer who &lt;em&gt;uses&lt;/em&gt; Angular tooling and one who &lt;em&gt;extends&lt;/em&gt; it.&lt;/p&gt;

&lt;p&gt;In this first article, we pull back the curtain completely. We'll examine the conceptual model, the runtime architecture, the four core abstractions, the virtual file system, and the execution pipeline that every schematic runs through. By the end, you'll have a precise mental model of how schematics work — which is the essential prerequisite for building your own.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What Exactly Is a Schematic?&lt;/li&gt;
&lt;li&gt;How the Angular CLI Uses Schematics&lt;/li&gt;
&lt;li&gt;Architecture Overview — The Four Pillars&lt;/li&gt;
&lt;li&gt;The Schematic Execution Pipeline&lt;/li&gt;
&lt;li&gt;Exploring the Built-in Schematics Collection&lt;/li&gt;
&lt;li&gt;When to Create Custom Schematics&lt;/li&gt;
&lt;li&gt;Summary &amp;amp; What's Next&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What Exactly Is a Schematic?
&lt;/h2&gt;

&lt;p&gt;At its core, a schematic is a &lt;strong&gt;pure function that describes file system transformations&lt;/strong&gt;. It receives a representation of the file system as input and returns a description of how that file system should change — without directly touching disk until all transformations are validated and committed.&lt;/p&gt;

&lt;p&gt;That definition contains three ideas worth unpacking carefully.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. It's a pure function.&lt;/strong&gt; Schematics follow functional programming principles. A schematic doesn't mutate the world directly. It receives state, produces a new description of state, and hands that description off to a runner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. It describes transformations — it doesn't execute them.&lt;/strong&gt; Creating a file in a schematic isn't the same as calling &lt;code&gt;fs.writeFileSync()&lt;/code&gt;. You're building a description of what should happen, giving the framework an opportunity to validate, preview, and batch everything before committing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. It operates on a virtual file system.&lt;/strong&gt; The file system schematics work with is an in-memory representation. This is what makes dry-run mode, transactional rollbacks, and unit testing possible and practical.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;A useful mental model:&lt;/strong&gt; Think of a schematic the way a database thinks of a transaction. You open a transaction (the virtual Tree), describe all your changes (Rules), and then either commit them all atomically or roll back entirely. No partial states, no corrupted workspaces.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  How the Angular CLI Uses Schematics
&lt;/h2&gt;

&lt;p&gt;Angular CLI commands map directly to schematics. When you run any of the following, you are invoking a schematic from a registered collection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Invokes the 'component' schematic from @schematics/angular&lt;/span&gt;
ng generate component my-feature

&lt;span class="c"&gt;# Invokes the 'ng-add' schematic from @angular/material's package.json&lt;/span&gt;
ng add @angular/material

&lt;span class="c"&gt;# Invokes migration schematics listed in the package's migrations.json&lt;/span&gt;
ng update @angular/core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This mapping is not magic — it's configuration. Every Angular library that participates in this ecosystem declares its schematics in &lt;code&gt;package.json&lt;/code&gt; through two fields:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"schematics"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Path to collection.json for ng generate / ng add&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"./schematics/collection.json"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"ng-update"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Points to migrations.json for ng update&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"migrations": "./migrations.json"}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;collection.json&lt;/code&gt; acts as a manifest — a registry of all named schematics the package exposes, the TypeScript factory function for each, and the JSON schema that defines the schematic's options. The CLI reads this manifest, resolves the factory, and hands it to the DevKit runtime.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Overview — The Four Pillars
&lt;/h2&gt;

&lt;p&gt;The Angular DevKit (&lt;code&gt;@angular-devkit/schematics&lt;/code&gt;) is built around four foundational abstractions. Understanding each one precisely is the prerequisite for writing effective schematics.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Tree — The Virtual File System
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Tree&lt;/code&gt; is the representation of the workspace as the schematic sees it. It is an immutable snapshot combined with a &lt;strong&gt;staged layer of mutations&lt;/strong&gt; that haven't been applied yet. When you call &lt;code&gt;tree.create()&lt;/code&gt; or &lt;code&gt;tree.overwrite()&lt;/code&gt;, you are adding entries to that staging layer — not touching disk.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Tree&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular-devkit/schematics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mySchematic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MyOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// READ — returns buffer or null (no I/O hits disk)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;angular.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// WRITE — stages a change, does NOT write to disk&lt;/span&gt;
    &lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/my-file.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;export const x = 42;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// OVERWRITE — also staged, not committed yet&lt;/span&gt;
    &lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;overwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/app/app.module.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updatedContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// return the mutated tree&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Tree also exposes &lt;code&gt;tree.exists()&lt;/code&gt;, &lt;code&gt;tree.delete()&lt;/code&gt;, &lt;code&gt;tree.rename()&lt;/code&gt;, and &lt;code&gt;tree.getDir()&lt;/code&gt; for directory traversal. Everything is synchronous and backed by in-memory buffers unless you are reading a file that only exists on disk and hasn't been modified yet.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Rule — The Unit of Work
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;Rule&lt;/code&gt; is the atomic unit of transformation in schematics. Its type signature is elegantly simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// From @angular-devkit/schematics internals&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Rule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SchematicContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Tree&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Tree&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Rule&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rules are composable. The DevKit provides &lt;code&gt;chain()&lt;/code&gt; to compose multiple rules sequentially, &lt;code&gt;branchAndMerge()&lt;/code&gt; to run rules on a branched copy of the Tree, and &lt;code&gt;mergeWith()&lt;/code&gt; to merge a generated file source into the existing Tree. This composability is what makes schematics architecturally powerful — complex operations are built from simple, independently testable pieces.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mergeWith&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular-devkit/schematics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Rule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nf"&gt;addFilesFromTemplates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;// Rule 1&lt;/span&gt;
    &lt;span class="nf"&gt;updateAngularJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;      &lt;span class="c1"&gt;// Rule 2&lt;/span&gt;
    &lt;span class="nf"&gt;addImportToAppModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;// Rule 3&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Source — Generating a New Tree
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;Source&lt;/code&gt; is a factory that produces an entirely new, empty Tree from some origin — typically a directory of template files. The most commonly used Source is &lt;code&gt;url()&lt;/code&gt;, which reads template files from a local directory. Sources are merged into the main Tree using &lt;code&gt;mergeWith()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;applyTemplates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;move&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mergeWith&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular-devkit/schematics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;strings&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular-devkit/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;templateSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./files&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nf"&gt;applyTemplates&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;dasherize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dasherize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;classify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nf"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;mergeWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;templateSource&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. SchematicContext — Runtime Information
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;SchematicContext&lt;/code&gt; is passed alongside the Tree into every Rule. It provides access to the logger (use this instead of &lt;code&gt;console.log&lt;/code&gt;), task scheduling (for running npm install after file generation), and the engine executing the schematic. It's the schematic's connection to the outside world.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SchematicContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NodePackageInstallTask&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular-devkit/schematics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SchematicContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Installing dependencies...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Schedule npm install to run AFTER all file mutations commit&lt;/span&gt;
  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NodePackageInstallTask&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Schematic Execution Pipeline
&lt;/h2&gt;

&lt;p&gt;With the four pillars understood, let's trace the full lifecycle of a schematic execution from CLI command to committed files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLI Command
───────────
ng generate component dashboard

     │
     ▼
CLI resolves collection
─────────────────────────
Reads package.json → "schematics": "./schematics/collection.json"
Reads collection.json → finds "component" entry → factory path

     │
     ▼
JSON Schema Validation
──────────────────────
Merges CLI flags + prompts against schema.json
Validates required fields, applies defaults
Prompts user for missing required options

     │
     ▼
Factory Function Invoked
─────────────────────────
schematic(options) → returns root Rule

     │
     ▼
Engine Creates Tree
────────────────────
Base layer  = current workspace on disk
Staging layer = empty (no mutations yet)

     │
     ▼
Rule Chain Executes
────────────────────
Rule 1: Generate files from templates → staged
Rule 2: Update angular.json            → staged
Rule 3: Add import to app.module.ts    → staged

     │
     ▼
Post-Rule Tasks Run
────────────────────
NodePackageInstallTask (if scheduled)
RunSchematicTask (chained schematics)

     │
     ▼
Commit (unless --dry-run)
──────────────────────────
Staging layer flushed to real disk
Console output: CREATE / UPDATE / DELETE logs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Exploring the Built-in Schematics Collection
&lt;/h2&gt;

&lt;p&gt;The best way to learn schematic patterns is to read production-grade schematics. The entire &lt;code&gt;@schematics/angular&lt;/code&gt; package is open source and worth studying carefully.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install to inspect locally&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @schematics/angular &lt;span class="nt"&gt;--save-dev&lt;/span&gt;

&lt;span class="c"&gt;# Browse on GitHub:&lt;/span&gt;
&lt;span class="c"&gt;# github.com/angular/angular-cli/tree/main/packages/schematics/angular&lt;/span&gt;

&lt;span class="c"&gt;# List all available schematics in the built-in collection&lt;/span&gt;
ng generate &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside &lt;code&gt;@schematics/angular&lt;/code&gt; you'll find the implementation for every built-in generator: &lt;code&gt;component&lt;/code&gt;, &lt;code&gt;service&lt;/code&gt;, &lt;code&gt;module&lt;/code&gt;, &lt;code&gt;guard&lt;/code&gt;, &lt;code&gt;pipe&lt;/code&gt;, &lt;code&gt;directive&lt;/code&gt;, &lt;code&gt;class&lt;/code&gt;, &lt;code&gt;enum&lt;/code&gt;, &lt;code&gt;interface&lt;/code&gt;, and &lt;code&gt;application&lt;/code&gt;. Each one follows the same pattern: a &lt;code&gt;schema.json&lt;/code&gt; defining options, an &lt;code&gt;index.ts&lt;/code&gt; factory, and a &lt;code&gt;files/&lt;/code&gt; directory of templates.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Schematic&lt;/th&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;Interesting Technique&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;component&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/component/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Conditional template rendering based on &lt;code&gt;--standalone&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;module&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/module/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Routing module generation, lazy-load wiring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;guard&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/guard/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multiple implementations based on &lt;code&gt;--implements&lt;/code&gt; flag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;application&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/application/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Complete workspace scaffolding, cross-schematic calls&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Practical Tip:&lt;/strong&gt; When learning schematics, clone the Angular CLI repo and read the &lt;code&gt;component&lt;/code&gt; schematic in its entirety. It demonstrates conditional template inclusion, AST-based module updates, path normalization, and option defaults — all the patterns you'll use in production schematics.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  When to Create Custom Schematics
&lt;/h2&gt;

&lt;p&gt;Having established the technical foundation, the natural question is: when does writing a custom schematic become the right investment?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repeated scaffolding patterns.&lt;/strong&gt; If your team frequently creates a "feature module" that always includes a component, a service, a store, and a routing module — and that setup consistently takes 15 minutes of copy-pasting — you have a schematic waiting to be written. A single &lt;code&gt;ng generate feature dashboard&lt;/code&gt; should handle it in under a second.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enforcing architectural rules.&lt;/strong&gt; Custom schematics are a powerful mechanism for ensuring generated code follows your team's conventions. Folder structures, naming patterns, required barrel files, mandatory test setups — all of these can be encoded into a schematic rather than documented in a wiki that no one reads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Library installation complexity.&lt;/strong&gt; If your internal UI library requires updating &lt;code&gt;angular.json&lt;/code&gt;, modifying global styles, adding a provider to &lt;code&gt;app.config.ts&lt;/code&gt;, and installing two peer dependencies — an &lt;code&gt;ng add&lt;/code&gt; schematic automates all of that into a single, repeatable, idempotent operation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Framework migrations.&lt;/strong&gt; When you introduce a breaking change in your shared library — a renamed method, a restructured API, a changed import path — an &lt;code&gt;ng update&lt;/code&gt; schematic can apply that migration automatically across every workspace that consumes your library. This is how Angular itself ships breaking changes responsibly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚦 &lt;strong&gt;The Decision Test:&lt;/strong&gt; Ask yourself: &lt;em&gt;"Would I describe this task in an onboarding doc?"&lt;/em&gt; If yes, the task is probably worth automating with a schematic. Documentation describes what humans should do manually. Schematics eliminate the need for the documentation entirely.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Summary &amp;amp; What's Next
&lt;/h2&gt;

&lt;p&gt;We've covered significant ground. Here's what to take forward:&lt;/p&gt;

&lt;p&gt;A schematic is a &lt;strong&gt;pure function that describes file system transformations&lt;/strong&gt; on a virtual, in-memory representation of the workspace. The Angular CLI maps its core commands directly to schematics declared in package manifests. The DevKit runtime is built on four core abstractions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tree&lt;/strong&gt; — virtual file system with a staging layer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule&lt;/strong&gt; — atomic, composable unit of transformation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source&lt;/strong&gt; — template file origin that produces a new Tree&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SchematicContext&lt;/strong&gt; — runtime connection for logging and task scheduling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The virtual file system is the design decision that enables everything valuable about schematics: dry-run previews, atomic rollback, and millisecond-fast unit testing. The execution pipeline runs schema validation, factory invocation, rule chain execution, task scheduling, and a final atomic commit.&lt;/p&gt;

&lt;p&gt;Understanding this architecture deeply is not just academic. It is the precise foundation upon which every practical schematic is built. In &lt;strong&gt;Part 2&lt;/strong&gt;, we take this knowledge directly into the editor and build our first real custom generator — a feature module scaffolder that produces a complete, production-ready module structure in a single command.&lt;/p&gt;




&lt;h2&gt;
  
  
  Series Roadmap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Part 1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Understanding Angular Schematics — Architecture &amp;amp; Core Concepts&lt;/td&gt;
&lt;td&gt;✅ You are here&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Part 2&lt;/td&gt;
&lt;td&gt;Creating Custom Generators with &lt;code&gt;ng generate&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;🔜 Coming Soon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Part 3&lt;/td&gt;
&lt;td&gt;Building Installation Schematics with &lt;code&gt;ng add&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;🔜 Coming Soon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Part 4&lt;/td&gt;
&lt;td&gt;Writing Migration Schematics with &lt;code&gt;ng update&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;🔜 Coming Soon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Part 5&lt;/td&gt;
&lt;td&gt;Testing Schematics with Angular DevKit&lt;/td&gt;
&lt;td&gt;🔜 Coming Soon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Part 6&lt;/td&gt;
&lt;td&gt;Advanced Patterns, Publishing &amp;amp; Monorepo Integration&lt;/td&gt;
&lt;td&gt;🔜 Coming Soon&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Previous Blogs:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/sakthicodes22/the-nx-cheatsheet-commands-for-daily-development-3h4k"&gt;https://dev.to/sakthicodes22/the-nx-cheatsheet-commands-for-daily-development-3h4k&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.to/sakthicodes22/stop-the-spaghetti-enforcing-module-boundaries-in-an-nx-monorepo-2a24"&gt;https://dev.to/sakthicodes22/stop-the-spaghetti-enforcing-module-boundaries-in-an-nx-monorepo-2a24&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Found this useful? Drop a ❤️ and share it with a teammate who would be intersted.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>schematics</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Nx Cheatsheet — Commands for Daily Development</title>
      <dc:creator>Sakthikumaran Navakumar</dc:creator>
      <pubDate>Mon, 02 Mar 2026 15:17:27 +0000</pubDate>
      <link>https://dev.to/sakthicodes22/the-nx-cheatsheet-commands-for-daily-development-3h4k</link>
      <guid>https://dev.to/sakthicodes22/the-nx-cheatsheet-commands-for-daily-development-3h4k</guid>
      <description>&lt;p&gt;If you have just started working with &lt;strong&gt;Nx&lt;/strong&gt; — or joined a team that uses it — the CLI can feel overwhelming at first. There are dozens of commands, flags, and concepts to absorb all at once.&lt;/p&gt;

&lt;p&gt;This cheatsheet cuts through the noise. It covers the &lt;strong&gt;commands you will actually use every day&lt;/strong&gt;, organised by workflow, explained plainly. Bookmark it, come back to it, share it with your team. Complete List of CLI commands can be found &lt;a href="https://nx.dev/docs/reference/nx-commands" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  📋 Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is Nx?&lt;/li&gt;
&lt;li&gt;Creating a Workspace&lt;/li&gt;
&lt;li&gt;Adding Plugins&lt;/li&gt;
&lt;li&gt;Generating Projects and Code&lt;/li&gt;
&lt;li&gt;Running and Serving Applications&lt;/li&gt;
&lt;li&gt;Running Multiple Tasks&lt;/li&gt;
&lt;li&gt;Filtering Projects with Patterns and Tags&lt;/li&gt;
&lt;li&gt;Building Projects&lt;/li&gt;
&lt;li&gt;The Affected Commands&lt;/li&gt;
&lt;li&gt;Skipping and Managing the Cache&lt;/li&gt;
&lt;li&gt;Running Tests&lt;/li&gt;
&lt;li&gt;Linting and Formatting&lt;/li&gt;
&lt;li&gt;Visualising the Dependency Graph&lt;/li&gt;
&lt;li&gt;Workspace and Project Info&lt;/li&gt;
&lt;li&gt;The Nx Daemon&lt;/li&gt;
&lt;li&gt;Quick Reference Cheat Sheet&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What is Nx?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Nx&lt;/strong&gt; is an open-source build system and monorepo toolkit. It lets you manage all your applications and shared libraries in a single repository while giving you intelligent tooling to build, test, and serve them efficiently.&lt;/p&gt;

&lt;p&gt;Three things make Nx stand out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧠 &lt;strong&gt;Smart task runner&lt;/strong&gt; — understands your project dependency graph and only rebuilds what has actually changed&lt;/li&gt;
&lt;li&gt;🏗️ &lt;strong&gt;Code generation&lt;/strong&gt; — scaffolds apps, libraries, and components using best-practice templates&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Visualisation&lt;/strong&gt; — interactive graph to see how all your projects connect&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. Creating a Workspace
&lt;/h2&gt;

&lt;p&gt;Everything starts here. A workspace is the root container for all your applications and libraries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a new workspace interactively&lt;/span&gt;
npx create-nx-workspace@latest

&lt;span class="c"&gt;# Create with a specific framework preset&lt;/span&gt;
&lt;span class="c"&gt;# (react, angular, next, node, ts, empty)&lt;/span&gt;
npx create-nx-workspace@latest &lt;span class="nt"&gt;--preset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;react

&lt;span class="c"&gt;# Create a blank monorepo with a custom name&lt;/span&gt;
npx create-nx-workspace@latest &lt;span class="nt"&gt;--preset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;empty &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myorg

&lt;span class="c"&gt;# Specify your preferred package manager (npm, yarn, pnpm)&lt;/span&gt;
npx create-nx-workspace@latest &lt;span class="nt"&gt;--packageManager&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pnpm

&lt;span class="c"&gt;# Add Nx to an already-existing project or monorepo&lt;/span&gt;
nx init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Adding Plugins
&lt;/h2&gt;

&lt;p&gt;Nx is plugin-driven. Plugins provide the framework-specific generators and executors for React, Angular, Node, and more. The &lt;code&gt;nx add&lt;/code&gt; command installs a plugin &lt;strong&gt;and&lt;/strong&gt; automatically runs its initialisation generator in one step — no manual wiring required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install the React plugin and auto-initialize it&lt;/span&gt;
nx add @nx/react

&lt;span class="c"&gt;# Install the Angular plugin and wire it up automatically&lt;/span&gt;
nx add @nx/angular
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Generating Projects and Code
&lt;/h2&gt;

&lt;p&gt;Instead of manually creating folders and configuration files, let Nx do it for you. The &lt;code&gt;nx generate&lt;/code&gt; (or &lt;code&gt;nx g&lt;/code&gt;) command creates everything following best practices for your chosen framework.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate a new application&lt;/span&gt;
nx g app &amp;lt;n&amp;gt;

&lt;span class="c"&gt;# Generate a new shared library&lt;/span&gt;
nx g lib &amp;lt;n&amp;gt;

&lt;span class="c"&gt;# Generate a component (framework-specific)&lt;/span&gt;
nx g component &amp;lt;n&amp;gt;

&lt;span class="c"&gt;# Preview what will be generated WITHOUT writing any files&lt;/span&gt;
nx g app &amp;lt;n&amp;gt; &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Running and Serving Applications
&lt;/h2&gt;

&lt;p&gt;Nx provides a consistent interface for running your projects regardless of which framework or tooling is underneath. &lt;code&gt;nx serve myapp&lt;/code&gt; is shorthand for &lt;code&gt;nx run myapp:serve&lt;/code&gt; — both do the same thing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start a local development server&lt;/span&gt;
nx serve &amp;lt;project&amp;gt;

&lt;span class="c"&gt;# Run any defined target on a project&lt;/span&gt;
nx run &amp;lt;project&amp;gt;:&amp;lt;target&amp;gt;

&lt;span class="c"&gt;# Run a target with a specific configuration (e.g. production, staging)&lt;/span&gt;
nx run &amp;lt;project&amp;gt;:&amp;lt;target&amp;gt;:&amp;lt;configuration&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Running Multiple Tasks
&lt;/h2&gt;

&lt;p&gt;As your monorepo grows, you need to run operations across multiple projects at once. &lt;code&gt;nx run-many&lt;/code&gt; fans out work across your workspace in parallel. The default parallelism is &lt;code&gt;3&lt;/code&gt; — increase it based on your machine's capacity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run multiple targets across ALL projects in one command&lt;/span&gt;
nx run-many &lt;span class="nt"&gt;-t&lt;/span&gt; build &lt;span class="nb"&gt;test &lt;/span&gt;lint

&lt;span class="c"&gt;# Run multiple targets on specific named projects only&lt;/span&gt;
nx run-many &lt;span class="nt"&gt;-t&lt;/span&gt; build &lt;span class="nb"&gt;test &lt;/span&gt;lint &lt;span class="nt"&gt;-p&lt;/span&gt; app1 app2

&lt;span class="c"&gt;# Run tasks with a concurrency limit&lt;/span&gt;
nx run-many &lt;span class="nt"&gt;-t&lt;/span&gt; build &lt;span class="nt"&gt;--parallel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5

&lt;span class="c"&gt;# Run with the interactive Terminal UI (new in Nx v22)&lt;/span&gt;
nx run-many &lt;span class="nt"&gt;-t&lt;/span&gt; build &lt;span class="nt"&gt;--outputStyle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Filtering Projects with Patterns and Tags
&lt;/h2&gt;

&lt;p&gt;Instead of listing every project name manually, use glob patterns and Nx project tags to target exactly the right set of projects. Tags are labels you assign to projects in their config — for example &lt;code&gt;scope:frontend&lt;/code&gt; or &lt;code&gt;type:ui&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run only projects tagged with scope:frontend&lt;/span&gt;
nx run-many &lt;span class="nt"&gt;-t&lt;/span&gt; build &lt;span class="nt"&gt;--projects&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'tag:scope:frontend'&lt;/span&gt;

&lt;span class="c"&gt;# Run only projects whose names end with -app&lt;/span&gt;
nx run-many &lt;span class="nt"&gt;-t&lt;/span&gt; build &lt;span class="nt"&gt;--projects&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*-app'&lt;/span&gt;

&lt;span class="c"&gt;# Mix explicit names and glob patterns&lt;/span&gt;
nx run-many &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--projects&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'app1,app2,shared-*'&lt;/span&gt;

&lt;span class="c"&gt;# Run on all projects, EXCLUDING e2e projects&lt;/span&gt;
nx run-many &lt;span class="nt"&gt;-t&lt;/span&gt; lint &lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*-e2e'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. Building Projects
&lt;/h2&gt;

&lt;p&gt;Building compiles your source code and produces deployment-ready artifacts. The command is the same regardless of whether you use Webpack, Vite, or esbuild underneath.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build a specific project&lt;/span&gt;
nx build &amp;lt;project&amp;gt;

&lt;span class="c"&gt;# Build using the production configuration (optimised, minified)&lt;/span&gt;
nx build &amp;lt;project&amp;gt; &lt;span class="nt"&gt;--configuration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production

&lt;span class="c"&gt;# Build all projects in the workspace&lt;/span&gt;
nx run-many &lt;span class="nt"&gt;-t&lt;/span&gt; build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  8. The Affected Commands — Nx's Superpower
&lt;/h2&gt;

&lt;p&gt;This is the feature that makes Nx genuinely transformative. Instead of rebuilding and retesting everything on every commit, Nx analyses your dependency graph and figures out &lt;strong&gt;exactly which projects are affected by your changes&lt;/strong&gt; — and only runs tasks for those.&lt;/p&gt;

&lt;p&gt;If you change a utility library, Nx identifies every app and library that depends on it and runs the target for all of them. Everything else is skipped. On a large monorepo, this can reduce CI time significantly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build only projects affected by recent changes&lt;/span&gt;
nx affected &lt;span class="nt"&gt;-t&lt;/span&gt; build

&lt;span class="c"&gt;# Test only affected projects&lt;/span&gt;
nx affected &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# Lint only affected projects&lt;/span&gt;
nx affected &lt;span class="nt"&gt;-t&lt;/span&gt; lint

&lt;span class="c"&gt;# Run multiple targets on affected projects in one command&lt;/span&gt;
nx affected &lt;span class="nt"&gt;-t&lt;/span&gt; build &lt;span class="nb"&gt;test &lt;/span&gt;lint

&lt;span class="c"&gt;# Determine affected projects relative to a specific branch/commit range&lt;/span&gt;
nx affected &lt;span class="nt"&gt;--base&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;main &lt;span class="nt"&gt;--head&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;HEAD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  9. Skipping and Managing the Cache
&lt;/h2&gt;

&lt;p&gt;Nx caches the results of every task. If nothing has changed since the last run, Nx replays the cached result in milliseconds. There are times, however, when you need to bypass the cache — debugging a flaky test, verifying a fix actually works, or clearing stale output.&lt;/p&gt;

&lt;p&gt;Refer here for &lt;a href="https://nx.dev/docs/concepts/how-caching-works" rel="noopener noreferrer"&gt;How caching works&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run a build bypassing the local cache entirely&lt;/span&gt;
nx build &amp;lt;project&amp;gt; &lt;span class="nt"&gt;--skipNxCache&lt;/span&gt;

&lt;span class="c"&gt;# Skip cache for all projects in run-many&lt;/span&gt;
nx run-many &lt;span class="nt"&gt;-t&lt;/span&gt; build &lt;span class="nt"&gt;--skipNxCache&lt;/span&gt;

&lt;span class="c"&gt;# Skip cache on all affected test runs&lt;/span&gt;
nx affected &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--skipNxCache&lt;/span&gt;

&lt;span class="c"&gt;# Clear the entire local cache AND daemon state&lt;/span&gt;
nx reset

&lt;span class="c"&gt;# Clear ONLY the task cache, leave the daemon running&lt;/span&gt;
nx reset &lt;span class="nt"&gt;--only-cache&lt;/span&gt;

&lt;span class="c"&gt;# Restart ONLY the daemon, leave the cache intact&lt;/span&gt;
nx reset &lt;span class="nt"&gt;--only-daemon&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  10. Running Tests
&lt;/h2&gt;

&lt;p&gt;Whether your projects use Jest, Vitest, Playwright, or Cypress, &lt;code&gt;nx test&lt;/code&gt; and &lt;code&gt;nx e2e&lt;/code&gt; provide a consistent interface. Combine with &lt;code&gt;affected&lt;/code&gt; and &lt;code&gt;run-many&lt;/code&gt; for full-scale coverage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run unit tests for a specific project&lt;/span&gt;
nx &lt;span class="nb"&gt;test&lt;/span&gt; &amp;lt;project&amp;gt;

&lt;span class="c"&gt;# Run end-to-end tests for a project&lt;/span&gt;
nx e2e &amp;lt;project&amp;gt;

&lt;span class="c"&gt;# Run unit tests only for affected projects&lt;/span&gt;
nx affected &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# Run unit tests across ALL projects&lt;/span&gt;
nx run-many &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  11. Linting and Formatting
&lt;/h2&gt;

&lt;p&gt;Keeping code quality consistent across a monorepo is hard without tooling. Nx gives every project its own lint target and provides workspace-level format commands powered by Prettier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Lint a specific project&lt;/span&gt;
nx lint &amp;lt;project&amp;gt;

&lt;span class="c"&gt;# Lint only the projects affected by recent changes&lt;/span&gt;
nx affected &lt;span class="nt"&gt;-t&lt;/span&gt; lint

&lt;span class="c"&gt;# Auto-format ALL files in the workspace using Prettier&lt;/span&gt;
nx format:write

&lt;span class="c"&gt;# Check formatting across the workspace WITHOUT writing (perfect for CI)&lt;/span&gt;
nx format:check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  12. Visualising the Dependency Graph
&lt;/h2&gt;

&lt;p&gt;At any point you can visualise the entire architecture of your monorepo as an interactive browser-based graph. This is invaluable for onboarding new team members, auditing architecture, and understanding the blast radius of a change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Open the interactive dependency graph in the browser&lt;/span&gt;
nx graph

&lt;span class="c"&gt;# Show only the subgraph of a specific project and its dependencies&lt;/span&gt;
nx graph &lt;span class="nt"&gt;--focus&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;project&amp;gt;

&lt;span class="c"&gt;# Print the project graph as JSON to the terminal&lt;/span&gt;
nx graph &lt;span class="nt"&gt;--print&lt;/span&gt;

&lt;span class="c"&gt;# Export the full graph to a JSON file&lt;/span&gt;
nx graph &lt;span class="nt"&gt;--file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;output.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  13. Workspace and Project Info
&lt;/h2&gt;

&lt;p&gt;As your workspace grows, you need reliable ways to explore it. The &lt;code&gt;nx show&lt;/code&gt;, &lt;code&gt;nx list&lt;/code&gt;, and &lt;code&gt;nx report&lt;/code&gt; commands answer the everyday questions: what exists, what is affected, what plugins are installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List ALL projects in the workspace&lt;/span&gt;
nx show projects

&lt;span class="c"&gt;# List only the projects affected by recent changes&lt;/span&gt;
&lt;span class="c"&gt;# (modern replacement for the deprecated nx affected:apps/libs)&lt;/span&gt;
nx show projects &lt;span class="nt"&gt;--affected&lt;/span&gt;

&lt;span class="c"&gt;# List all projects that have a specific target defined&lt;/span&gt;
nx show projects &lt;span class="nt"&gt;--with-target&lt;/span&gt; serve

&lt;span class="c"&gt;# Show full configuration details of a specific project&lt;/span&gt;
nx show project &amp;lt;n&amp;gt;

&lt;span class="c"&gt;# Open a rich browser view of a project's fully resolved configuration&lt;/span&gt;
nx show project &amp;lt;n&amp;gt; &lt;span class="nt"&gt;--web&lt;/span&gt;

&lt;span class="c"&gt;# List all installed Nx plugins in the workspace&lt;/span&gt;
nx list

&lt;span class="c"&gt;# List all generators and executors provided by a specific plugin&lt;/span&gt;
nx list &amp;lt;plugin&amp;gt;

&lt;span class="c"&gt;# Print all installed Nx and plugin versions (essential for debugging)&lt;/span&gt;
nx report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  14. The Nx Daemon
&lt;/h2&gt;

&lt;p&gt;The Nx Daemon is a background process that keeps your workspace's project graph in memory. Without it, every command has to re-read and re-parse your entire workspace from scratch — noticeable latency in large monorepos. In most cases it starts and stops automatically. These commands give you manual control when needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Manually start the Nx daemon&lt;/span&gt;
nx daemon &lt;span class="nt"&gt;--start&lt;/span&gt;

&lt;span class="c"&gt;# Stop the Nx daemon&lt;/span&gt;
nx daemon &lt;span class="nt"&gt;--stop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚡ Quick Reference Cheat Sheet
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🏗️ Workspace Setup
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npx create-nx-workspace@latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a new workspace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx init&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add Nx to an existing project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx add @nx/react&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add and initialize a plugin&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🔧 Generate Code
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx g app &amp;lt;n&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New application&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx g lib &amp;lt;n&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New shared library&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx g component &amp;lt;n&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New component&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx g app &amp;lt;n&amp;gt; --dry-run&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preview generation without writing files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ▶️ Run Tasks
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx serve &amp;lt;project&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start dev server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx build &amp;lt;project&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build a project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx test &amp;lt;project&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run unit tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx e2e &amp;lt;project&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run e2e tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx lint &amp;lt;project&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lint a project&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🚀 Scale Across Projects
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx run-many -t build test lint&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run multiple targets across all projects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx run-many -t build --parallel=5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run with concurrency control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx run-many -t build --projects='*-app'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Target projects by glob pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx run-many -t build --projects='tag:scope:frontend'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Target projects by tag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx run-many -t lint --exclude='*-e2e'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exclude projects by pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx affected -t build test lint&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only run what is affected by your changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx affected --base=main --head=HEAD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Affected in a PR commit range&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🗄️ Cache Management
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx build &amp;lt;project&amp;gt; --skipNxCache&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bypass cache for one task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx reset --only-cache&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clear task cache only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx reset --only-daemon&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restart daemon only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx reset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full reset — cache + daemon&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🔍 Explore &amp;amp; Debug
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx graph&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open interactive dependency graph&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx graph --focus=&amp;lt;project&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Focus graph on one project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx show projects --affected&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List affected projects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx show project &amp;lt;n&amp;gt; --web&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inspect full project config in browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx report&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check all installed plugin versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx format:check&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check formatting (great for CI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nx format:write&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Auto-format the entire workspace&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Useful Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📖 &lt;a href="https://nx.dev/docs" rel="noopener noreferrer"&gt;Official Nx Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔧 &lt;a href="https://nx.dev/docs/reference/nx-commands" rel="noopener noreferrer"&gt;Nx CLI Reference (v22)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Found this useful? Drop a ❤️ and share it with a teammate who is just getting started with Nx.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nx</category>
      <category>monorepo</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Stop the Spaghetti: Enforcing Module Boundaries in an Nx Monorepo</title>
      <dc:creator>Sakthikumaran Navakumar</dc:creator>
      <pubDate>Sat, 28 Feb 2026 14:44:59 +0000</pubDate>
      <link>https://dev.to/sakthicodes22/stop-the-spaghetti-enforcing-module-boundaries-in-an-nx-monorepo-2a24</link>
      <guid>https://dev.to/sakthicodes22/stop-the-spaghetti-enforcing-module-boundaries-in-an-nx-monorepo-2a24</guid>
      <description>&lt;h2&gt;
  
  
  Enforcing Module Boundaries in an Nx Monorepo
&lt;/h2&gt;

&lt;p&gt;It starts with good intentions. Your team decides to adopt a monorepo. You scaffold your workspace, create a handful of libraries, and for the first few sprints everything feels clean and purposeful. Then the deadlines hit.&lt;/p&gt;

&lt;p&gt;A developer needs a formatting utility from the payments module — so they reach in and import it directly. Another engineer needs a component from the loans feature for a quick prototype in the accounts section. Someone else, pressed for time, imports a service three layers deep from another domain's internals. Nobody reviews it too carefully. The CI pipeline is green. Ship it.&lt;/p&gt;

&lt;p&gt;Six months later, your dependency graph looks like a bowl of spaghetti. The payments domain knows about accounts. The accounts feature imports from loans. Shared utilities carry domain-specific logic. Nobody can confidently change anything without triggering a cascade of broken imports across the workspace. Your architecture diagram, proudly mounted on the team's Confluence page, bears no resemblance to what the codebase actually does.&lt;br&gt;
This is not a discipline problem. It is a tooling problem. And Nx solves it elegantly.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Module Boundaries Matter: The Architectural Case
&lt;/h2&gt;

&lt;p&gt;Before we look at the tooling, it is worth understanding why boundary enforcement is not just a nice-to-have but an architectural necessity in any sufficiently large codebase.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implicit Coupling Is the Silent Killer
&lt;/h2&gt;

&lt;p&gt;In a monorepo without enforced boundaries, any library can import from any other library. Technically, there is nothing stopping a UI component from reaching directly into a data-access service, or a feature module from importing internal implementation details from a completely unrelated domain. These imports create implicit coupling — undocumented, invisible dependencies that accumulate quietly until refactoring becomes genuinely dangerous.&lt;/p&gt;
&lt;h2&gt;
  
  
  Social Enforcement Does Not Scale
&lt;/h2&gt;

&lt;p&gt;With a team of three engineers and ten libraries, you can maintain architectural discipline through code review and shared understanding. With a team of fifteen engineers, forty libraries, and three concurrent feature tracks, you cannot. The cognitive overhead of manually auditing import paths in code review is enormous, the feedback loop is slow, and violations slip through. You need the tooling to carry the architectural intent forward, independent of team size, experience level, or deadline pressure.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Architecture Diagram Should Match the Import Graph
&lt;/h2&gt;

&lt;p&gt;This is the principle that underpins everything. If your architecture diagram shows that the payments domain is isolated from the loans domain, then your import graph should reflect exactly that. Nx module boundary enforcement is the mechanism that keeps these two things in sync — automatically, continuously, and without relying on human vigilance.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Nx Mental Model: Tags and Dependency Constraints
&lt;/h2&gt;

&lt;p&gt;Nx enforces boundaries through a tag-based system. Each library in your workspace is assigned one or more tags via its project.json file, and your ESLint configuration defines rules that govern which tags are allowed to depend on which other tags.&lt;/p&gt;

&lt;p&gt;Tags typically carry two dimensions of information: scope and type.&lt;/p&gt;

&lt;p&gt;Scope answers the question: which domain or vertical does this library belong to? In a banking application, you might have scopes like scope:payments, scope:loans, scope:accounts, scope:kyc (Know Your Customer), and scope:shared.&lt;/p&gt;

&lt;p&gt;Type answers the question: what architectural layer or role does this library play? Rather than the generic feature/ui/data-access/util convention, a more expressive and flexible approach uses tags like type:app, type:lib, type:shared, and type:e2e. This maps more naturally to how libraries actually behave in large-scale production workspaces and gives you coarser, more durable rules.&lt;/p&gt;

&lt;p&gt;Here is how tags are applied in a library's project.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//apps/banking-portal-e&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;e/project.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"banking-portal-e2e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"projectType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"scope:banking-portal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"type:e2e"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once your libraries are tagged, the ESLint rule &lt;code&gt;@nx/enforce-module-boundaries&lt;/code&gt; becomes the enforcement engine.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Sample Project Structure
&lt;/h2&gt;

&lt;p&gt;Before diving into the ESLint configuration, here is the workspace structure we will be working with throughout this article. This represents a simplified but realistic banking platform monorepo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;banking-workspace/
├── apps/
│   ├── banking-portal/       # Customer-facing web app
│   │   └── project.json      # tags: scope:banking-portal, type:app
│   ├── banking-portal-e2e/   # E2E tests for banking-portal
│   │   └── project.json      # tags: scope:banking-portal, type:e2e
│   ├── admin-dashboard/      # Internal ops/admin app
│       └── project.json      # tags: scope:admin, type:app  
├── libs/
│   ├── payments/
│   │   ├── feature-transfer/ # tags: scope:payments, type:lib
│   │   ├── feature-transaction-history/ # tags: scope:payments, type:lib
│   │   └── data-access/      # tags: scope:payments, type:lib
│   ├── loans/
│   │   ├── feature-apply/    # tags: scope:loans, type:lib
│   │   ├── feature-repayment/# tags: scope:loans, type:lib
│   │   └── data-access/      # tags: scope:loans, type:lib
│   ├── accounts/
│   │   ├── feature-dashboard/# tags: scope:accounts, type:lib
│   │   ├── feature-settings/ # tags: scope:accounts, type:lib
│   │   └── data-access/      # tags: scope:accounts, type:lib
│   ├── kyc/
│   │   ├── feature-onboarding/# tags: scope:kyc, type:lib
│   │   └── data-access/       # tags: scope:kyc, type:lib
│   └── shared/
│       ├── ui-design-system/  # tags: scope:shared, type:shared
│       ├── ui-forms/          # tags: scope:shared, type:shared
│       ├── util-formatters/   # tags: scope:shared, type:shared
│       ├── util-validators/   # tags: scope:shared, type:shared
│       └── data-access-http/  # tags: scope:shared, type:shared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every domain (payments, loans, accounts, kyc) is a self-contained vertical. The shared scope holds anything that is genuinely cross-cutting. Apps consume libs. Libs do not reach into other domain's libs unless explicitly permitted. E2E projects only test — they never become a source of shared logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enforcing Boundaries with the Nx ESLint Plugin
&lt;/h2&gt;

&lt;p&gt;With the project structure established, let us look at the ESLint configuration that encodes these architectural rules. All of this lives in your root .eslintrc.json.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"@nx"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"overrides"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"*.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*.tsx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*.jsx"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"@nx/enforce-module-boundaries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"enforceBuildableLibDependency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"allowCircularSelfDependency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"banTransitiveDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"depConstraints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;

              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"sourceTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"type:app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"onlyDependOnLibsWithTags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"type:lib"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"type:shared"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;

              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"sourceTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"type:lib"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"onlyDependOnLibsWithTags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"type:lib"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"type:shared"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;

              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"sourceTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"type:shared"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"onlyDependOnLibsWithTags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"type:shared"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;

              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"sourceTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"type:e2e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"onlyDependOnLibsWithTags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"type:shared"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;

              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"sourceTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"scope:payments"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"onlyDependOnLibsWithTags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"scope:payments"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"scope:shared"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"sourceTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"scope:loans"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"onlyDependOnLibsWithTags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"scope:loans"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"scope:shared"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"sourceTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"scope:accounts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"onlyDependOnLibsWithTags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"scope:accounts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"scope:shared"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"sourceTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"scope:kyc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"onlyDependOnLibsWithTags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"scope:kyc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"scope:shared"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"sourceTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"scope:shared"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"onlyDependOnLibsWithTags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"scope:shared"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  This configuration encodes six architectural decisions simultaneously:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Apps can consume domain libs and shared libs — but never other apps&lt;/li&gt;
&lt;li&gt;Domain libs can consume other libs within their own scope and anything in shared — but never reach across domain boundaries&lt;/li&gt;
&lt;li&gt;Shared libs are the foundation layer — they are self-contained and import nothing outside their own scope&lt;/li&gt;
&lt;li&gt;E2E projects can reference shared utilities if needed, but are otherwise isolated from application and domain logic&lt;/li&gt;
&lt;li&gt;banTransitiveDependencies ensures that if lib A depends on lib B which depends on lib C, lib A cannot import from lib C directly — it must go through lib B's public API&lt;/li&gt;
&lt;li&gt;enforceBuildableLibDependencyCheck ensures that if you enable buildable libs, your dependency declarations stay honest&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Advanced Tag Expressions: !, *, and Combining Constraints
&lt;/h2&gt;

&lt;p&gt;One of the most underused and underappreciated features of the @nx/enforce-module-boundaries rule is its support for tag expression operators. Beyond simple string matching, the rule supports negation, wildcards, and compound logic that lets you write precise, expressive constraints.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Wildcard Operator: *
&lt;/h2&gt;

&lt;p&gt;The wildcard * matches any tag. This is useful when you want to say "this library can import from literally anything" — which you should use sparingly, but it has legitimate use cases for certain shell or orchestration libraries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sourceTag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"type:app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"onlyDependOnLibsWithTags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More practically, wildcards shine when used within a tag expression for partial matching. For example, if you want to allow a shared utility to depend on any shared library regardless of a sub-classification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;json
{
  "sourceTag": "scope:shared",
  "onlyDependOnLibsWithTags": ["scope:shared", "*:shared"]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use ! directly within the &lt;em&gt;onlyDependOnLibsWithTags&lt;/em&gt; array to say "must have this tag and must not have that tag simultaneously":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;json{
  "sourceTag": "scope:accounts",
  "onlyDependOnLibsWithTags": ["scope:accounts", "scope:shared", "!type:e2e"]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reads as: "accounts-scoped libraries may only depend on libraries that are tagged scope:accounts or scope:shared, and in all cases, those libraries must not be tagged type:e2e." This is particularly useful when your shared scope contains a mix of test utilities and production utilities that you want to distinguish at the dependency constraint level.&lt;/p&gt;

&lt;p&gt;Violations in Action: What This Looks Like in Your IDE&lt;br&gt;
Let us see what happens when a developer violates one of these rules. This is arguably the most important section of the article because it shows the tangible developer experience of boundary enforcement.&lt;br&gt;
typescript// libs/payments/feature-transfer/src/lib/transfer.component.ts&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// ✅ ALLOWED — payments lib importing from its own domain&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PaymentsApiService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@banking/payments/data-access&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ ALLOWED — payments lib importing from shared scope&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CurrencyFormatterPipe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@banking/shared/util-formatters&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ButtonComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@banking/shared/ui-design-system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ❌ VIOLATION — crossing domain boundaries&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LoanEligibilityService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@banking/loans/data-access&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ESLint Error: "A project tagged with 'scope:payments' can only depend on &lt;/span&gt;
&lt;span class="c1"&gt;// libs tagged with 'scope:payments' or 'scope:shared'"&lt;/span&gt;

&lt;span class="c1"&gt;// ❌ VIOLATION — lib importing from an app&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@banking/banking-portal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ESLint Error: "A project tagged with 'type:lib' cannot depend on &lt;/span&gt;
&lt;span class="c1"&gt;// libs tagged with 'type:app'"&lt;/span&gt;

&lt;span class="c1"&gt;// ❌ VIOLATION — reaching into e2e project&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MockApiInterceptor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@banking/banking-portal-e2e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ESLint Error: "Imports of e2e projects are not permitted"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These errors surface in real time in VS Code (with the ESLint extension installed).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architectural Payoff
&lt;/h2&gt;

&lt;p&gt;Once module boundaries are in place and running in CI, the benefits compound over time in ways that go well beyond code organization.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your architecture becomes self-documenting. A new engineer joining the banking platform team can read the ESLint configuration and understand the entire domain structure and dependency rules in under five minutes. The rules tell the same story as the architecture diagram — because they are the architecture diagram, expressed as code.&lt;/li&gt;
&lt;li&gt;Code reviews become faster and more focused. Reviewers no longer need to manually audit import paths or ask questions like "should payments really know about loans?" The linter has already answered that question before the pull request was opened. Reviews can focus entirely on logic, correctness, and intent.&lt;/li&gt;
&lt;li&gt;Refactoring becomes safe. Because every library exposes only what its index.ts exports, and because boundaries prevent cross-domain imports, you can refactor a library's internals with surgical confidence. The blast radius of any change is bounded by the public API surface.&lt;/li&gt;
&lt;li&gt;Teams can work in parallel without stepping on each other. When domain boundaries are enforced, the payments team and the loans team genuinely cannot create accidental dependencies between their work. Conway's Law works in your favour: the organizational structure is mirrored by — and protected by — the module boundary rules.&lt;/li&gt;
&lt;li&gt;The architecture survives team turnover. Senior engineers who understand the original design decisions eventually leave. Without tooling, their architectural intent leaves with them. With boundary enforcement, the decisions are encoded in the linting configuration and outlive any individual contributor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The companion repository for this article, including the full workspace structure, ESLint configuration, sample library implementations, and intentional violations with their error output, is available at &lt;a href="https://github.com/sakthikumaran22/nx-module-boundaries-demo" rel="noopener noreferrer"&gt;github&lt;/a&gt;. Clone it, break the rules, and watch the linter push back.&lt;/p&gt;

&lt;p&gt;github: &lt;a href="https://github.com/sakthikumaran22/nx-module-boundaries-demo" rel="noopener noreferrer"&gt;https://github.com/sakthikumaran22/nx-module-boundaries-demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reference: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nx.dev/docs/technologies/eslint/eslint-plugin/guides/enforce-module-boundaries" rel="noopener noreferrer"&gt;https://nx.dev/docs/technologies/eslint/eslint-plugin/guides/enforce-module-boundaries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nx.dev/docs/features/enforce-module-boundaries" rel="noopener noreferrer"&gt;https://nx.dev/docs/features/enforce-module-boundaries&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nx</category>
      <category>architecture</category>
      <category>typescript</category>
      <category>monorepo</category>
    </item>
  </channel>
</rss>
