<?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: Phaneendra Kanduri</title>
    <description>The latest articles on DEV Community by Phaneendra Kanduri (@9thquadrant).</description>
    <link>https://dev.to/9thquadrant</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%2F279228%2Fe26faddb-3ba3-494e-8e9b-dbe796bcc996.png</url>
      <title>DEV Community: Phaneendra Kanduri</title>
      <link>https://dev.to/9thquadrant</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/9thquadrant"/>
    <language>en</language>
    <item>
      <title>Material Design Principles Are Correct. Implementing Them Will Break You.</title>
      <dc:creator>Phaneendra Kanduri</dc:creator>
      <pubDate>Sat, 18 Apr 2026 12:11:02 +0000</pubDate>
      <link>https://dev.to/9thquadrant/material-design-principles-are-correct-implementing-them-will-break-you-2bm3</link>
      <guid>https://dev.to/9thquadrant/material-design-principles-are-correct-implementing-them-will-break-you-2bm3</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Long read. Written from nine years of implementation experience, not the spec.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Preface
&lt;/h2&gt;

&lt;p&gt;I believe Material Design is correct. Not partially correct -- correct in its core philosophy about how interfaces should behave under load, under interaction, under uncertainty. I follow new versions closely. I implement what I can, whenever I can. I use a Pixel phone specifically because the software is built on these principles, and without that, I would genuinely prefer not using a smartphone at all.&lt;/p&gt;

&lt;p&gt;That conviction is also what makes me honest about the cost. I have lived on both sides of it -- the belief in the philosophy, and the two-week sprint that nearly broke me trying to apply it to a page with 12 components and 7 API calls six years into a codebase. The industry does not talk honestly about that gap. This article does.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I Use a Pixel Phone
&lt;/h2&gt;

&lt;p&gt;I use a Pixel phone. Not because of the camera. Not because of the price. Because the software is built on Material principles, and without that, I would genuinely prefer not using a smartphone at all.&lt;/p&gt;

&lt;p&gt;That is not hyperbole. I have used enough software to know what it feels like when a product is built by people who have internalized a philosophy versus people who have implemented a component library. The difference is not subtle. It is the difference between a product that feels like it understands you and one that feels like it is tolerating you.&lt;/p&gt;

&lt;p&gt;Google proves this at scale. So did Microsoft -- once, with Windows Phone and Windows 7. Both products were built by teams that had a coherent philosophy about how software should behave, and both felt smooth in a way that had nothing to do with hardware specs or engineering heroics. It was ideology, made executable.&lt;/p&gt;

&lt;p&gt;Windows Phone was killed. Not because the UX was bad -- the UX was excellent. It was killed for market and ecosystem reasons that had nothing to do with software quality. That is important. Good philosophy does not guarantee market success. But its absence guarantees UX debt. And that debt compounds silently until it becomes someone's emergency sprint.&lt;/p&gt;

&lt;p&gt;This article is about what that sprint actually costs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Material Is Not the Only Path. It Is One of the Most Correct Ones.
&lt;/h2&gt;

&lt;p&gt;Before I go further: I am not claiming Material Design is the only valid design philosophy. I have used Azure Dashboard circa 2017. I have used WordPress. Both are smooth, both handle data-heavy interfaces competently, and neither follows Material principles as rigorously as Google's own products do.&lt;/p&gt;

&lt;p&gt;There are other coherent philosophies. Apple's HIG is one. Fluent Design, when Microsoft actually commits to it, is another. What these share with Material is not visual style -- it is the existence of a philosophy at all. A set of answers to questions like: what happens when data is loading? What happens when an element appears? What happens when the user scrolls and content shifts? How does the interface communicate uncertainty?&lt;/p&gt;

&lt;p&gt;Products that answer these questions consistently feel smooth. Products that don't -- regardless of how polished the visual design is -- feel unreliable.&lt;/p&gt;

&lt;p&gt;I have used Navan. I have used insurance platforms that are clearly well-funded and well-designed at the visual layer. They are frustrating. Not broken. Frustrating. Because the answers to those questions are inconsistent, and inconsistency at interaction level is something users feel even when they cannot name it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://m3.material.io/foundations" rel="noopener noreferrer"&gt;Material Design's contribution&lt;/a&gt; is that it answers these questions rigorously, in writing, with rationale. That is rare. That is valuable. And that is exactly why implementing it properly is one of the hardest things you can do in frontend engineering.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem Nobody Prices In
&lt;/h2&gt;

&lt;p&gt;Enterprise SaaS applications are not simple. The page I was working on had roughly 12 components, 7 API calls pulling from different data sources, a sticky table header mid-page, and a popup that triggered yet another API call. Each component mounted independently. Each API call resolved at its own time.&lt;/p&gt;

&lt;p&gt;In Angular, the default behavior is straightforward: data arrives, you assign it to a variable, Angular renders it. Clean. Simple. Completely at odds with Material principles.&lt;/p&gt;

&lt;p&gt;What actually happens on screen: components pop in one by one. Heights shift. The sticky header jumps. Scroll position drifts. A popup appears without transition. A button materializes where there was nothing.&lt;/p&gt;

&lt;p&gt;Users tolerate this. They have been trained to tolerate this. That tolerance is not a design success -- it is a UX debt that accumulates silently until someone files a ticket that says "the page feels janky."&lt;/p&gt;

&lt;p&gt;That ticket is how this sprint started.&lt;/p&gt;




&lt;h2&gt;
  
  
  What "No Flickering" Actually Requires
&lt;/h2&gt;

&lt;p&gt;The naive fix is a spinner. Throw a loader on the page, hide everything, show everything when done. Problem solved.&lt;/p&gt;

&lt;p&gt;Except it is not solved, because nested components have their own async lifecycles, a whole-screen loader means users wait longer in perceived time even if actual load time is identical, and when the loader disappears and 12 components render simultaneously, you still get layout shift -- just deferred by one frame.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://m3.material.io/foundations/content-design/writing-for-google/progressive-disclosure" rel="noopener noreferrer"&gt;Material's actual answer is progressive disclosure&lt;/a&gt;: show structure first, then fill it. Reserve space before content lands. Coordinate fade-ins. No element should appear without the space already being reserved for it.&lt;/p&gt;

&lt;p&gt;Angular does not give you this out of the box. Material's component library, at the time, did not have a skeleton loader. So I built the coordination layer myself.&lt;/p&gt;

&lt;p&gt;Every API call got wrapped -- not just for loading state tracking, but for orchestration. The page had a single loading gate: nothing fades in until the critical path calls resolve. Secondary data sources fill in progressively after. The whole-screen loader exists only for the first render. After that, inline loaders handle async state at the component level.&lt;/p&gt;

&lt;p&gt;Here is a simplified version of the orchestration wrapper:&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;// loading-orchestrator.service.ts&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&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;class&lt;/span&gt; &lt;span class="nc"&gt;LoadingOrchestratorService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;criticalSources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&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;private&lt;/span&gt; &lt;span class="nx"&gt;resolvedSources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&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;private&lt;/span&gt; &lt;span class="nx"&gt;criticalResolved$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;BehaviorSubject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;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="nf"&gt;registerCritical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sourceId&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;criticalSources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sourceId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sourceId&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolvedSources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sourceId&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;allResolved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;criticalSources&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;every&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;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolvedSources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allResolved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;criticalResolved$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&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="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;isCriticalResolved&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&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;boolean&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;criticalResolved$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asObservable&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// page.component.ts&lt;/span&gt;
&lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orchestrator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerCritical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;job-details&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orchestrator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerCritical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;candidates&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orchestrator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isCriticalResolved&lt;/span&gt;&lt;span class="nf"&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;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jobService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jobId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jobDetails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orchestrator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;job-details&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidateService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jobId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orchestrator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;candidates&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;// Secondary -- does not block page fade-in&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;analyticsService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSummary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jobId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;analytics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- page.component.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"page-wrap"&lt;/span&gt; &lt;span class="na"&gt;[class.visible]=&lt;/span&gt;&lt;span class="s"&gt;"pageVisible"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;app-job-details&lt;/span&gt; &lt;span class="na"&gt;[data]=&lt;/span&gt;&lt;span class="s"&gt;"jobDetails"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;app-candidates-table&lt;/span&gt; &lt;span class="na"&gt;[data]=&lt;/span&gt;&lt;span class="s"&gt;"candidates"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;app-analytics&lt;/span&gt; &lt;span class="na"&gt;[data]=&lt;/span&gt;&lt;span class="s"&gt;"analytics"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difficult part is nested components. When a parent is waiting, its children need to know to hold their render. When a parent resolves, children need to cascade in a coordinated way, not fire independently. This is not complex logic -- it is tedious, deliberate, component-by-component discipline. There is no shortcut.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Scroll Flicker Problem
&lt;/h2&gt;

