<?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: Yanka Santos(She/her)</title>
    <description>The latest articles on DEV Community by Yanka Santos(She/her) (@yankasantos).</description>
    <link>https://dev.to/yankasantos</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%2F1045714%2Fa12b0835-36c1-4f4f-ba9a-03426e608e52.jpeg</url>
      <title>DEV Community: Yanka Santos(She/her)</title>
      <link>https://dev.to/yankasantos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yankasantos"/>
    <language>en</language>
    <item>
      <title>Feature Flags: The Kill Switch Every Startup Needs</title>
      <dc:creator>Yanka Santos(She/her)</dc:creator>
      <pubDate>Fri, 06 Mar 2026 14:01:28 +0000</pubDate>
      <link>https://dev.to/yankasantos/feature-flagsthe-kill-switchevery-startupneeds-1j5c</link>
      <guid>https://dev.to/yankasantos/feature-flagsthe-kill-switchevery-startupneeds-1j5c</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;For frontend, backend &amp;amp; platform engineers at fintech and gaming startups&lt;/strong&gt; · 12 min read&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;You're shipping a new payment flow on Black Friday. Or rolling out a new loot-box mechanic to 2 million players simultaneously. One bug and it's all over. &lt;strong&gt;Feature flags are the safety net, the telescope, and the scalpel — all in one.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's be direct: if you're working at a fintech or gaming startup and you're &lt;em&gt;not&lt;/em&gt; using feature flags, you're flying blind at altitude. You're deploying code and hoping for the best — and hope is not a release strategy.&lt;/p&gt;