&lt;p&gt;This is the one that took a week just to articulate.&lt;/p&gt;

&lt;p&gt;The layout was: a fixed header at the top, a job details section taking roughly 40% of the page, a candidates table with its own sticky header taking 60%, and a footer. On scroll, the job details section collapsed -- driven by a JS scroll listener -- and the table expanded to fill the space. The problem: the collapse happened via a height snap, which opened a void in the layout, and the sticky table header recalculated its position against the new DOM in a single frame. The user saw it jump.&lt;/p&gt;

&lt;p&gt;The animation below shows exactly what this looked like. Left side: without the fix. Right side: with it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnon4scwrmhxdxsc3iw7h.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnon4scwrmhxdxsc3iw7h.gif" alt="Animated demo showing two side-by-side browser viewports. Left side shows a sticky table header snapping and flashing red as the job details section collapses on scroll, opening a layout void. Right side shows the same scroll with margin compensation absorbing the collapse -- the table header stays stable throughout" width="800" height="431"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Left: sticky header jumps as layout void opens. Right: margin compensation absorbs the collapse -- no jump.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The fix is not a CSS trick. It is a discipline problem.&lt;/p&gt;

&lt;p&gt;The scroll listener had to drive two things simultaneously: the job section collapsing, and the table's &lt;code&gt;margin-top&lt;/code&gt; reducing by exactly the same delta. The margin absorbs the void before it opens. No reflow, no jump. The table header never moves relative to the viewport because the space it occupies never changes.&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;// scroll-coordinator.ts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;JOB_FULL_HEIGHT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;130&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;JOB_MIN_HEIGHT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HostListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;window:scroll&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;onScroll&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;scrollY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollY&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;collapseStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&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;collapseEnd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&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;scrollY&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;collapseStart&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jobHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JOB_FULL_HEIGHT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableMarginTop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scrollY&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;collapseEnd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jobHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JOB_MIN_HEIGHT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableMarginTop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;progress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scrollY&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;collapseStart&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="nx"&gt;collapseEnd&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;collapseStart&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;delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JOB_FULL_HEIGHT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;JOB_MIN_HEIGHT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jobHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JOB_FULL_HEIGHT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// Margin grows as job shrinks -- table never sees a void&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tableMarginTop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;delta&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 key line is &lt;code&gt;this.tableMarginTop = delta&lt;/code&gt;. As the job section gives up height, the table gains margin by exactly the same amount. The two cancel out. From the table's perspective -- and from the user's -- nothing moved.&lt;/p&gt;

&lt;p&gt;This sounds straightforward. Across 12 generic components with different data sources, built by different people at different times, it is not straightforward. You are retrofitting a spatial contract onto a codebase that never had one. Neither the job component nor the table component knows about each other. The page-level orchestration has to know the collapse delta and drive both simultaneously from a single scroll handler. Any timing gap between the two produces a visible jump.&lt;/p&gt;

&lt;p&gt;The articulation took one week. This was &lt;strong&gt;pre-AI&lt;/strong&gt; era. Going through the codebase, reasoning about each component's height behavior, mapping the dependency graph of API calls to visual state -- all of it manual, all of it requiring a mental model rebuilt from scratch.&lt;/p&gt;

&lt;p&gt;The implementation took another week. The demo passed unanimously. Nothing was compromised.&lt;/p&gt;

&lt;p&gt;That is the cost of doing it right, once, on one page, six years into a codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why It Cannot Be Retrofitted at Scale
&lt;/h2&gt;

&lt;p&gt;We had approximately 500 components at this point in the product.&lt;/p&gt;

&lt;p&gt;Applying this discipline across all of them is not a project. It is a rewrite with a different name. Each component would need its height contract audited. Each API call would need to be pulled into the orchestration layer. Every place where Angular's default change detection just assigns and renders would need to be rethought.&lt;/p&gt;

&lt;p&gt;Some of it is not feasible at all. Components built with implicit height assumptions -- where content drives layout -- cannot simply be given explicit dimensions without redesigning the content model. The design team would need to be involved. The product team would need to sign off on the visual changes. Timelines expand immediately.&lt;/p&gt;

&lt;p&gt;This is not a failure of will. It is geometry. The later you implement Material principles, the more surface area they touch, and the more of the product you have to rebuild to support them.&lt;/p&gt;

&lt;p&gt;The ROI argument is real: users tolerate flickering. They have low expectations, especially in enterprise software where the alternative is a spreadsheet. For many products, especially at early stage, accepting that debt is the correct business decision.&lt;/p&gt;

&lt;p&gt;But the debt does not stay flat. It grows with the codebase. And when the ticket finally arrives -- when a user or a stakeholder names it -- the fix is no longer a sprint. It is a quarter, if you are lucky, or a permanent compromise if you are not.&lt;/p&gt;




&lt;h2&gt;
  
  
  It Is Not About Code. It Is About How You Approach.
&lt;/h2&gt;

&lt;p&gt;Google products are not smooth because Google engineers are better at writing JavaScript. They are smooth because the people who wrote the philosophy are embedded in the culture of the people building the product. The spec is not a reference document -- it is a shared mental model. When an engineer at Google makes a decision about loading state, they are not consulting a checklist. They are operating from an internalized answer to "what does the user know right now, and what should the interface tell them?"&lt;/p&gt;

&lt;p&gt;That is what Microsoft had with Windows Phone. Not a better rendering engine. A coherent answer to that question, applied consistently across the product.&lt;/p&gt;

&lt;p&gt;That coherence is what most teams are missing. Not skill. Not resources. Ideology -- and the organizational commitment to treat it as non-negotiable from day one.&lt;/p&gt;

&lt;p&gt;When designers spec a &lt;a href="https://m3.material.io/styles/motion/overview" rel="noopener noreferrer"&gt;transition&lt;/a&gt;, they are not speccing an animation. They are speccing a contract between data state and visual state. When a PM approves a loading state, they are not approving a spinner. They are approving the engineering time to coordinate async data across a component tree so that nothing renders before its space is reserved.&lt;/p&gt;

&lt;p&gt;These are not implementation details. They are architectural decisions. And they need to be made before the first business component is written, not after the first complaint ticket is filed.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Practical Conclusion
&lt;/h2&gt;

&lt;p&gt;If you are starting a new product: establish the spatial and loading contract in your component architecture before you write business logic. Decide how heights are handled. Decide how API orchestration works. Decide what progressive disclosure looks like in your framework. Make it boring infrastructure now, so it is not heroic effort later.&lt;/p&gt;

&lt;p&gt;If you are in an existing codebase: triage ruthlessly. Pick the highest-traffic, highest-complaint flows. Do the sprint. Document the contract. Use it as the template for new components going forward. Do not attempt to retrofit everything -- you will fail and the team will lose confidence in the approach.&lt;/p&gt;

&lt;p&gt;Material is not aspirational. It is correct. The question is only when you pay for it -- at the beginning, when it is cheap, or later, when it costs everything.&lt;/p&gt;

&lt;p&gt;Windows Phone proved you can build something philosophically excellent and still lose the market. That is a business problem. What it also proved is that philosophy-first software feels different in your hands. Users know. They may not be able to name it. But they know.&lt;/p&gt;

&lt;p&gt;Build accordingly.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I work on frontend architecture, performance engineering, and large-scale migrations. More at &lt;a href="https://dev.to/9thquadrant"&gt;dev.to/9thquadrant&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How I Stumbled Into Cloudflare Before It Took Over the Internet (2017)</title>
      <dc:creator>Phaneendra Kanduri</dc:creator>
      <pubDate>Sun, 01 Mar 2026 08:47:15 +0000</pubDate>
      <link>https://dev.to/9thquadrant/how-i-stumbled-into-cloudflare-before-it-took-over-the-internet-2017-5219</link>
      <guid>https://dev.to/9thquadrant/how-i-stumbled-into-cloudflare-before-it-took-over-the-internet-2017-5219</guid>
      <description>&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; WordPress site loading in ~10s. No git. No control. Just pain.&lt;/p&gt;

&lt;h4&gt;
  
  
  I tried the classic frontend interview answer for website optimization:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Moved external JS to end of body&lt;/li&gt;
&lt;li&gt;Moved external CSS to head&lt;/li&gt;
&lt;li&gt;Photoshopped images down to reasonable sizes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; 10s → 9s. Not a win.&lt;/p&gt;

&lt;p&gt;I had no access to WordPress's default JS/CSS bundles. Database queries ran on every page load. I was stuck.&lt;/p&gt;

&lt;h2&gt;
  
  
  Desperation research
&lt;/h2&gt;

&lt;p&gt;I needed to understand &lt;em&gt;how websites actually load&lt;/em&gt;, not just frontend theory. But the full network layer, browser rendering pipeline, and WordPress's request-response cycle.&lt;/p&gt;

&lt;p&gt;StackOverflow felt too high-stakes for what I thought was a basic question. Google and WordPress plugin repositories were my starting point.&lt;/p&gt;

&lt;p&gt;Most WordPress developers reach for a plugin first. I did too. None of them solved the problem.&lt;/p&gt;

&lt;p&gt;Then I found the term: &lt;strong&gt;CDN&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;During this research, I also discovered Automattic, the company behind WordPress. I have a habit of checking parent companies ever since I learned Alphabet owns Google. Finding Automattic made me realize for the first time: there are companies building infrastructure that runs the internet at scale.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Enter The Cloudflare
&lt;/h2&gt;

&lt;p&gt;CDN research surfaced two main options: Akamai and Cloudflare. Cloudflare's free tier and DNS-level integration made it the obvious choice for a small company with no DevOps budget.&lt;/p&gt;

&lt;p&gt;I researched DNS configuration, got approval from my manager for a trial run, changed the DNS records, and waited 24 hours for propagation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; 10s → 0.9s.&lt;/p&gt;

&lt;p&gt;The client emailed us unprompted: &lt;em&gt;"Website is really fast now. Only the chatbot is slow."&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The chatbot had a setTimeout(2000) to let the page load first. I removed it immediately.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It felt like I'd been trying to drain a swimming pool with a bucket until I found the pump.&lt;/p&gt;

&lt;h2&gt;
  
  
  What stuck with me
&lt;/h2&gt;

&lt;p&gt;That 10s → 0.9s improvement taught me that infrastructure solves problems code optimization alone can't touch. It's why I now evaluate CDN strategy, edge caching, and origin offloading as first-order concerns in any performance audit, not afterthoughts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CDNs solve problems frontend optimization alone can't touch&lt;/li&gt;
&lt;li&gt;Sometimes the answer isn't in your code. It's in understanding how infrastructure works at scale&lt;/li&gt;
&lt;li&gt;Cloudflare was already solving the right problems in 2017, long before it became ubiquitous&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>cdn</category>
      <category>frontend</category>
      <category>wordpress</category>
    </item>
    <item>
      <title>Mistakes I Made as a Frontend Engineer (And What They Actually Cost Me)</title>
      <dc:creator>Phaneendra Kanduri</dc:creator>
      <pubDate>Sat, 28 Feb 2026 06:26:08 +0000</pubDate>
      <link>https://dev.to/9thquadrant/mistakes-i-made-as-a-frontend-engineer-and-what-they-actually-cost-me-587m</link>
      <guid>https://dev.to/9thquadrant/mistakes-i-made-as-a-frontend-engineer-and-what-they-actually-cost-me-587m</guid>
      <description>&lt;p&gt;I've shipped a 2s → 0.2s FCP improvement.&lt;br&gt;
I've cut build times from 20 minutes to 90 seconds by making the right framework call. I've led an Angular migration across 500+ components with a 12-engineer team.&lt;/p&gt;

&lt;p&gt;None of that changes the fact that I made avoidable mistakes early in my career.&lt;br&gt;
Some &lt;br&gt;
from ignorance, some from arrogance, some from nobody being around to tell me otherwise.&lt;/p&gt;

&lt;p&gt;This is for junior and mid-level engineers who are still in the window where these &lt;br&gt;
mistakes are correctable.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. I Stayed With jQuery Because It Was Working (2013 - 2015)
&lt;/h2&gt;

&lt;p&gt;In college, I built personal projects with jQuery. It worked. I never had a reason to look further. No one around me, faculty included knew much more than that. So I didn't push.&lt;/p&gt;

&lt;p&gt;That attitude  &lt;em&gt;this is enough, I'm getting results&lt;/em&gt; — followed me into my first job. &lt;br&gt;
When the industry had already moved to Angular and React, I was still thinking in jQuery &lt;br&gt;
terms. I had to learn a component-based mental model on the job, under delivery pressure, &lt;br&gt;
without the luxury of focused learning time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it actually cost:&lt;/strong&gt; Not the job. But the first year was harder than it needed to &lt;br&gt;
be. I was catching up on fundamentals while simultaneously trying to ship features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; Curiosity in college is free. It costs nothing to ask &lt;em&gt;what are teams &lt;br&gt;
actually using in production right now?&lt;/em&gt; That question alone would have changed my &lt;br&gt;
trajectory by 12 months.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. I Ignored TypeScript Until a Production Bug Forced My Hand
&lt;/h2&gt;

&lt;p&gt;Early in my career, TypeScript felt like overhead. I was writing JavaScript, things were &lt;br&gt;
working, and adding types felt like extra ceremony with no visible return.&lt;/p&gt;

&lt;p&gt;Then I passed a wrong type in production. It wasn't a subtle edge case. It was a runtime failure that reached users. That was the moment I stopped telling myself TypeScript was unnecessary overhead.&lt;/p&gt;

&lt;p&gt;What I found after actually learning TypeScript:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refactoring became significantly safer. I could rename, restructure, and move things 
without hunting for every consumer manually.&lt;/li&gt;
&lt;li&gt;Code review got faster because intent was explicit.&lt;/li&gt;
&lt;li&gt;Reusability improved. Properly typed interfaces forced me to think about contracts 
before implementation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; TypeScript isn't ceremony. It's a communication layer between you, your &lt;br&gt;
teammates, and future-you. The overhead is front-loaded. The payoff compounds.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. I Thought DSA Didn't Apply to Frontend
&lt;/h2&gt;

&lt;p&gt;For years, I treated Data Structures and Algorithms as a hiring filter — something you &lt;br&gt;
studied for interviews and forgot immediately after. Frontend is UI, right? State &lt;br&gt;
management, component trees, CSS. What does Big O have to do with any of that?&lt;/p&gt;

&lt;p&gt;Then I hit a real performance problem.&lt;/p&gt;

&lt;p&gt;We had a filtering operation running on a large dataset. The existing code used array &lt;br&gt;
iteration and nested loops for comparisons — the team had written it that way, it worked, &lt;br&gt;
and nobody questioned it. I replaced the comparison logic with a &lt;code&gt;Set&lt;/code&gt;. That single &lt;br&gt;
change gave us a &lt;strong&gt;200ms performance gain&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Users notice 200ms. It's the difference between an interaction feeling instant and feeling &lt;br&gt;
sluggish.&lt;/p&gt;

&lt;p&gt;I also used recursion extensively in production — tree traversal for nested UI structures, &lt;br&gt;
recursive rendering logic, hierarchical data transformations. These aren't academic &lt;br&gt;
exercises. They show up in real frontend work regularly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; You won't use Dijkstra's algorithm in a React component. But you will &lt;br&gt;
absolutely encounter problems where knowing the right data structure is the difference &lt;br&gt;
between a fast product and a slow one. DSA teaches you to &lt;em&gt;think about operations&lt;/em&gt;, not &lt;br&gt;
just write code that works.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. I Hammered Angular's &lt;code&gt;@Input&lt;/code&gt; Listeners Without Thinking About the Cost
&lt;/h2&gt;

&lt;p&gt;In Angular, &lt;code&gt;@Input&lt;/code&gt; with &lt;code&gt;ngOnChanges&lt;/code&gt; is the obvious way to react to parent-to-child &lt;br&gt;
data flow. I used it heavily. It was the path of least resistance, and for a while, &lt;br&gt;
nothing visibly broke.&lt;/p&gt;