&lt;p&gt;This post covers what feature flags actually are (and what they're not), how they differ from feature toggles, and how to implement them in production using &lt;strong&gt;OpenFeature&lt;/strong&gt; and &lt;strong&gt;LaunchDarkly&lt;/strong&gt; — the modern standard for flag management at scale.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a Feature Flag?
&lt;/h2&gt;

&lt;p&gt;A feature flag (also called a feature gate, feature switch, or feature control) is a software mechanism that allows you to &lt;strong&gt;enable or disable functionality at runtime — without deploying new code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think of it as a conditional in your codebase that's controlled externally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before flags:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// You either ship the feature or you don't&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;renderNewCheckoutFlow&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;&lt;strong&gt;With flags:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The flag value comes from an external system — no redeploy needed&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;showNewCheckout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBooleanValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new-checkout-flow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;showNewCheckout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;renderNewCheckoutFlow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;renderLegacyCheckoutFlow&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;That flag value — &lt;code&gt;new-checkout-flow&lt;/code&gt; — can be toggled on or off via a dashboard, pushed to a specific user segment, rolled out to 5% of traffic, or locked to a certain geography. &lt;strong&gt;All without touching your codebase.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Real-World Example:&lt;/strong&gt; Stripe uses feature flags to test new payment APIs with a subset of merchants before full rollout. Riot Games uses them to enable experimental gameplay mechanics on specific servers before global release. The pattern is the same — controlled exposure, at scale.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  Feature Flag Evaluation Flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; User Request  ──→  SDK / Client  ──→  Flag Evaluation
                                             │
                                     ┌───────┴────────┐
                                     │   Flag ON?     │
                                     │   Segment?     │
                                     │   Rollout %?   │
                                     └───────┬────────┘
                                    ┌────────┴─────────┐
                                   YES                  NO
                                    │                   │
                             ┌──────┴──────┐   ┌───────┴──────┐
                             │ New Feature │   │ Legacy Code  │
                             └──────┬──────┘   └───────┬──────┘
                                    │                   │
                             📊 Track Metrics   📊 Track Metrics
                                    │                   │
                             └──────┴───────────────────┘
                                          │
                              Aggregate → Decide → Ship or Kill
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Feature Flag vs Feature Toggle: Not the Same Thing
&lt;/h2&gt;

&lt;p&gt;This is where most engineers get fuzzy. The terms are often used interchangeably — but there's a meaningful difference in &lt;em&gt;intent and lifecycle.&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Feature Toggle&lt;/th&gt;
&lt;th&gt;Feature Flag&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lifespan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Short-lived&lt;/td&gt;
&lt;td&gt;Long-lived&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Location&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Inside the codebase&lt;/td&gt;
&lt;td&gt;External service / config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hide incomplete work during development&lt;/td&gt;
&lt;td&gt;Experimentation, ops control, segmentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Removed after ship?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Not necessarily (some are permanent)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dashboard needed?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Supports A/B testing?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"A feature toggle says 'not yet'. A feature flag says 'for you, yes — for everyone else, not yet.'"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  The 4 Types of Feature Flags You'll Actually Use
&lt;/h3&gt;

&lt;h4&gt;
  
  
  🚀 Release Flags
&lt;/h4&gt;

&lt;p&gt;Deploy code to production but keep it dark. Enable for specific users or a percentage of traffic. Classic for canary deployments.&lt;/p&gt;

&lt;h4&gt;
  
  
  🧪 Experiment Flags
&lt;/h4&gt;

&lt;p&gt;A/B and multivariate testing. Show variant A to 50% of users, variant B to the other 50%. Measure, decide, ship the winner.&lt;/p&gt;

&lt;h4&gt;
  
  
  🔌 Ops Flags
&lt;/h4&gt;

&lt;p&gt;Kill switches for your infrastructure. Disable a third-party payment provider in real-time if it starts failing. &lt;strong&gt;Essential for fintechs.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  💼 Permission Flags
&lt;/h4&gt;

&lt;p&gt;Control access to features by plan tier, region, or user attribute. Enterprise-only dashboard? Beta access for power users? This is it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters for Fintech &amp;amp; Gaming
&lt;/h2&gt;

&lt;p&gt;These two verticals share something critical: &lt;strong&gt;the cost of a bad deploy is catastrophic and immediate.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In fintech, a broken payment flow means lost revenue, chargebacks, and regulatory risk — every minute counts. In gaming, a bugged ability or a broken economy mechanic at launch can spiral into player outrage and press coverage within hours.&lt;/p&gt;

&lt;h3&gt;
  
  
  Incident Timeline: With vs. Without Flags
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;❌ Without Feature Flags&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deploy ──→ Bug Found ──→ Hotfix Dev ──→ CI/CD Pipeline ──→ Redeploy
                                                                │
                                              ⏱ 30 min – 4 hours of exposure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;✅ With Feature Flags&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Deploy ──→ Bug Found ──→ Toggle Flag OFF ──→ Fix in peace
                               │
                ⏱ 30 seconds to mitigate. Zero redeploy needed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  OpenFeature: The Vendor-Neutral Standard
&lt;/h2&gt;

&lt;p&gt;Before OpenFeature, every feature flag provider had its own SDK. Switch from LaunchDarkly to Unleash? Rewrite all your flag evaluation code. It was vendor lock-in by design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenFeature&lt;/strong&gt; is a &lt;a href="https://cncf.io" rel="noopener noreferrer"&gt;CNCF&lt;/a&gt; incubating project that defines a &lt;em&gt;vendor-neutral, open standard API&lt;/em&gt; for feature flagging. You code against the OpenFeature API — and swap providers via a plugin with zero application code changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenFeature Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────┐
│                     Your Application                        │
│              (calls OpenFeature SDK only)                   │
└────────────────────────┬────────────────────────────────────┘
                         │  Standard API
┌────────────────────────▼────────────────────────────────────┐
│                   OpenFeature SDK                           │
│          (Node.js / Go / Java / Python / .NET)              │
└────────────────────────┬────────────────────────────────────┘
                         │  Provider Interface
┌────────────────────────▼────────────────────────────────────┐
│              Provider Plugin (your choice)                  │
│    LaunchDarkly │ Unleash │ Flagsmith │ CloudBees │ ...      │
└─────────────────────────────────────────────────────────────┘

  Swap the provider → zero application code changes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting Up OpenFeature with LaunchDarkly (Node.js)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Install dependencies&lt;/span&gt;
&lt;span class="c1"&gt;// npm install @openfeature/server-sdk @openfeature/launchdarkly-provider&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;OpenFeature&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;@openfeature/server-sdk&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;LaunchDarklyProvider&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;@openfeature/launchdarkly-provider&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Register your provider once at startup&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;OpenFeature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProviderAndWait&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;LaunchDarklyProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_SDK_KEY&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="c1"&gt;// 3. Get a client&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;OpenFeature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// 4. Evaluate a flag&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isNewCheckoutEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBooleanValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new-checkout-flow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                     &lt;span class="c1"&gt;// default if flag not found&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;targetingKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;// evaluation context&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🔁 &lt;strong&gt;Switching Providers:&lt;/strong&gt; Tomorrow you decide to move from LaunchDarkly to Unleash. You change &lt;strong&gt;one line&lt;/strong&gt; — the provider registration. Every &lt;code&gt;getBooleanValue()&lt;/code&gt;, &lt;code&gt;getStringValue()&lt;/code&gt;, and &lt;code&gt;getNumberValue()&lt;/code&gt; call across your entire codebase stays exactly the same.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  LaunchDarkly: The Provider
&lt;/h2&gt;

&lt;p&gt;LaunchDarkly is the battle-tested flag management platform used by Atlassian, IBM, and hundreds of fintechs and game studios. It handles targeting rules, percentage rollouts, A/B experiments, scheduled flags, and more — all from a dashboard your PMs can also operate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advanced: Targeting Rules for Gaming
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Rich evaluation context for player segmentation&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;evaluationContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;multi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// 'US', 'DE', 'BR'&lt;/span&gt;
    &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;// 42&lt;/span&gt;
    &lt;span class="na"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptionTier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// 'free' | 'premium'&lt;/span&gt;
    &lt;span class="na"&gt;betaAccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isBetaTester&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;game&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gameId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;// 'EU-WEST', 'NA-EAST'&lt;/span&gt;
    &lt;span class="na"&gt;matchType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// 'ranked' | 'casual'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// This flag only enables for premium EU players above level 30&lt;/span&gt;
&lt;span class="c1"&gt;// — configured entirely in the LaunchDarkly dashboard&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;showNewAbility&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBooleanValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hero-new-ultimate-ability&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;evaluationContext&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fintech Example: Payment Provider Fallback (Ops Flag)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Ops flag: switch payment processors without a deploy&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeProcessor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getStringValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active-payment-processor&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;stripe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                      &lt;span class="c1"&gt;// default to Stripe&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;targetingKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;merchant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;merchant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&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;processorMap&lt;/span&gt; &lt;span class="o"&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;stripe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="nx"&gt;StripeGateway&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;adyen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="nx"&gt;AdyenGateway&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;braintree&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="nx"&gt;BraintreeGateway&lt;/span&gt;&lt;span class="p"&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;gateway&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;processorMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;activeProcessor&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;StripeGateway&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Stripe starts degrading? Log into LaunchDarkly, flip &lt;code&gt;active-payment-processor&lt;/code&gt; to &lt;code&gt;'adyen'&lt;/code&gt;. Done. No deploy, no 3am incident call.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Progressive Rollout Pattern
&lt;/h2&gt;

&lt;p&gt;The real power of feature flags isn't just on/off — it's the ability to &lt;em&gt;progressively increase exposure&lt;/em&gt; while monitoring metrics in real time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rollout Stages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Stage 1 — Internal Dogfooding
  ████░░░░░░░░░░░░░░░░░░░░░░░░░░  ~team only
  Enable for engineering &amp;amp; QA. Test in production with real data.

Stage 2 — Beta Segment (1–5%)
  ████████░░░░░░░░░░░░░░░░░░░░░░  1-5%
  Opted-in beta users or random sample. Monitor errors + KPIs.

Stage 3 — Canary Release (10–20%)
  ████████████████░░░░░░░░░░░░░░  10-20%
  Increase gradually. Auto-kill if error rate spikes.

Stage 4 — Staged Rollout (50%)
  ████████████████████████░░░░░░  50%
  Half your users. Confident in metrics? Keep going.

Stage 5 — Full Release (100%)
  ██████████████████████████████  100% ✓
  Ship to everyone. Monitor 24-48h. Then clean up the flag.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The 5-Step Rollout Playbook
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;01 — Internal Dogfooding&lt;/strong&gt; &lt;code&gt;0% → team&lt;/code&gt;&lt;br&gt;
Enable the flag only for your engineering and QA team. Test in production with real data, no real user impact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;02 — Beta Segment&lt;/strong&gt; &lt;code&gt;team → 1–5%&lt;/code&gt;&lt;br&gt;
Roll out to opted-in beta users or a randomly sampled cohort. Monitor error rates, latency, and core KPIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;03 — Canary Release&lt;/strong&gt; &lt;code&gt;5% → 20% → 50%&lt;/code&gt;&lt;br&gt;
Gradually increase the percentage. Automate metric monitoring — if error rate spikes above threshold, auto-kill the flag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;04 — Full Rollout&lt;/strong&gt; &lt;code&gt;50% → 100%&lt;/code&gt;&lt;br&gt;
Ship to everyone. Monitor for 24–48h. Then remove the flag from code and clean up the technical debt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;05 — Cleanup&lt;/strong&gt; &lt;code&gt;100% → archived&lt;/code&gt;&lt;br&gt;
Don't let dead flags accumulate. Archive or delete from your provider, and remove the conditional from code. &lt;strong&gt;Flag debt is real.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Dos and Don'ts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✅ Do This
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Name flags semantically: &lt;code&gt;checkout-v2-enabled&lt;/code&gt;, &lt;code&gt;payment-fallback-adyen&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Always provide a sensible &lt;strong&gt;default value&lt;/strong&gt; (fail safe)&lt;/li&gt;
&lt;li&gt;Add &lt;strong&gt;flag owners&lt;/strong&gt; and &lt;strong&gt;expiry dates&lt;/strong&gt; in your provider dashboard&lt;/li&gt;
&lt;li&gt;Track flag evaluations as telemetry / observability signals&lt;/li&gt;
&lt;li&gt;Clean up flags after 100% rollout — schedule it as a ticket&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;evaluation context&lt;/strong&gt; for rich targeting (user tier, region, plan)&lt;/li&gt;
&lt;li&gt;Cache flag values client-side to avoid latency on every request&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ Avoid This
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Nesting flags inside other flags (impossible to reason about)&lt;/li&gt;
&lt;li&gt;Using flags for secrets or sensitive configuration&lt;/li&gt;
&lt;li&gt;Letting old flags accumulate — "flag debt" slows teams down&lt;/li&gt;
&lt;li&gt;Hardcoding flag names as magic strings scattered everywhere (use constants)&lt;/li&gt;
&lt;li&gt;Skipping the cleanup phase post-rollout&lt;/li&gt;
&lt;li&gt;Blocking UI render on asynchronous flag evaluation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Feature flags&lt;/strong&gt; let you control feature exposure at runtime, without code deploys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature toggles&lt;/strong&gt; are short-lived code conditionals; &lt;strong&gt;feature flags&lt;/strong&gt; are long-lived, externally managed controls&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;release, experiment, ops, and permission&lt;/strong&gt; flag types for different use cases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenFeature&lt;/strong&gt; is the vendor-neutral SDK standard — code once, swap providers freely&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LaunchDarkly&lt;/strong&gt; gives you targeting rules, percentage rollouts, and a PM-friendly dashboard&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progressive rollout&lt;/strong&gt; (team → 1% → 20% → 100%) is the safest path to production&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;fintechs&lt;/strong&gt;: ops flags are your circuit breaker&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;gaming studios&lt;/strong&gt;: release flags are your launch control panel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flag debt is real&lt;/strong&gt; — always schedule cleanup after full rollout&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Tags: &lt;code&gt;#featureflags&lt;/code&gt; &lt;code&gt;#openfeature&lt;/code&gt; &lt;code&gt;#launchdarkly&lt;/code&gt; &lt;code&gt;#fintech&lt;/code&gt; &lt;code&gt;#gamedev&lt;/code&gt; &lt;code&gt;#devops&lt;/code&gt; &lt;code&gt;#engineering&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>featureflags</category>
      <category>openfeature</category>
      <category>launchdarkly</category>
      <category>software</category>
    </item>
    <item>
      <title>Circular Dependencies in Monorepos: The Silent Architecture Killer</title>
      <dc:creator>Yanka Santos(She/her)</dc:creator>
      <pubDate>Mon, 16 Feb 2026 13:51:48 +0000</pubDate>
      <link>https://dev.to/yankasantos/circular-dependencies-in-monorepos-the-silent-architecture-killer-2hch</link>
      <guid>https://dev.to/yankasantos/circular-dependencies-in-monorepos-the-silent-architecture-killer-2hch</guid>
      <description>&lt;p&gt;If you've ever worked with an Nx monorepo (especially in a React front-end), chances are you've hit this wall:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Why is this import causing a circular dependency… and why is my app breaking in the weirdest way possible?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Recently, I ran into this exact issue while working with a monorepo architecture that looked perfectly reasonable at first glance — until it wasn't. The build failed, the error message was cryptic, and my "perfectly logical" code structure turned out to be a dependency nightmare.&lt;/p&gt;

&lt;p&gt;Let me walk you through what happened, why it's dangerous, and how to fix it the right way.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 The Scenario: When Good Architecture Goes Circular
&lt;/h2&gt;

&lt;p&gt;Picture this: you're working with an Nx monorepo with the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apps/
  web/
libs/
  modules/
    product/
    price/
  shared/
    ui/
    data/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your modules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@modules/product&lt;/code&gt; - domain logic for products&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@modules/price&lt;/code&gt; - pricing logic derived from product data&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@shared/ui&lt;/code&gt; - reusable UI components used across the app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's where things went sideways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;product&lt;/code&gt; module needed to access mock data for testing&lt;/li&gt;
&lt;li&gt;That mock data lived in the &lt;code&gt;price&lt;/code&gt; module (seemed logical at the time)&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;price&lt;/code&gt; module imported UI components from &lt;code&gt;shared/ui&lt;/code&gt; to render pricing displays&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;shared/ui&lt;/code&gt; module had a &lt;code&gt;ProductCard&lt;/code&gt; component that imported types from &lt;code&gt;product&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Boom. 💥 Circular dependency detected.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;product → price (imports mock data)
    ↓
price → shared/ui (imports UI components)
    ↓
shared/ui → product (imports types and components)
    ↓
    ← → ← (circular!)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nx caught it. The build failed. And I learned an important lesson about architecture boundaries.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔄 What Is a Circular Dependency (And Why Should You Care)?
&lt;/h2&gt;

&lt;p&gt;A circular dependency happens when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Module A depends on Module B&lt;/li&gt;
&lt;li&gt;Module B depends on Module C
&lt;/li&gt;
&lt;li&gt;Module C (directly or indirectly) depends back on Module A&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In monorepos, this often happens accidentally because the boundaries between domains, UI, and shared utilities start to blur as the codebase grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚠️ Why This Is Actually Dangerous
&lt;/h2&gt;

&lt;p&gt;Circular dependencies aren't just a linting annoyance. They cause real, production-breaking problems:&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ &lt;strong&gt;Unpredictable Module Initialization&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The JavaScript module system can't determine which module to initialize first. Here's what happens:&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;// product/index.ts&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;priceMocks&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;@modules/price&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;const&lt;/span&gt; &lt;span class="nx"&gt;productData&lt;/span&gt; &lt;span class="o"&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;priceMocks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// priceMocks might be undefined here!&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// price/index.ts  &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;ProductCard&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;@shared/ui&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;const&lt;/span&gt; &lt;span class="nx"&gt;priceMocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...],&lt;/span&gt; &lt;span class="c1"&gt;// This might not be initialized when product imports it&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your imports might resolve to &lt;code&gt;undefined&lt;/code&gt;, leading to runtime errors that only appear in specific initialization orders—the worst kind of bug to debug.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ &lt;strong&gt;Broken Tree-Shaking and Bundle Bloat&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Bundlers like Webpack and Vite can't properly shake unused code when modules form circular chains. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Larger bundle sizes&lt;/li&gt;
&lt;li&gt;Slower load times&lt;/li&gt;
&lt;li&gt;Code that should be split across chunks gets bundled together&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ &lt;strong&gt;Testing Nightmares&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When modules depend on each other, mocking becomes impossible:&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;// Trying to test product module&lt;/span&gt;
&lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@modules/price&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// But price depends on shared/ui&lt;/span&gt;
&lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@shared/ui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// But shared/ui depends on product&lt;/span&gt;
&lt;span class="c1"&gt;// 🤯 Your test setup becomes a house of cards&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ❌ &lt;strong&gt;Tight Coupling Between Domains&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Circular dependencies signal that your domain boundaries are wrong. If &lt;code&gt;product&lt;/code&gt; can't exist without &lt;code&gt;price&lt;/code&gt;, and &lt;code&gt;price&lt;/code&gt; can't exist without &lt;code&gt;product&lt;/code&gt;, you don't have separate domains—you have one tangled mess.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Worst of all?&lt;/strong&gt; These issues compound as your codebase grows. What starts as a simple two-module cycle becomes a web of interdependencies that makes refactoring feel like defusing a bomb.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 The Root Cause (Hint: It's Architecture, Not Just Imports)
&lt;/h2&gt;

&lt;p&gt;After stepping back and analyzing the problem, I realized the real issue:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shared UI was doing too much.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When a &lt;code&gt;shared/ui&lt;/code&gt; module:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Knows about domain data (&lt;code&gt;product&lt;/code&gt;, &lt;code&gt;price&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Imports business logic or domain types directly&lt;/li&gt;
&lt;li&gt;Exposes data instead of just UI contracts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…it stops being "shared UI" and becomes a &lt;strong&gt;dependency trap&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The problem isn't the imports themselves—it's that module boundaries weren't clearly defined. Each module was reaching across domains because there was no clear contract between layers.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ The Right Way to Fix It: A Complete Refactoring Guide
&lt;/h2&gt;

&lt;p&gt;Let me show you the actual before and after of how I fixed this issue.&lt;/p&gt;

&lt;h3&gt;
  
  
  📛 Before: The Circular Dependency
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// libs/modules/product/src/index.ts&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;priceMocks&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;@modules/price&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;const&lt;/span&gt; &lt;span class="nx"&gt;useProduct&lt;/span&gt; &lt;span class="o"&gt;=&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;priceMocks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productPrices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Importing from price module&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// libs/modules/price/src/index.ts&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;ProductCard&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;@shared/ui&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;const&lt;/span&gt; &lt;span class="nx"&gt;priceMocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;productPrices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&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;const&lt;/span&gt; &lt;span class="nx"&gt;PriceDisplay&lt;/span&gt; &lt;span class="o"&gt;=&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProductCard&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// Using shared UI&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// libs/shared/ui/src/components/ProductCard.tsx&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;Product&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;@modules/product&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Importing from product!&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ProductCardProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Using product domain type&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;product&lt;/code&gt; → &lt;code&gt;price&lt;/code&gt; → &lt;code&gt;shared/ui&lt;/code&gt; → &lt;code&gt;product&lt;/code&gt; (circular!)&lt;/li&gt;
&lt;li&gt;Shared UI knows about domain types&lt;/li&gt;
&lt;li&gt;Mock data is embedded in feature modules&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  ✅ After: Clean, One-Way Dependencies
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Extract Shared Types and Data&lt;/strong&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="c1"&gt;// libs/shared/data/src/lib/types/product.types.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Price&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// libs/shared/data/src/lib/mocks/product.mocks.ts&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;Product&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;../types&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;const&lt;/span&gt; &lt;span class="nx"&gt;productMocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Widget&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A great widget&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="c1"&gt;// libs/shared/data/src/lib/mocks/price.mocks.ts&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;Price&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;../types&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;const&lt;/span&gt; &lt;span class="nx"&gt;priceMocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;29.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Make Shared UI Dumb (On Purpose)&lt;/strong&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="c1"&gt;// libs/shared/ui/src/components/ProductCard.tsx&lt;/span&gt;
&lt;span class="c1"&gt;// ✅ NO imports from feature modules!&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ProductCardProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;onAddToCart&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&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;const&lt;/span&gt; &lt;span class="nx"&gt;ProductCard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProductCardProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;onAddToCart&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;product-card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h3&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;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h3&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&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;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;price&lt;/span&gt;&lt;span class="dl"&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;price&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onAddToCart&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onAddToCart&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;Add&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;Cart&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&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;&lt;strong&gt;Key principle:&lt;/strong&gt; The UI component receives data via props. It knows &lt;em&gt;nothing&lt;/em&gt; about where the data comes from. No domain imports. No business logic. No surprises.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Feature Modules Orchestrate, UI Presents&lt;/strong&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="c1"&gt;// libs/modules/product/src/lib/containers/ProductContainer.tsx&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;ProductCard&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;@shared/ui&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;productMocks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;priceMocks&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;@shared/data&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;formatPrice&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;./utils&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;const&lt;/span&gt; &lt;span class="nx"&gt;ProductContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;productId&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;productMocks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;productId&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;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;priceMocks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProductCard&lt;/span&gt;
      &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&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="nx"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;formatPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="nx"&gt;onAddToCart&lt;/span&gt;&lt;span class="o"&gt;=&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Added to cart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&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;&lt;strong&gt;Step 4: Clear Dependency Flow&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@shared/data (types, mocks, utilities)
    ↓
@shared/ui (presentation components)
    ↓
@modules/product, @modules/price (domain logic + orchestration)
    ↓
@apps/web (application)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each layer only imports from layers below it. &lt;strong&gt;Never upward.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Enforce It with Nx Module Boundaries
&lt;/h2&gt;

&lt;p&gt;Since you're using Nx, you have a superpower: &lt;strong&gt;enforceable module boundaries&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure Module Boundaries in Your Nx Workspace
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;nx.json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.eslintrc.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;"@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;"allow"&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;"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:feature"&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:ui"&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:data"&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:util"&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:ui"&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:util"&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:data"&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:data"&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:util"&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:util"&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="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;h3&gt;
  
  
  Tag Your Libraries
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;libs/modules/product/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;"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;"type:feature"&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:product"&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="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;libs/shared/ui/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;"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;"type:ui"&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="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;libs/shared/data/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;"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;"type:data"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now Nx will &lt;strong&gt;prevent you from creating circular dependencies at compile time&lt;/strong&gt;. If &lt;code&gt;shared/ui&lt;/code&gt; tries to import from &lt;code&gt;modules/product&lt;/code&gt;, the linter will immediately fail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✗ A project tagged with "type:ui" can only depend on libs tagged with "type:util", "type:data"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔍 Detection: Catch Circular Dependencies Before They Ship
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Use Nx's Built-In Dependency Graph&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx nx graph
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This opens a visual graph of your workspace. Circular dependencies will be highlighted in red.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Add Madge for CI/CD Checks&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; madge

&lt;span class="c"&gt;# Add to package.json scripts&lt;/span&gt;
&lt;span class="s2"&gt;"check:circular"&lt;/span&gt;: &lt;span class="s2"&gt;"madge --circular --extensions ts,tsx libs/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this to your CI pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/ci.yml&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check for circular dependencies&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run check:circular&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. &lt;strong&gt;ESLint Plugin for Immediate Feedback&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; eslint-plugin-import
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.eslintrc.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;"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;"import"&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;"import/no-cycle"&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;"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;"maxDepth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ignoreExternal"&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="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;p&gt;Your IDE will now warn you &lt;strong&gt;as you type&lt;/strong&gt; when you create a circular import.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 The Mental Model Shift
&lt;/h2&gt;

&lt;p&gt;Here's the key mindset change that helped me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't ask:&lt;/strong&gt; "What feature does this code belong to?"&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Ask instead:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What does this code depend on?"&lt;/li&gt;
&lt;li&gt;"What should be allowed to depend on this code?"&lt;/li&gt;
&lt;li&gt;"If I delete this module, what breaks?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of your architecture like a building:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Foundation (shared/data, shared/util):&lt;/strong&gt; The base that everything rests on&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Floors (shared/ui):&lt;/strong&gt; Built on the foundation, used by everyone&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rooms (feature modules):&lt;/strong&gt; Built on the floors, can't support each other&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Roof (apps):&lt;/strong&gt; Sits on top, depends on everything below&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;A room can't hold up another room. That's not how buildings work.&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  🎯 Practical Rules to Live By
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1️⃣ &lt;strong&gt;Shared UI Should Be Dumb&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A good &lt;code&gt;shared/ui&lt;/code&gt; component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Receives data via props&lt;/li&gt;
&lt;li&gt;✅ Has no idea where data comes from&lt;/li&gt;
&lt;li&gt;✅ Could be published as an npm package tomorrow&lt;/li&gt;
&lt;li&gt;❌ Never imports from feature modules&lt;/li&gt;
&lt;li&gt;❌ Contains no business logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it like Material-UI or Chakra—it renders what you give it, period.&lt;/p&gt;
&lt;h3&gt;
  
  
  2️⃣ &lt;strong&gt;Feature Modules Should Never Import Each Other&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If &lt;code&gt;product&lt;/code&gt; needs something from &lt;code&gt;price&lt;/code&gt;, that "something" belongs in &lt;code&gt;shared/data&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="c1"&gt;// ❌ Bad&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;calculateDiscount&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;@modules/price&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Good&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;calculateDiscount&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;@shared/data&lt;/span&gt;&lt;span class="dl"&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️⃣ &lt;strong&gt;Types and Interfaces Belong in Shared Layers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If more than one module needs a type, it's shared by definition:&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;// ❌ Bad - defined in feature module&lt;/span&gt;
&lt;span class="c1"&gt;// libs/modules/product/src/types.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Good - defined in shared layer&lt;/span&gt;
&lt;span class="c1"&gt;// libs/shared/data/src/types/product.types.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&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;
  
  
  4️⃣ &lt;strong&gt;Mocks Don't Live in Production Code&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Move mocks out of &lt;code&gt;src/&lt;/code&gt; folders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;libs/
  modules/
    product/
      src/              ← Production code
      __mocks__/        ← Mocks for testing
      __storybook__/    ← Storybook-specific mocks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or better yet, centralize them in &lt;code&gt;shared/data/mocks/&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  5️⃣ &lt;strong&gt;One Direction: Dependencies Flow Downward&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Apps ────────────┐
                 ↓
Feature Modules ─┤
                 ↓
Shared UI ───────┤
                 ↓
Shared Data ─────┤
                 ↓
Shared Utils ────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you ever find yourself making an import that goes upward, stop. That's your architectural red flag.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚠️ When Circular Dependencies Are (Kinda) OK
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Exception: Type-Only Imports
&lt;/h3&gt;

&lt;p&gt;TypeScript's &lt;code&gt;import type&lt;/code&gt; doesn't create runtime circular dependencies:&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="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Product&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;@modules/product&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// This is safe because types are erased at compile time&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Price&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Product&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;However, I still recommend avoiding this. Even if it's technically safe, it creates mental coupling and makes refactoring harder. Better to keep types in shared layers.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Real-World Impact: What Changed After the Fix
&lt;/h2&gt;

&lt;p&gt;After refactoring to eliminate circular dependencies in our monorepo, we saw:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Build time decreased by 23%&lt;/strong&gt; (from 45s to 35s)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Bundle size reduced by 18%&lt;/strong&gt; (tree-shaking finally worked)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Test setup simplified&lt;/strong&gt; (no more circular mock hell)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Onboarding improved&lt;/strong&gt; (new devs could understand module boundaries)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Zero circular dependency errors&lt;/strong&gt; for 6 months and counting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best part? When we added new features, the clear boundaries made it obvious where code should live. No more "where should I put this?" debates.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎬 Conclusion
&lt;/h2&gt;

&lt;p&gt;Circular dependencies in monorepos aren't just a technical nuisance—they're &lt;strong&gt;architecture feedback&lt;/strong&gt;. When you see one, don't just "fix the import." Step back and ask:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Why do these modules know so much about each other?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your codebase is asking for clearer boundaries. Listen to it.&lt;/p&gt;

&lt;p&gt;By:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extracting shared concerns to dedicated layers&lt;/li&gt;
&lt;li&gt;Making UI components presentation-only&lt;/li&gt;
&lt;li&gt;Enforcing module boundaries with Nx&lt;/li&gt;
&lt;li&gt;Detecting violations early with tooling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…you create a monorepo that's not just buildable, but maintainable, scalable, and actually enjoyable to work in.&lt;/p&gt;

&lt;p&gt;The next time you're tempted to make that "quick import" that completes the circle, pause. Your future self (and your team) will thank you.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Quick Reference: Nx Circular Dependency Fix Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Run &lt;code&gt;nx graph&lt;/code&gt; to visualize your dependency graph&lt;/li&gt;
&lt;li&gt;[ ] Create &lt;code&gt;@shared/data&lt;/code&gt; library for types and mocks&lt;/li&gt;
&lt;li&gt;[ ] Move all shared types from feature modules to &lt;code&gt;@shared/data&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Refactor &lt;code&gt;@shared/ui&lt;/code&gt; components to be prop-based (no domain imports)&lt;/li&gt;
&lt;li&gt;[ ] Configure Nx module boundary rules with tags&lt;/li&gt;
&lt;li&gt;[ ] Add &lt;code&gt;import/no-cycle&lt;/code&gt; ESLint rule&lt;/li&gt;
&lt;li&gt;[ ] Set up &lt;code&gt;madge&lt;/code&gt; in CI pipeline&lt;/li&gt;
&lt;li&gt;[ ] Update team documentation with dependency rules&lt;/li&gt;
&lt;li&gt;[ ] Add architectural decision record (ADR) explaining the approach&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Have you battled circular dependencies in your Nx monorepo? What strategies worked (or didn't work) for you? I'd love to hear your war stories in the comments!&lt;/em&gt; 💬&lt;/p&gt;

&lt;h1&gt;
  
  
  Nx #Monorepo #React #SoftwareArchitecture #CleanCode #Frontend #TypeScript #WebDevelopment #SoftwareEngineering
&lt;/h1&gt;

</description>
      <category>monorepo</category>
      <category>architecture</category>
      <category>frontend</category>
      <category>react</category>
    </item>
  </channel>
</rss>