&lt;p&gt;Then we hit scale.&lt;/p&gt;

&lt;p&gt;The symptoms came gradually, then all at once: keystrokes appearing in bursts after a &lt;br&gt;
delay, dropdowns not opening, the sidebar freezing mid-interaction, the browser throwing &lt;br&gt;
a page crash dialog asking users to either wait or exit. Users couldn't reliably interact &lt;br&gt;
with the product.&lt;/p&gt;

&lt;p&gt;The root cause: too many change detection cycles firing simultaneously across too many &lt;br&gt;
components, all triggered by input changes cascading through the tree.&lt;/p&gt;

&lt;p&gt;The fix required going back and auditing the architecture — determining what actually &lt;br&gt;
needed to react to changes versus what was reacting unnecessarily. It was migration work &lt;br&gt;
that shouldn't have been necessary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; &lt;code&gt;ngOnChanges&lt;/code&gt; and &lt;code&gt;@Input&lt;/code&gt; listeners are not free. Every one you add is &lt;br&gt;
a hook into Angular's change detection. At component scale, this compounds. Before wiring &lt;br&gt;
up an input listener, ask: does this component actually need to re-evaluate on every &lt;br&gt;
parent change, or can this be derived, memoized, or handled further up the tree?&lt;/p&gt;




&lt;h2&gt;
  
  
  5. I Overused Directives and Created a Click Listener Explosion
&lt;/h2&gt;

&lt;p&gt;Directives in Angular are powerful. I used them extensively for shared behavior, which &lt;br&gt;
is correct in principle. The mistake was not thinking about what happens when a directive &lt;br&gt;
is instantiated hundreds of times across a large application.&lt;/p&gt;

&lt;p&gt;I had a directive that listened to click events. It was applied broadly. Each instance &lt;br&gt;
created its own event listener. On a screen with many instances, we had hundreds of &lt;br&gt;
active click listeners running simultaneously.&lt;/p&gt;

&lt;p&gt;The user-visible symptom: a simple search bar — no search-as-you-type, just a standard &lt;br&gt;
Enter key to trigger an API call — became unresponsive. Users couldn't type. Keystrokes &lt;br&gt;
were being swallowed. What should have been a trivial interaction was broken by listener &lt;br&gt;
saturation.&lt;/p&gt;

&lt;p&gt;The fix: replace the distributed listener pattern with a single event source in a &lt;br&gt;
service, broadcast via an observable. One listener. One source of truth. The UI became &lt;br&gt;
responsive immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; When a directive handles DOM events and gets used at scale, you don't &lt;br&gt;
have one listener — you have N listeners. Before writing event-handling logic into a &lt;br&gt;
directive, ask whether a centralized service with a single subscription would serve the &lt;br&gt;
same purpose with a fraction of the overhead.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. I Refactored Code That Wasn't My Ticket — And I'd Do It Again
&lt;/h2&gt;

&lt;p&gt;This one is more complicated.&lt;/p&gt;

&lt;p&gt;Management's view: refactoring work you weren't assigned is wasted time. It's scope &lt;br&gt;
creep. It's not in the sprint.&lt;/p&gt;

&lt;p&gt;My experience over four years: I refactored proactively whenever I saw something &lt;br&gt;
structurally wrong — &lt;code&gt;float&lt;/code&gt;-based layouts that should have been flexbox, JS-driven &lt;br&gt;
dropdown state that should have been CSS &lt;code&gt;focus-within&lt;/code&gt;, array comparisons that should &lt;br&gt;
have been &lt;code&gt;Set&lt;/code&gt; lookups. Nobody asked me to. I just did it.&lt;/p&gt;

&lt;p&gt;This happened &lt;strong&gt;seven times in four years&lt;/strong&gt;. And in every single case, within the next &lt;br&gt;
sprint, I had a feature to build on that exact component.&lt;/p&gt;

&lt;p&gt;The float→flexbox refactors were the clearest example. A CSS float layout is brittle. &lt;br&gt;
It breaks under theme changes, under translation string length variation, under responsive &lt;br&gt;
breakpoints. When I was handed a feature that required layout changes to those components, &lt;br&gt;
my prior refactor meant I was working with a clean, predictable structure instead of &lt;br&gt;
fighting the existing one. What would have taken 3 days was done in a day.&lt;/p&gt;

&lt;p&gt;The opportunistic refactoring wasn't wasted time — it was &lt;strong&gt;pre-paid time&lt;/strong&gt;. The sprint &lt;br&gt;
where I refactored looked inefficient. The sprint where the feature landed showed the &lt;br&gt;
return.&lt;/p&gt;

&lt;p&gt;Replacing JS-based click listeners on dropdowns with CSS &lt;code&gt;focus-within&lt;/code&gt; also reduced the &lt;br&gt;
event listener footprint across the application — the same class of problem described &lt;br&gt;
in mistake 5, resolved structurally rather than reactively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; Proactive refactoring is a bet. You're spending time now against &lt;br&gt;
uncertain future return. In my experience, if you have enough system knowledge to &lt;br&gt;
&lt;em&gt;know&lt;/em&gt; a component is going to be touched again soon, it's usually a good bet. The &lt;br&gt;
mistake is doing it indiscriminately. The skill is doing it with judgment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;Most of these mistakes share a root: I optimized for &lt;em&gt;working now&lt;/em&gt; without thinking &lt;br&gt;
about &lt;em&gt;cost at scale&lt;/em&gt;. jQuery worked. &lt;code&gt;@Input&lt;/code&gt; listeners worked. Directives worked. &lt;br&gt;
Array loops worked.&lt;/p&gt;

&lt;p&gt;Until they didn't.&lt;/p&gt;

&lt;p&gt;Frontend engineering at scale is not about making things work. It's about understanding &lt;br&gt;
what you're trading when you make a choice — and being honest about when that trade &lt;br&gt;
stops being worth it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;These mistakes are what actually make you an engineer, not just someone who writes code.&lt;br&gt;
The engineers AI can't replace are the ones who've already made them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hopefully this is that someone for you.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>angular</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>An Engineer's Practical Manual for Using AI Code Assistants</title>
      <dc:creator>Phaneendra Kanduri</dc:creator>
      <pubDate>Tue, 24 Feb 2026 10:01:21 +0000</pubDate>
      <link>https://dev.to/9thquadrant/an-engineers-practical-manual-for-using-ai-code-assistants-5pd</link>
      <guid>https://dev.to/9thquadrant/an-engineers-practical-manual-for-using-ai-code-assistants-5pd</guid>
      <description>&lt;p&gt;AI code assistants are everywhere now. But most engineers either treat them like magic oracles or ignore them entirely. Neither approach works.&lt;/p&gt;

&lt;p&gt;Here's a practical manual for getting actual value from AI tools without letting them wreck your codebase.&lt;/p&gt;




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

&lt;ol&gt;
&lt;li&gt;Treat AI like a smart intern who is good at execution, bad at judgment&lt;/li&gt;
&lt;li&gt;Set your communication style upfront in custom instructions&lt;/li&gt;
&lt;li&gt;Define non-negotiables AI must remember (tech stack, conventions, compliance)&lt;/li&gt;
&lt;li&gt;End every task with "Ask questions if you have gaps"&lt;/li&gt;
&lt;li&gt;For long projects, ask AI to document progress in a file&lt;/li&gt;
&lt;li&gt;Monitor actively. AI will go out of bounds&lt;/li&gt;
&lt;li&gt;Double-check everything, twice again before shipping&lt;/li&gt;
&lt;li&gt;AI accelerates scaffolding, doesn't replace decision-making&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Treat AI Like a Smart, Overenthusiastic Intern
&lt;/h2&gt;

&lt;p&gt;This is the most important mental model.&lt;/p&gt;

&lt;p&gt;AI tools are &lt;strong&gt;not&lt;/strong&gt; senior engineers. They don't understand system constraints, political context, or why a "simple refactor" will break 6 downstream consumers. But they're excellent at scaffolding, repetitive transforms, and generating first drafts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this means in practice:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You still own the architecture&lt;/li&gt;
&lt;li&gt;You still own the tradeoffs&lt;/li&gt;
&lt;li&gt;AI handles the grunt work: boilerplate, repetitive edits, documentation templates, test scaffolding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example: During a platform migration, AI scaffolded 200+ Components files and saved ~40 hours of manual work. But the engineer still designed the directory structure, schema, and migration sequence.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Set Your Communication Style Upfront
&lt;/h2&gt;

&lt;p&gt;AI adapts to how you speak to it. If you're vague, you'll get vague output. If you're direct, you'll get direct output.&lt;/p&gt;

&lt;p&gt;Set custom instructions once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Direct, structured, blunt. No filler, no apologies.
Flag gaps in my request if they exist.
When code has edge cases I didn't mention, list them before generating.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This cuts back-and-forth by ~50%. Instead of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Sure! I'd be happy to help. Let me create a component for you. Would you like me to include props for styling?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You get:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Missing: error state handling, loading state, TypeScript types for &lt;code&gt;onSubmit&lt;/code&gt;. Proceeding with basic implementation."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Set the tone once. Enforce it consistently.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Define Non-Negotiables That AI Must Remember
&lt;/h2&gt;

&lt;p&gt;Some context applies to every task. Don't repeat yourself, encode it once.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Always use functional components with TypeScript. No class components."&lt;/li&gt;
&lt;li&gt;"Accessibility is non-negotiable. Include ARIA labels and keyboard nav by default."&lt;/li&gt;
&lt;li&gt;"When writing tests, use Enzyme, not React Testing Library"&lt;/li&gt;
&lt;li&gt;"Performance matters. Flag unnecessary re-renders or heavy computations."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This prevents AI from generating outdated patterns or ignoring your team's conventions.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Always End Tasks With: "Ask Questions If You Have Gaps"
&lt;/h2&gt;

&lt;p&gt;AI will confidently generate wrong code if you don't give it permission to push back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Build a dropdown component with keyboard navigation."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Better prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Build a dropdown component with keyboard navigation (arrow keys, Enter, Escape). Must be WCAG 2.1 AA compliant. Ask questions if you need clarification on interaction patterns or edge cases."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second version produces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Should focus trap inside the dropdown when open?"&lt;/li&gt;
&lt;li&gt;"Should Escape close and return focus to the trigger?"&lt;/li&gt;
&lt;li&gt;"Should arrow keys wrap at list boundaries?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you're reviewing a solution that handles real-world behavior, not a demo-quality stub.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. For Multi-Week Projects: Make AI Document Progress
&lt;/h2&gt;

&lt;p&gt;When working on long-running tasks (e.g., framework migrations, large refactors), ask AI to maintain a progress log.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt template:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"We're upgrading our framework. This will take 3 weeks. After each session, update &lt;code&gt;PROGRESS_LOG.md&lt;/code&gt; with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What we completed today&lt;/li&gt;
&lt;li&gt;Blockers encountered&lt;/li&gt;
&lt;li&gt;Next steps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When I return, I should be able to read the log and resume without context loss."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Example log entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## 2024-01-15: Upgraded core packages&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Completed: Core packages updated to latest version
&lt;span class="p"&gt;-&lt;/span&gt; Blockers: 14 components using deprecated APIs
&lt;span class="p"&gt;-&lt;/span&gt; Next: Migrate deprecated API usage
&lt;span class="p"&gt;-&lt;/span&gt; Estimate: 2 days for API migration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This is critical.&lt;/strong&gt; AI has no memory between sessions. If you don't externalize progress, you're starting from scratch every time.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. AI Will Go Out of Bounds. Always Monitor Actively
&lt;/h2&gt;

&lt;p&gt;AI optimizes for "solving the prompt," not "solving the right problem."&lt;/p&gt;

&lt;p&gt;Real example:&lt;/p&gt;

&lt;p&gt;An engineer asked AI to optimize a Python script. It rewrote the entire script using a different parsing library, which:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Broke compatibility with existing plugins&lt;/li&gt;
&lt;li&gt;Introduced a new dependency&lt;/li&gt;
&lt;li&gt;Required changes to CI/CD&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI's solution was faster, but architecturally wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to prevent this:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set constraints upfront: "Do not introduce new dependencies without asking."&lt;/li&gt;
&lt;li&gt;Review diffs before running code&lt;/li&gt;
&lt;li&gt;Test in isolated environments first&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI will go off-script. Your job is to catch it before it hits production.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Double-Check Everything. Then Twice Again.
&lt;/h2&gt;

&lt;p&gt;AI-generated code is &lt;strong&gt;never&lt;/strong&gt; production-ready on the first pass.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Standard review checklist:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does it handle edge cases? (Empty arrays, null values, network errors)&lt;/li&gt;
&lt;li&gt;Is it accessible? (Keyboard nav, ARIA, focus management)&lt;/li&gt;
&lt;li&gt;Is it performant? (Unnecessary re-renders, large bundle size)&lt;/li&gt;
&lt;li&gt;Does it follow team conventions? (Naming, file structure, test patterns)&lt;/li&gt;
&lt;li&gt;Will it break in 6 months? (Deprecated APIs, hardcoded assumptions)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Real scenario: AI generated GraphQL queries that worked but over-fetched data, pulling entire content trees when only leaf nodes were needed.&lt;/p&gt;

&lt;p&gt;Fixing this required:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Understanding the schema&lt;/li&gt;
&lt;li&gt;Knowing the API's performance characteristics&lt;/li&gt;
&lt;li&gt;Rewriting queries with proper field and depth limits&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;AI got 70% of the way there. The last 30% required domain knowledge.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. AI Accelerates Scaffolding. It Doesn't Replace Decision-Making
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Where AI excels:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Migrations: Generated files, parsers, layouts → saved ~40 hours&lt;/li&gt;
&lt;li&gt;PWA setup: Service worker boilerplate, cache strategies → saved ~8 hours&lt;/li&gt;
&lt;li&gt;Accessibility audits: Scanned for missing ARIA labels, generated fixes → saved ~12 hours&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Where AI fails and humans must step in:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deciding between micro-frontends vs. monolith (architectural tradeoff)&lt;/li&gt;
&lt;li&gt;Evaluating framework options (performance + DX tradeoff)&lt;/li&gt;
&lt;li&gt;Negotiating with designers on performance vs. aesthetics (subjective judgment)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What AI Is Actually Good For
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Excellent:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Boilerplate generation (repos, components, configs)&lt;/li&gt;
&lt;li&gt;Repetitive transforms (file renames, import updates, data migrations)&lt;/li&gt;
&lt;li&gt;Documentation (README templates, API docs, guides)&lt;/li&gt;
&lt;li&gt;Test scaffolding (basic unit test structure, mock setup)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mediocre:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complex refactors (high risk of breaking changes)&lt;/li&gt;
&lt;li&gt;Performance optimization (requires profiling and domain knowledge)&lt;/li&gt;
&lt;li&gt;Debugging (suggests fixes, but often misses root cause)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Bad:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Architecture decisions (tradeoffs require human judgment)&lt;/li&gt;
&lt;li&gt;Security-critical code (high stakes, low tolerance for error)&lt;/li&gt;
&lt;li&gt;Organizational dynamics (AI doesn't understand politics or people)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Take
&lt;/h2&gt;

&lt;p&gt;AI tools are &lt;strong&gt;productivity multipliers&lt;/strong&gt;, not replacements.&lt;/p&gt;

&lt;p&gt;They can cut scaffolding time by ~60%. They eliminate entire classes of grunt work. But they haven't changed the fact that &lt;strong&gt;someone still needs to know what to build, why to build it, and how to evaluate if it's correct.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That someone is you.&lt;/p&gt;

&lt;p&gt;Use AI to move faster. But never stop thinking.&lt;/p&gt;




&lt;p&gt;What's your experience with AI code assistants? What patterns have worked (or failed spectacularly) for you?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>softwareengineering</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Your Icon Strategy Is a Hidden Tech Debt Bomb</title>
      <dc:creator>Phaneendra Kanduri</dc:creator>
      <pubDate>Fri, 20 Feb 2026 07:33:33 +0000</pubDate>
      <link>https://dev.to/9thquadrant/your-icon-strategy-is-a-hidden-tech-debt-bomb-2b6n</link>
      <guid>https://dev.to/9thquadrant/your-icon-strategy-is-a-hidden-tech-debt-bomb-2b6n</guid>
      <description>&lt;p&gt;&lt;em&gt;Three approaches to scaling icons across hundreds of components. Two of them will cost you.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Icons Are Never Just Icons
&lt;/h2&gt;

&lt;p&gt;Icons feel like a solved problem. Pick a library, drop them in, move on.&lt;/p&gt;

&lt;p&gt;That works fine at 10 components. At 100 components, you start feeling friction. At 500 components with 100 icons across multiple feature modules, your icon strategy is either invisible infrastructure or a daily source of pain.&lt;/p&gt;

&lt;p&gt;Most teams don't make a deliberate icon decision. They inherit one. And by the time the problems show up, the icons are everywhere.&lt;/p&gt;

&lt;p&gt;This article covers three approaches, what breaks in each, and which one actually holds up in a large enterprise SaaS context.&lt;/p&gt;




&lt;h2&gt;
  
  
  Approach 1: Font-Based Icons
&lt;/h2&gt;

&lt;p&gt;This is the most common starting point. Libraries like Font Awesome, Material Icons, and Google's own icon font work by mapping icon names to glyphs inside a custom font file. You reference them by class name or by writing the icon name as text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"material-icons"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;save&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It feels clean. One CDN link, infinite icons, no SVG anywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where It Breaks
&lt;/h3&gt;

&lt;p&gt;Font icons are network-dependent by design. The font file has to load before any icon renders.&lt;/p&gt;

&lt;p&gt;I've seen this fail on Google's own properties. When the font hasn't loaded yet, the icon name renders as plain text. A button meant to show a save icon shows the word "save" instead. On a slow connection or a constrained device, this isn't an edge case — it's the experience.&lt;/p&gt;

&lt;p&gt;For a public-facing marketing page, that might be acceptable. For an enterprise SaaS product used daily by thousands of users, it is not. Your users will see broken UI. And it will happen precisely when their network or device is already under stress.&lt;/p&gt;

&lt;p&gt;Beyond rendering, font icons carry another problem: you load the entire font file even if you use 12 icons. You cannot tree-shake a font. Every icon the library ships is in the bundle whether your product uses it or not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict: Rules itself out for enterprise. Network dependency and no tree-shaking are disqualifying at scale.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Approach 2: Inline SVG
&lt;/h2&gt;

&lt;p&gt;The natural response to font icon failures is to go directly to SVG. SVGs are vectors, they render perfectly at any size, they don't depend on a font file, and they're just markup.&lt;/p&gt;

&lt;p&gt;So you paste the SVG directly into your component template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"24"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"#333333"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon-primary"&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M17 3H5a2 2 ..."&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works. Until it doesn't.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where It Breaks
&lt;/h3&gt;

&lt;p&gt;The first problem is duplication. If the save icon appears in 15 components, the SVG markup is copy-pasted into 15 files. When the design team updates the icon — corrects a path, adjusts stroke width, updates the color — you're hunting across 15 files to apply the change. Miss one and you have an inconsistency that will take weeks to notice.&lt;/p&gt;

&lt;p&gt;The second problem is that colors and classes are often hardcoded inside the SVG markup itself. &lt;code&gt;fill="#333333"&lt;/code&gt; is not a variable. It's a value. Changing it requires touching every instance. Creating a variant — same icon, different color for a different state — means another copy.&lt;/p&gt;

&lt;p&gt;The third problem is migration. When this pattern has been in use for a year across a large codebase, moving away from it is not a refactor. It's an excavation. Every file that inlined SVG needs to be found, assessed, and updated individually.&lt;/p&gt;

&lt;p&gt;I tried this approach. The duplication was immediate and obvious. The design team updated an icon and the inconsistency spread faster than we could track it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verdict: Acceptable for small projects with few icons. Breaks down fast at scale. Migration cost is high.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Approach 3: SVG as Standalone Components
&lt;/h2&gt;

&lt;p&gt;This is the approach that holds up.&lt;/p&gt;

&lt;p&gt;Each icon is its own component. The SVG lives inside that component. Props control the variants — size, color, stroke, anything that needs to change between usages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SaveIcon component (framework-agnostic concept)&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IconProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;size&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;color&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;strokeWidth&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="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Template / JSX&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;
  &lt;span class="na"&gt;xmlns&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;
  &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"size"&lt;/span&gt;
  &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"size"&lt;/span&gt;
  &lt;span class="na"&gt;viewBox&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;attr&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="err"&gt;]&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"color"&lt;/span&gt; &lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"M17 3H5a2 2 ..."&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every consumer of this icon imports the component and passes props. No SVG markup in the consumer's file. No duplication.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Consumer&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SaveIcon&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"var(--icon-primary)"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why This Wins at Scale
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Maintainability.&lt;/strong&gt; When the design team updates the save icon, you update one file. One component. Every consumer gets the update automatically. No hunting, no inconsistency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Variants without duplication.&lt;/strong&gt; Need the same icon in a disabled state, a hover state, an error state? Pass a prop. Don't copy the file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bundle control.&lt;/strong&gt; Each icon is a discrete module. If a feature only needs 10 icons, it imports 10 components. Nothing else ships with it. When I ran a module visualizer on the codebase after this migration, I could see exactly which icons each module imported — and nothing extra. Around 100 icons across a 500-component codebase, with each module importing only what it actually used.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Migration path.&lt;/strong&gt; Because each icon is a named, importable component, replacing or updating the underlying SVG is a single-file change. Renaming or deprecating an icon is trackable through standard import analysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design system alignment.&lt;/strong&gt; Icon components sit naturally inside a shared component library or design system. They're versioned, documented, and owned — not scattered across feature folders as copy-pasted markup.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Trade-off
&lt;/h3&gt;

&lt;p&gt;Initial setup takes longer. You're creating a component per icon, not dropping in a CDN link. For a library of 100 icons, that's a one-time investment that pays back within the first few design updates.&lt;/p&gt;

&lt;p&gt;If you're on a two-week spike with a throwaway prototype, this is overkill. For anything that will be maintained past the first quarter, it is the only approach that doesn't accumulate debt.&lt;/p&gt;




&lt;h2&gt;
  
  
  Decision Guide
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Recommended Approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Quick prototype, throwaway project&lt;/td&gt;
&lt;td&gt;Font icons or inline SVG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small product, &amp;lt;20 icons, stable design&lt;/td&gt;
&lt;td&gt;Inline SVG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise SaaS, 50+ icons, active design iteration&lt;/td&gt;
&lt;td&gt;SVG components&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shared design system across multiple products&lt;/td&gt;
&lt;td&gt;SVG components, non-negotiable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The Pattern That Scales
&lt;/h2&gt;

&lt;p&gt;Font icons trade render reliability for convenience. Inline SVG trades maintainability for simplicity. SVG components trade setup time for everything that matters after day one.&lt;/p&gt;

&lt;p&gt;In a large B2B SaaS environment — multiple feature modules, active design iteration, a team that rotates — the icon strategy compounds quietly in the background. The wrong choice doesn't hurt immediately. It hurts when the design team updates a core icon and you realize it lives in 40 files. It hurts when a new engineer can't tell where an icon comes from or how to create a variant without duplicating it.&lt;/p&gt;

&lt;p&gt;The component approach is more setup upfront. It is less everything else, forever.&lt;/p&gt;

&lt;p&gt;Make the decision deliberately. Most teams don't, and they pay for it later.&lt;/p&gt;




</description>
      <category>frontend</category>
      <category>architecture</category>
      <category>webdev</category>
      <category>designsystem</category>
    </item>
    <item>
      <title>4 Years of Dual-Stacking: The High Cost of React’s Flexibility</title>
      <dc:creator>Phaneendra Kanduri</dc:creator>
      <pubDate>Thu, 19 Feb 2026 09:49:29 +0000</pubDate>
      <link>https://dev.to/9thquadrant/ive-used-react-and-angular-side-by-side-for-4-years-heres-why-im-still-biased-toward-angular-29jl</link>
      <guid>https://dev.to/9thquadrant/ive-used-react-and-angular-side-by-side-for-4-years-heres-why-im-still-biased-toward-angular-29jl</guid>
      <description>&lt;p&gt;&lt;em&gt;Not a framework war. A working engineer's honest accounting.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;I spent four years at Phenom (a B2B HR tech company) working on three concurrent frontend contexts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A large internal product built in Angular (v9 → v15, ~500 components, 5,000+ active users)&lt;/li&gt;
&lt;li&gt;A Pilot product started in React, with the stated USP of eventual React Native migration&lt;/li&gt;
&lt;li&gt;A Chrome extension that injected an iframe and loaded a React application inside it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn't a theoretical comparison. I lived in both codebases every week for nearly four years. The React product never launched as an app — it shipped as a responsive website instead. The Angular product is still running.&lt;/p&gt;

&lt;p&gt;I'm biased toward Angular. I know it. This article explains why, and I'll let you decide if the bias is earned.&lt;/p&gt;




&lt;h2&gt;
  
  
  What React Actually Gives You
&lt;/h2&gt;

&lt;p&gt;React is not a framework. It's a UI library. That distinction sounds academic until you're six months into a team of eight and you realize no two engineers made the same architectural call.&lt;/p&gt;

&lt;p&gt;In the Pilot React product I worked on, the early team had to make decisions that Angular would have made for them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;State management: Redux? Zustand? Context? Jotai? Each had advocates.&lt;/li&gt;
&lt;li&gt;Routing: React Router v5 → v6 introduced breaking changes mid-project.&lt;/li&gt;
&lt;li&gt;Styling: styled-components vs. SCSS modules, tailwind? never fully resolved.&lt;/li&gt;
&lt;li&gt;File structure: flat, feature-based, domain-driven, three opinions, no standard.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I joined when the product had 5 components in wireframe. By the time I transitioned out, it had approximately 200 components. The architectural debt from those early unconstrained choices compounded with every component added.&lt;/p&gt;

&lt;p&gt;React gives you freedom. Freedom without structure, at scale, becomes liability.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Angular Actually Forces You To Do
&lt;/h2&gt;

&lt;p&gt;Angular forces structure before you write a single line of business logic.&lt;/p&gt;

&lt;p&gt;Every feature has a dedicated template, stylesheet, component class, service, and route configuration. There are interceptors for HTTP. There are guards for navigation. There's a module system, and now, standalone components which makes you think in boundaries, not just files.&lt;/p&gt;

&lt;p&gt;When I led the Angular upgrade from v9 → v12, and later identified standalone components in v15 as an architectural improvement and made the business case to leadership - the migration was significant but tractable. ~500 components. A team of 12–15 engineers. The framework gave us enough shared language that we could execute it without rebuilding the mental model.&lt;/p&gt;

&lt;p&gt;That would have been a different story in React. A comparable refactor migrating from one state management library to another which has no prescribed path. Every team invents its own.&lt;/p&gt;

&lt;p&gt;Angular &lt;a href="https://angular.dev/guide/signals" rel="noopener noreferrer"&gt;Signals&lt;/a&gt;, introduced in recent versions, close the last real gap React had in reactivity ergonomics. There is no longer a compelling reactive programming argument for React over Angular in enterprise contexts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dependency Injection Is Not About Testability. It's About Time.
&lt;/h2&gt;

&lt;p&gt;Everyone says Angular DI makes testing easier. That's true, but it's the wrong headline.&lt;/p&gt;

&lt;p&gt;The real payoff shows up three years later, when someone maybe you, maybe not opens a file they didn't write.&lt;/p&gt;

&lt;p&gt;Compare these two lines doing the same thing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Angular:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;someFunction&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;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dependencyService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validateThisService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&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="nx"&gt;c&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;React:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;someFunction&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;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validateThisService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&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="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A React developer will say: just click through to the import. You'll see where it's from.&lt;/p&gt;

&lt;p&gt;That's fair. If you're in the file for the first time today, in a familiar codebase, with good IDE support.&lt;/p&gt;

&lt;p&gt;But visit that line 3 years later, in a file you didn't write, in a codebase that's rotated engineers twice. Now &lt;code&gt;validateThisService&lt;/code&gt; could be a local utility, a shared library function, a barrel export from a utils folder, or something that's been copy-pasted and diverged.&lt;/p&gt;

&lt;p&gt;You have to trace it.&lt;/p&gt;

&lt;p&gt;In Angular, &lt;code&gt;dependencyService.validateThisService(a)&lt;/code&gt; tells you something before you click anything. There is a service. It owns this validation. If you want to know what else that service does, it's one tab. If you want to know where it's injected from, it's the constructor. The code has a return address.&lt;/p&gt;

&lt;p&gt;The function call in React has no return address. It's floating. It could be from anywhere. And in a 200-component codebase with three engineers who made different structural choices — it often is.&lt;/p&gt;

&lt;p&gt;This is not a readability debate.&lt;/p&gt;

&lt;p&gt;This is the difference between code that documents its own dependencies and code that delegates that job to whoever's reading it at the worst possible time.&lt;/p&gt;

&lt;p&gt;Adults write code that explains itself after they've left the building.&lt;/p&gt;




&lt;h2&gt;
  
  
  Internationalization Is an Afterthought Everywhere Except Angular
&lt;/h2&gt;

&lt;p&gt;Angular ships with &lt;code&gt;@angular/localize&lt;/code&gt;. One package. Built by the same team that built the framework. No third-party dependency to evaluate, no community split between competing libraries.&lt;/p&gt;

&lt;p&gt;The translation workflow is straightforward: mark strings, extract them, hand off one file to your translators, get it back, done.&lt;/p&gt;

&lt;p&gt;When I delivered multilingual features for Fortune 500 EU clients, the translation file was the artifact. Not a collection of JSON files spread across feature folders. Not a barrel export someone assembled. One file with a clear structure that a non-engineer translator could open and work with.&lt;/p&gt;

&lt;p&gt;That's the adult version of internationalization. It doesn't make you feel clever. It just works, every time, the same way.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Manager Observation
&lt;/h2&gt;

&lt;p&gt;This is the part where I'll lose some readers, but it tracks with my experience.&lt;/p&gt;

&lt;p&gt;Technical managers who have shipped and maintained large frontend codebases over 3+ years tend to reach for Angular. Not because it's trendy — because they've felt the cost of the alternative.&lt;/p&gt;

&lt;p&gt;Managers who have moved through roles quickly, prioritizing hiring speed and ecosystem breadth, tend to reach for React. The React ecosystem is wide. That width reads as optionality. But optionality without constraint is just deferred decision-making — and someone downstream pays for it.&lt;/p&gt;

&lt;p&gt;This is not an attack on React engineers. It's an observation about what happens when framework choice is driven by hiring market size rather than long-term maintainability.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Chrome Extension Context
&lt;/h2&gt;

&lt;p&gt;The Chrome extension was React. Injecting an iframe into third-party pages and loading a full React application inside it. This context actually suited React well: isolated scope, small surface area, no long-running state across sessions. Framework overhead didn't compound the way it does in a 500-component product.&lt;/p&gt;

&lt;p&gt;This is the honest answer: React is not wrong. It's often wrong for the context it gets chosen for.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Actual Position
&lt;/h2&gt;

&lt;p&gt;Angular is boring. It's verbose. It has a learning curve that filters out engineers who won't invest in understanding it. It is not exciting to maintain. It is not flexible. It will not let your team build whatever they want however they want.&lt;/p&gt;

&lt;p&gt;That is exactly why I use it for serious work.&lt;/p&gt;

&lt;p&gt;The frontend ecosystem rewards novelty. Angular rewards discipline. For enterprise, long-running, high-user-count applications — I have not yet seen a credible argument that the tradeoffs favor React.&lt;/p&gt;

&lt;p&gt;If you're building a startup MVP with a team of two and a 6-month runway, use whatever ships fastest. But if you're building something that will be maintained by 10+ engineers across 4+ years, the structure Angular enforces is not overhead, it's insurance.&lt;/p&gt;

&lt;p&gt;I'm biased. But I've done the work that earns the bias.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tags: &lt;code&gt;angular&lt;/code&gt; &lt;code&gt;react&lt;/code&gt; &lt;code&gt;frontend&lt;/code&gt; &lt;code&gt;architecture&lt;/code&gt; &lt;code&gt;webdev&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>discuss</category>
      <category>frontend</category>
      <category>react</category>
    </item>
    <item>
      <title>From 2s to 0.2s: Why Astro is the New Gold Standard for Marketing Sites</title>
      <dc:creator>Phaneendra Kanduri</dc:creator>
      <pubDate>Wed, 18 Feb 2026 09:43:04 +0000</pubDate>
      <link>https://dev.to/9thquadrant/why-i-chose-astro-over-nextjs-for-a-marketing-site-and-cut-fcp-from-2s-to-02s-2o3e</link>
      <guid>https://dev.to/9thquadrant/why-i-chose-astro-over-nextjs-for-a-marketing-site-and-cut-fcp-from-2s-to-02s-2o3e</guid>
      <description>&lt;p&gt;Some of the best architectural decisions I've made never went to production. This is one of them.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Breaking Point
&lt;/h2&gt;

&lt;p&gt;We were running a high-traffic marketing site - heavy A/B testing, dynamic banners, offers, primarily organic traffic from email opens. The kind of site where first impressions are everything and every second of load time has a direct business cost.&lt;/p&gt;

&lt;p&gt;Our stack was Gatsby. And it was falling apart.&lt;/p&gt;

&lt;p&gt;Build times were getting out of hand. Load times were degrading. Then Netlify acquired Gatsby and support effectively stalled. We also started hitting Netlify downtime and third-party integration blockers that compounded an already fragile setup. The site was expensive to maintain and getting slower.&lt;/p&gt;

&lt;p&gt;Leadership assigned me to research a new stack. The brief was open - evaluate options, build POCs, present findings.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Room Had Already Decided
&lt;/h2&gt;

&lt;p&gt;The moment it was brought up in standup, the team rallied around Next.js. Everyone had experience with it. Everyone had opinions on why it would work. It was the obvious choice.&lt;/p&gt;

&lt;p&gt;I understood why. Next.js is a mature, well-supported framework with a strong ecosystem. For most use cases, it's a reasonable default.&lt;/p&gt;

&lt;p&gt;But I wasn't there to validate consensus. I was there to find the right answer for the product.&lt;/p&gt;

&lt;p&gt;So I ran the Next.js POC anyway - properly, without shortcuts. The developer experience was good. The ecosystem was solid. And then I looked at the output.&lt;/p&gt;

&lt;p&gt;Non-interactive components were being hydrated. Common JS bundles were being shipped to the client unnecessarily. The thread blocking time was higher than it needed to be for a site where most components never respond to user input. For an application, this is an acceptable tradeoff. For a marketing site built to load fast and convert, it's the wrong architecture.&lt;/p&gt;

&lt;p&gt;The framework was technically sound. It was the wrong tool for what the product actually needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Finding Astro
&lt;/h2&gt;

&lt;p&gt;I kept researching. Astro had been gaining traction with a specific promise: ship zero JS by default, hydrate only what needs to be interactive. For a marketing site, that's not a feature - that's the entire architectural requirement.&lt;/p&gt;

&lt;p&gt;I built the POC. Same content, same structure, equivalent functionality.&lt;/p&gt;

&lt;p&gt;The numbers were not close.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Gatsby (Production)&lt;/th&gt;
&lt;th&gt;Next.js POC&lt;/th&gt;
&lt;th&gt;Astro POC&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FCP&lt;/td&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;td&gt;~1.2s&lt;/td&gt;
&lt;td&gt;0.2s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LCP&lt;/td&gt;
&lt;td&gt;10s&lt;/td&gt;
&lt;td&gt;~5s&lt;/td&gt;
&lt;td&gt;2s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;FCP dropped 90%. LCP dropped 80%. These weren't micro-optimizations - they were the direct result of not shipping unnecessary JavaScript to a page that didn't need it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where AI Fit In
&lt;/h2&gt;

&lt;p&gt;I used AI heavily during the POC phase - wireframes, component scaffolding, boilerplate generation. It compressed weeks of setup into days.&lt;/p&gt;

&lt;p&gt;But AI didn't make the architectural call. It couldn't reason about why a marketing site with non-interactive components has no business hydrating them. It couldn't weigh the tradeoff between developer familiarity and production performance. It couldn't recognize that the team's instinct toward Next.js was a comfort zone decision, not a product decision.&lt;/p&gt;

&lt;p&gt;That judgment of understanding what the product actually needed and selecting the tool that served it. That's where the human work was.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Happened After
&lt;/h2&gt;

&lt;p&gt;I presented both POCs to leadership with full benchmarks. The Astro numbers were clear. The recommendation was clear.&lt;/p&gt;

&lt;p&gt;Then the whole team was laid off. Company was on the brink of an IPO, circumstances changed, and that was that. Neither stack ever went to production.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Walked Away With
&lt;/h2&gt;

&lt;p&gt;The work didn't ship. The thinking did.&lt;/p&gt;

&lt;p&gt;When you're assigned a technical decision, the room will often have already made up its mind. Your job isn't to confirm that. Your job is to understand what the product needs and find the architecture that serves it - regardless of what's familiar, regardless of what's popular, regardless of what the consensus is pointing at.&lt;/p&gt;

&lt;p&gt;AI will keep getting better at execution. It will scaffold faster, generate more, automate more. What it won't do is walk into a standup, hear a team default to the comfortable answer, and ask whether that answer is actually right.&lt;/p&gt;

&lt;p&gt;That's still the job.&lt;/p&gt;

&lt;h2&gt;
  
  
  One More Thing
&lt;/h2&gt;

&lt;p&gt;Recently, &lt;a href="https://www.cloudflare.com/en-in/press/press-releases/2026/cloudflare-acquires-astro-to-accelerate-the-future-of-high-performance-web-development/" rel="noopener noreferrer"&gt;Cloudflare acquired Astro&lt;/a&gt;. A framework built to ship minimal JavaScript, backed by one of the largest infrastructure companies on the internet. &lt;br&gt;
When I ran those POCs, Astro was the underdog pick against a category default. That acquisition is validation, not just of the framework, but of the architectural reasoning behind choosing it.&lt;/p&gt;

&lt;p&gt;The right tool for the product, before the market agreed.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>nextjs</category>
      <category>performance</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Stacking Multiple Dialogs in React Without Hooks or Effects</title>
      <dc:creator>Phaneendra Kanduri</dc:creator>
      <pubDate>Wed, 18 Feb 2026 08:52:33 +0000</pubDate>
      <link>https://dev.to/9thquadrant/stacking-multiple-dialogs-in-react-without-hooks-or-effects-4enj</link>
      <guid>https://dev.to/9thquadrant/stacking-multiple-dialogs-in-react-without-hooks-or-effects-4enj</guid>
      <description>&lt;p&gt;Managing z-index across multiple stacked dialogs in React gets messy fast. I ran this problem through an AI tool out of curiosity and it generated this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;jsxconst&lt;/span&gt; &lt;span class="nx"&gt;DialogManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;dialogs&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;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dialogs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&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;zIndexBackdrop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;zIndexContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;zIndexBackdrop&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
              &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"modal-backdrop show"&lt;/span&gt;
              &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;zIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;zIndexBackdrop&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
              &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt;
              &lt;span class="na"&gt;aria-modal&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
              &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;zIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;zIndexContent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&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;Functional, but it runs the &lt;code&gt;z-index&lt;/code&gt; calculation on every render cycle. For a static stacking requirement, that's unnecessary execution. Styling problems should be solved in the style layer.&lt;/p&gt;

&lt;p&gt;Here's the approach I came up with using SCSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@for&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="ow"&gt;from&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="ow"&gt;through&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$zIndexBackdrop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;$zIndexContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nc"&gt;.modal-backdrop.show&lt;/span&gt;&lt;span class="nd"&gt;:nth-of-type&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$zIndexBackdrop&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"dialog"&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="nt"&gt;aria-modal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:nth-of-type&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$zIndexContent&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;p&gt;This loop generates &lt;code&gt;z-index&lt;/code&gt; rules for up to 6 dialog layers at compile time - zero runtime cost. The backdrop and content for each layer are spaced 2 units apart, with 5-unit gaps between layers to leave room for other elements if needed.&lt;/p&gt;

&lt;p&gt;A few things to know before using this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;nth-of-type&lt;/code&gt; is sensitive to DOM structure. If other elements of the same type exist in the same context, the selector can misfire. Verify your rendered DOM matches the assumption.&lt;/li&gt;
&lt;li&gt;The ceiling of &lt;code&gt;6&lt;/code&gt; is arbitrary — adjust the loop range, base value, and step size to fit your &lt;code&gt;z-index&lt;/code&gt; scale.&lt;/li&gt;
&lt;li&gt;This compiles down to plain CSS. No runtime, no state, no effect hooks. That's the point.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>ui</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
