<?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: Nitin N.</title>
    <description>The latest articles on DEV Community by Nitin N. (@nitinnair89).</description>
    <link>https://dev.to/nitinnair89</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F727836%2Fe3be4437-00d9-47a2-aaf3-7c9d244baa92.JPG</url>
      <title>DEV Community: Nitin N.</title>
      <link>https://dev.to/nitinnair89</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nitinnair89"/>
    <language>en</language>
    <item>
      <title>Using Notion Database as a Static CMS with Next.js</title>
      <dc:creator>Nitin N.</dc:creator>
      <pubDate>Sun, 14 Jun 2026 05:47:38 +0000</pubDate>
      <link>https://dev.to/nitinnair89/using-notion-database-as-a-static-cms-with-nextjs-3nc6</link>
      <guid>https://dev.to/nitinnair89/using-notion-database-as-a-static-cms-with-nextjs-3nc6</guid>
      <description>&lt;p&gt;Most projects don't need a dedicated content management platform.&lt;/p&gt;

&lt;p&gt;Recently, I experimented with using a Notion database as a lightweight CMS for a Next.js application. The objective was simple: allow content to be managed in Notion while keeping the website statically generated.&lt;/p&gt;

&lt;p&gt;The architecture ended up being much simpler than I expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Notion Database
        ↓
Notion API
        ↓
Next.js App Router
        ↓
Static Pages
        ↓
Deployment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Content authors work entirely in Notion, while Next.js fetches the data during build time and generates static pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use Notion?
&lt;/h2&gt;

&lt;p&gt;The biggest advantage is that Notion already provides an interface that people are comfortable with. There's no admin dashboard to build, no authentication layer to maintain, and no additional infrastructure to manage.&lt;/p&gt;

&lt;p&gt;For many projects, that's enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Works Well
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Simple Content Management
&lt;/h3&gt;

&lt;p&gt;Content editors don't need access to the codebase. Everything is managed from Notion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fast Development
&lt;/h3&gt;

&lt;p&gt;Instead of spending time building internal tooling, development can focus on the actual product.&lt;/p&gt;

&lt;h3&gt;
  
  
  Static Generation
&lt;/h3&gt;

&lt;p&gt;Using &lt;code&gt;generateStaticParams()&lt;/code&gt; with the App Router allows pages to be generated ahead of time, resulting in good performance and SEO.&lt;/p&gt;

&lt;h3&gt;
  
  
  Low Operational Cost
&lt;/h3&gt;

&lt;p&gt;The stack remains minimal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js&lt;/li&gt;
&lt;li&gt;Notion API&lt;/li&gt;
&lt;li&gt;Vercel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For smaller projects, this is often sufficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where It Falls Short
&lt;/h2&gt;

&lt;h3&gt;
  
  
  API Performance
&lt;/h3&gt;

&lt;p&gt;The Notion API is noticeably slower than dedicated headless CMS platforms. Caching becomes important as content grows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limited Querying
&lt;/h3&gt;

&lt;p&gt;Complex filtering and relationships are possible, but they aren't particularly elegant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build Times
&lt;/h3&gt;

&lt;p&gt;Large databases can increase build times because content is fetched during static generation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Content Discipline Matters
&lt;/h3&gt;

&lt;p&gt;Without clear conventions, databases can quickly become inconsistent. Naming standards and property definitions are important.&lt;/p&gt;

&lt;h2&gt;
  
  
  When I'd Use This
&lt;/h2&gt;

&lt;p&gt;I think this approach works well for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blogs&lt;/li&gt;
&lt;li&gt;Documentation sites&lt;/li&gt;
&lt;li&gt;Portfolios&lt;/li&gt;
&lt;li&gt;Landing pages&lt;/li&gt;
&lt;li&gt;Small business websites&lt;/li&gt;
&lt;li&gt;Internal knowledge bases&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When I Wouldn't
&lt;/h2&gt;

&lt;p&gt;I probably wouldn't choose it for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;E-commerce applications&lt;/li&gt;
&lt;li&gt;Large editorial teams&lt;/li&gt;
&lt;li&gt;Complex relational data models&lt;/li&gt;
&lt;li&gt;High-frequency content updates&lt;/li&gt;
&lt;li&gt;Real-time applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In those cases, platforms like Sanity, Contentful, or Strapi provide much better tooling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Not every problem requires a sophisticated CMS.&lt;/p&gt;

&lt;p&gt;Sometimes a Notion database and a few API calls are enough to deliver a clean and maintainable solution.&lt;/p&gt;

&lt;p&gt;The combination of Notion and Next.js isn't the most powerful content architecture available, but for the right use case, it's difficult to argue against its simplicity.&lt;/p&gt;

</description>
      <category>api</category>
      <category>database</category>
      <category>nextjs</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Fixing a wrong GitHub repository mapping using GitHub CLI</title>
      <dc:creator>Nitin N.</dc:creator>
      <pubDate>Sat, 02 May 2026 05:04:57 +0000</pubDate>
      <link>https://dev.to/nitinnair89/fixing-a-wrong-github-repository-mapping-using-github-cli-1nd8</link>
      <guid>https://dev.to/nitinnair89/fixing-a-wrong-github-repository-mapping-using-github-cli-1nd8</guid>
      <description>&lt;p&gt;While working on a personal project, I ran into a workflow issue:&lt;/p&gt;

&lt;p&gt;My Linear team was mapped to the wrong GitHub repository.&lt;/p&gt;

&lt;p&gt;Result?&lt;/p&gt;

&lt;p&gt;New issues created from Linear were landing in the wrong repo.&lt;/p&gt;

&lt;p&gt;Not catastrophic — but messy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Once I identified the mapping issue, I had to clean up the already-created issues.&lt;/p&gt;

&lt;p&gt;Manual fix would mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;recreating issues&lt;/li&gt;
&lt;li&gt;copying descriptions&lt;/li&gt;
&lt;li&gt;re-adding labels&lt;/li&gt;
&lt;li&gt;restoring references manually&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s unnecessary operational overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;I used GitHub CLI to transfer the issues to the correct repository.&lt;/p&gt;

&lt;p&gt;That preserved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;issue history&lt;/li&gt;
&lt;li&gt;comments&lt;/li&gt;
&lt;li&gt;metadata&lt;/li&gt;
&lt;li&gt;workflow continuity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fast, clean, low-risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is useful
&lt;/h2&gt;

&lt;p&gt;CLI tooling often feels optional until it saves you time.&lt;/p&gt;

&lt;p&gt;This was a good reminder that understanding your tooling deeply helps you solve workflow problems faster and cleaner.&lt;/p&gt;

&lt;p&gt;Even in hobby projects, good operational habits matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Helpful resources for getting started with GitHub CLI
&lt;/h2&gt;

&lt;p&gt;GitHub CLI official docs:&lt;br&gt;
&lt;a href="https://cli.github.com/" rel="noopener noreferrer"&gt;https://cli.github.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Installation:&lt;br&gt;
&lt;a href="https://cli.github.com/manual/installation" rel="noopener noreferrer"&gt;https://cli.github.com/manual/installation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Authentication:&lt;br&gt;
&lt;a href="https://cli.github.com/manual/gh_auth_login" rel="noopener noreferrer"&gt;https://cli.github.com/manual/gh_auth_login&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Issue management:&lt;br&gt;
&lt;a href="https://cli.github.com/manual/gh_issue" rel="noopener noreferrer"&gt;https://cli.github.com/manual/gh_issue&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repository management:&lt;br&gt;
&lt;a href="https://cli.github.com/manual/gh_repo" rel="noopener noreferrer"&gt;https://cli.github.com/manual/gh_repo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Small workflow problem. Useful tooling lesson.&lt;/p&gt;

</description>
      <category>software</category>
      <category>git</category>
      <category>linear</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Thought I Understood Async — Until I Built a Real Task Runner in Node.js</title>
      <dc:creator>Nitin N.</dc:creator>
      <pubDate>Sun, 18 Jan 2026 10:34:35 +0000</pubDate>
      <link>https://dev.to/nitinnair89/i-thought-i-understood-async-until-i-built-a-real-task-runner-in-nodejs-m47</link>
      <guid>https://dev.to/nitinnair89/i-thought-i-understood-async-until-i-built-a-real-task-runner-in-nodejs-m47</guid>
      <description>&lt;p&gt;If you’ve ever written this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tasks&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;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ve probably told yourself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“That’s concurrency.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I used to believe that too.&lt;/p&gt;

&lt;p&gt;Then I tried to build something that behaved like a real system — not a tutorial.&lt;/p&gt;

&lt;p&gt;What I wanted was simple on paper:&lt;br&gt;
A Node.js task runner that could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run tasks concurrently&lt;/li&gt;
&lt;li&gt;Retry failures&lt;/li&gt;
&lt;li&gt;Never overload the system&lt;/li&gt;
&lt;li&gt;Always reach a terminal state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I got instead was a lesson in &lt;strong&gt;engineering humility&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Problem That Started It
&lt;/h2&gt;

&lt;p&gt;I wasn’t trying to optimize performance.&lt;br&gt;
I was trying to build trust into my system.&lt;/p&gt;

&lt;p&gt;In production, this is what really matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APIs fail&lt;/li&gt;
&lt;li&gt;Networks lie&lt;/li&gt;
&lt;li&gt;Logs deceive&lt;/li&gt;
&lt;li&gt;Retries spiral out of control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I asked myself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If this were a job queue or microservice worker — would I trust it at 3 AM?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question shaped everything.&lt;/p&gt;


&lt;h2&gt;
  
  
  What a “Task” Really Is
&lt;/h2&gt;

&lt;p&gt;In my system, a task is not a promise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It’s a function that returns a promise.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Why that matters:&lt;br&gt;
Because retries require re-execution, not re-awaiting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;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 single design choice eliminated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stale promises&lt;/li&gt;
&lt;li&gt;Broken retries&lt;/li&gt;
&lt;li&gt;Phantom concurrency bugs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Fake API That Told the Truth
&lt;/h2&gt;

&lt;p&gt;Instead of mocking success, I mocked reality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fakeApi&lt;/span&gt; &lt;span class="o"&gt;=&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="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shouldSucceed&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;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`STARTING Task ID: &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="s2"&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;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&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="nf"&gt;setTimeout&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;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;shouldSucceed&lt;/span&gt;&lt;span class="p"&gt;)&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="s2"&gt;`SUCCEEDED Task ID: &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="s2"&gt; | Duration &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms`&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;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`FAILED Task ID: &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="s2"&gt; | Duration &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; failed`&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;delay&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 gave me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real durations&lt;/li&gt;
&lt;li&gt;Real failures&lt;/li&gt;
&lt;li&gt;Real concurrency pressure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No illusions. Only evidence.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Engineering Problem
&lt;/h2&gt;

&lt;p&gt;The hard part wasn’t “running tasks.”&lt;/p&gt;

&lt;p&gt;It was enforcing three laws:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Concurrency is a Hard Ceiling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Never more than &lt;code&gt;MAX_CONCURRENT_TASKS&lt;/code&gt; running — even during retries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Retries Must Be Deterministic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A failed task can retry — but it must never exist twice in-flight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Everything Must End&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every task must reach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Success&lt;/li&gt;
&lt;li&gt;Or retry exhaustion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No zombies. No silent failures.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Breakthrough Moment
&lt;/h2&gt;

&lt;p&gt;The moment it clicked wasn’t when my code worked.&lt;/p&gt;

&lt;p&gt;It was when my logs told a story:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;STARTING Task ID: 2
FAILED Task ID: 2
STARTING Task ID: 2
FAILED Task ID: 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That wasn’t noise.&lt;/p&gt;

&lt;p&gt;That was controlled failure.&lt;/p&gt;

&lt;p&gt;That’s what real systems do.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Taught Me About Backend Engineering
&lt;/h2&gt;

&lt;p&gt;Concurrency isn’t about parallelism.&lt;br&gt;
It’s about &lt;strong&gt;governance&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Retries aren’t about recovery.&lt;br&gt;
They’re about &lt;strong&gt;discipline&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Logs aren’t for debugging.&lt;br&gt;
They’re for &lt;strong&gt;truth&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Pattern Matters in Production
&lt;/h2&gt;

&lt;p&gt;This architecture maps directly to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Job queues&lt;/li&gt;
&lt;li&gt;Worker pools&lt;/li&gt;
&lt;li&gt;Microservice orchestrators&lt;/li&gt;
&lt;li&gt;API batch processors&lt;/li&gt;
&lt;li&gt;Event-driven systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you can control this — you can control load, cost, and reliability.&lt;/p&gt;




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

&lt;p&gt;Most developers learn async.&lt;/p&gt;

&lt;p&gt;Engineers learn systems behavior.&lt;/p&gt;

&lt;p&gt;This project pushed me from one side to the other.&lt;/p&gt;




&lt;p&gt;Repo link - &lt;a href="https://github.com/NitinNair89/concurrent-task-runner" rel="noopener noreferrer"&gt;Concurrency Task Runner&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>backend</category>
      <category>systemdesign</category>
      <category>programming</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Nitin N.</dc:creator>
      <pubDate>Fri, 26 Dec 2025 07:42:47 +0000</pubDate>
      <link>https://dev.to/nitinnair89/-2l6o</link>
      <guid>https://dev.to/nitinnair89/-2l6o</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/icons/icon-libraries-for-nextjs-1915" class="crayons-story__hidden-navigation-link"&gt;Top 10 Icon Libraries for Next.js - 2026&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/icons" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F406004%2F69465f5d-f240-4078-b29f-c3b36cfd6a39.jpg" alt="icons profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/icons" class="crayons-story__secondary fw-medium m:hidden"&gt;
              LineIcons
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                LineIcons
                
              
              &lt;div id="story-author-preview-content-2407848" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/icons" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F406004%2F69465f5d-f240-4078-b29f-c3b36cfd6a39.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;LineIcons&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/icons/icon-libraries-for-nextjs-1915" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 15 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/icons/icon-libraries-for-nextjs-1915" id="article-link-2407848"&gt;
          Top 10 Icon Libraries for Next.js - 2026
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/icons"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;icons&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/nextjs"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;nextjs&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/icons/icon-libraries-for-nextjs-1915" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;88&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/icons/icon-libraries-for-nextjs-1915#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              4&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            6 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>icons</category>
      <category>nextjs</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Nitin N.</dc:creator>
      <pubDate>Fri, 26 Dec 2025 06:39:04 +0000</pubDate>
      <link>https://dev.to/nitinnair89/-17fj</link>
      <guid>https://dev.to/nitinnair89/-17fj</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/rowsanali/how-to-use-layer-for-advanced-styling-in-tailwind-css-26pj" class="crayons-story__hidden-navigation-link"&gt;How to Use @layer for Advanced Styling in Tailwind CSS&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/rowsanali" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F1185586%2F522a1ca1-55eb-48a8-87ef-92d53e4847f4.png" alt="rowsanali profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/rowsanali" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Rowsan Ali
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Rowsan Ali
                
              
              &lt;div id="story-author-preview-content-2298578" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/rowsanali" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F1185586%2F522a1ca1-55eb-48a8-87ef-92d53e4847f4.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Rowsan Ali&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/rowsanali/how-to-use-layer-for-advanced-styling-in-tailwind-css-26pj" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Feb 26 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/rowsanali/how-to-use-layer-for-advanced-styling-in-tailwind-css-26pj" id="article-link-2298578"&gt;
          How to Use @layer for Advanced Styling in Tailwind CSS
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/beginners"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;beginners&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/rowsanali/how-to-use-layer-for-advanced-styling-in-tailwind-css-26pj" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;6&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/rowsanali/how-to-use-layer-for-advanced-styling-in-tailwind-css-26pj#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              1&lt;span class="hidden s:inline"&gt;&amp;nbsp;comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Swapping Authentication Strategies Without Touching UI: A Frontend Architecture Demo</title>
      <dc:creator>Nitin N.</dc:creator>
      <pubDate>Wed, 24 Dec 2025 17:34:41 +0000</pubDate>
      <link>https://dev.to/nitinnair89/swapping-authentication-strategies-without-touching-ui-a-frontend-architecture-demo-4g1h</link>
      <guid>https://dev.to/nitinnair89/swapping-authentication-strategies-without-touching-ui-a-frontend-architecture-demo-4g1h</guid>
      <description>&lt;p&gt;When frontend applications grow, authentication is often the first place where architectural cracks appear.&lt;/p&gt;

&lt;p&gt;JWTs, cookies, sessions, headers — each change tends to ripple through components, hooks, and state logic. Over time, the UI becomes inseparable from the authentication mechanism.&lt;/p&gt;

&lt;p&gt;I wanted to explore what it looks like when that &lt;em&gt;doesn’t&lt;/em&gt; happen.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Goal&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Build a frontend demo where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The UI does not know how authentication works&lt;/li&gt;
&lt;li&gt;Switching between cookie-based auth and JWT-based auth requires no UI changes&lt;/li&gt;
&lt;li&gt;The swap happens at the composition boundary, not inside components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This demo is built on top of my &lt;code&gt;frontend-foundation&lt;/code&gt; template, which enforces strict separation between UI, hooks, services, and implementations.&lt;/p&gt;




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

&lt;p&gt;The application exposes a single hook to the UI:&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="nf"&gt;useAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything downstream depends on a stable contract:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;login()&lt;/li&gt;
&lt;li&gt;logout()&lt;/li&gt;
&lt;li&gt;getSession()&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The UI never imports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cookie logic&lt;/li&gt;
&lt;li&gt;JWT logic&lt;/li&gt;
&lt;li&gt;storage APIs&lt;/li&gt;
&lt;li&gt;auth libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two different clients implement the same contract:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cookieClient&lt;/li&gt;
&lt;li&gt;jwtClient&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each client is passed into &lt;code&gt;useAuth&lt;/code&gt; independently, allowing both strategies to run side-by-side on the same page.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;In many real-world projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switching auth strategies means rewriting hooks&lt;/li&gt;
&lt;li&gt;Components start branching on auth type&lt;/li&gt;
&lt;li&gt;State logic leaks implementation details&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This demo proves that those problems are architectural, not inevitable.&lt;/p&gt;

&lt;p&gt;If auth is treated as an implementation detail behind a contract, the UI becomes resilient to change.&lt;/p&gt;




&lt;h2&gt;
  
  
  Repositories
&lt;/h2&gt;

&lt;p&gt;Demo app: &lt;a href="https://github.com/NitinNair89/frontend-foundation-auth-management-swap-demo" rel="noopener noreferrer"&gt;Frontend Foundation Auth Swap Demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Template: &lt;a href="https://github.com/NitinNair89/frontend-foundation" rel="noopener noreferrer"&gt;Frontend Foundation Starter Kit&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;If swapping core infrastructure feels risky, it’s usually a signal that the UI is coupled too tightly to implementation details.&lt;/p&gt;

&lt;p&gt;The fix isn’t a new library — it’s better boundaries.&lt;/p&gt;




&lt;h2&gt;
  
  
  Other articles in this series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/nitinnair89/frontend-foundation-a-swap-friendly-nextjs-starter-template-4e81"&gt;Frontend Foundation: A Production-grade Starter Template&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/nitinnair89/why-i-built-a-frontend-template-that-makes-swapping-libraries-boring-52l9"&gt;Frontend Foundation: HTTP Swap Demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/nitinnair89/swapping-redux-and-jotai-without-touching-ui-a-production-grade-frontend-pattern-1o7m"&gt;Frontend Foundation: Swapping Redux and Jotai Without Touching UI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>frontend</category>
      <category>javascript</category>
      <category>security</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Swapping Redux and Jotai Without Touching UI: A Production-Grade Frontend Pattern</title>
      <dc:creator>Nitin N.</dc:creator>
      <pubDate>Wed, 24 Dec 2025 04:47:23 +0000</pubDate>
      <link>https://dev.to/nitinnair89/swapping-redux-and-jotai-without-touching-ui-a-production-grade-frontend-pattern-1o7m</link>
      <guid>https://dev.to/nitinnair89/swapping-redux-and-jotai-without-touching-ui-a-production-grade-frontend-pattern-1o7m</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Frontend teams often debate &lt;strong&gt;Redux vs Jotai vs Zustand&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The real problem is not which library you choose — it’s how hard it is to change later.&lt;/p&gt;

&lt;p&gt;This article demonstrates a compile-time state swap pattern using a frontend template I built, validated with a real demo app.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Usual Problem
&lt;/h2&gt;

&lt;p&gt;In most projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI components import store-specific hooks&lt;/li&gt;
&lt;li&gt;Business logic leaks into reducers or atoms&lt;/li&gt;
&lt;li&gt;Changing state libraries means rewriting half the app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is not scalable.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Idea
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;UI should depend on contracts, not implementations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this template:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI consumes a single useCounter() hook&lt;/li&gt;
&lt;li&gt;That hook is the only swap boundary&lt;/li&gt;
&lt;li&gt;Redux and Jotai both conform to the same outward shape
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
  &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;
  &lt;span class="nf"&gt;decrement&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No UI changes.&lt;br&gt;
No page-level changes.&lt;br&gt;
No runtime assumptions.&lt;/p&gt;




&lt;h2&gt;
  
  
  State Swap in Action
&lt;/h2&gt;

&lt;p&gt;The demo app contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One Counter page&lt;/li&gt;
&lt;li&gt;One hook boundary&lt;/li&gt;
&lt;li&gt;Two implementations:

&lt;ul&gt;
&lt;li&gt;Redux Toolkit&lt;/li&gt;
&lt;li&gt;Jotai&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Swapping engines means changing one import, nothing else.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Template Repo: &lt;a href="https://github.com/NitinNair89/frontend-foundation" rel="noopener noreferrer"&gt;Frontend Foundation Template Kit&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;State Swap Demo: &lt;a href="https://github.com/NitinNair89/frontend-foundation-state-management-swap-demo" rel="noopener noreferrer"&gt;State Management Swap Demo App&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why This Matters in Real Teams
&lt;/h2&gt;

&lt;p&gt;This pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduces long-term technical risk&lt;/li&gt;
&lt;li&gt;Makes architectural decisions reversible&lt;/li&gt;
&lt;li&gt;Allows teams to adopt new libraries safely&lt;/li&gt;
&lt;li&gt;Fits enterprise and startup codebases alike&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Libraries will change. Frameworks will evolve.&lt;/p&gt;

&lt;p&gt;Your architecture must survive those changes.&lt;/p&gt;

&lt;p&gt;This template is my attempt to codify that principle. If this resonates, I’d love to hear your thoughts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Other articles in this series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/nitinnair89/frontend-foundation-a-swap-friendly-nextjs-starter-template-4e81"&gt;Frontend Foundation: A Swap-Friendly Next.js Starter Template&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/nitinnair89/why-i-built-a-frontend-template-that-makes-swapping-libraries-boring-52l9"&gt;Frontend Foundation: HTTP Swap Demo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>resources</category>
      <category>tooling</category>
      <category>webdev</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Why I Built a Frontend Template That Makes Swapping Libraries Boring</title>
      <dc:creator>Nitin N.</dc:creator>
      <pubDate>Tue, 23 Dec 2025 09:38:28 +0000</pubDate>
      <link>https://dev.to/nitinnair89/why-i-built-a-frontend-template-that-makes-swapping-libraries-boring-52l9</link>
      <guid>https://dev.to/nitinnair89/why-i-built-a-frontend-template-that-makes-swapping-libraries-boring-52l9</guid>
      <description>&lt;p&gt;Frontend projects rarely fail because of bad code.&lt;br&gt;
They fail because &lt;strong&gt;change becomes expensive&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In many codebases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI imports fetch directly&lt;/li&gt;
&lt;li&gt;State libraries leak into components&lt;/li&gt;
&lt;li&gt;Infrastructure decisions become permanent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted a different default.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;Frontend Foundation&lt;/strong&gt;, a minimal Next.js template designed around one constraint:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;UI code should never care how data is fetched or stored.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The template enforces simple boundaries:&lt;br&gt;
UI → hooks → services → clients&lt;/p&gt;

&lt;p&gt;To validate this, I created a separate demo repository.&lt;/p&gt;

&lt;p&gt;In that demo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The UI renders a list of posts&lt;/li&gt;
&lt;li&gt;Data can be fetched using fetch or axios&lt;/li&gt;
&lt;li&gt;A button switches the implementation at runtime&lt;/li&gt;
&lt;li&gt;No UI, hook, or service code changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The swap happens behind a stable contract.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is not a pattern for demos only. This is how long-lived frontend systems stay flexible.&lt;/p&gt;

&lt;p&gt;The template itself stays intentionally minimal. Each project adds what it needs. Nothing is locked in.&lt;/p&gt;

&lt;p&gt;If you’ve ever avoided refactoring because “it would touch too much,”&lt;br&gt;
this approach is worth exploring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Template repo:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/NitinNair89/frontend-foundation" rel="noopener noreferrer"&gt;frontend-foundation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTTP swap Demo repo:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/NitinNair89/frontend-foundation-http-swap-demo" rel="noopener noreferrer"&gt;frontend-foundation-http-swap-demo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>resources</category>
      <category>webdev</category>
      <category>architecture</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Frontend Foundation: A Swap-Friendly Next.js Starter Template</title>
      <dc:creator>Nitin N.</dc:creator>
      <pubDate>Mon, 22 Dec 2025 11:25:13 +0000</pubDate>
      <link>https://dev.to/nitinnair89/frontend-foundation-a-swap-friendly-nextjs-starter-template-4e81</link>
      <guid>https://dev.to/nitinnair89/frontend-foundation-a-swap-friendly-nextjs-starter-template-4e81</guid>
      <description>&lt;p&gt;Frontend developers often struggle with long-lived projects where UI becomes tightly coupled to state libraries, HTTP clients, or backend frameworks.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend Foundation&lt;/strong&gt; solves this by:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decoupling UI from all implementations
&lt;/li&gt;
&lt;li&gt;Allowing library swaps without changing any frontend code
&lt;/li&gt;
&lt;li&gt;Using Tailwind CSS design tokens for scalable, maintainable styling
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Next.js 14 App Router + TypeScript strict mode
&lt;/li&gt;
&lt;li&gt;Token-driven Tailwind CSS (dark/light themes via next-themes)
&lt;/li&gt;
&lt;li&gt;Clear folder responsibilities (&lt;code&gt;hooks&lt;/code&gt;, &lt;code&gt;services&lt;/code&gt;, &lt;code&gt;clients&lt;/code&gt;, &lt;code&gt;stores&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;Documentation baked in via GitHub Pages
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to see it in action:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Template repo: &lt;a href="https://github.com/NitinNair89/frontend-foundation" rel="noopener noreferrer"&gt;https://github.com/NitinNair89/frontend-foundation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Sample apps (coming soon)
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This starter kit is ideal for senior engineers and teams aiming for global-scale frontend architecture.  &lt;/p&gt;

&lt;h1&gt;
  
  
  NextJS #TailwindCSS #WebDev #TypeScript #FrontendArchitecture
&lt;/h1&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>architecture</category>
      <category>resources</category>
    </item>
    <item>
      <title>Fixing Unwanted Re-renders in a Nested Component Tree Using React Context</title>
      <dc:creator>Nitin N.</dc:creator>
      <pubDate>Sat, 13 Dec 2025 11:44:04 +0000</pubDate>
      <link>https://dev.to/nitinnair89/fixing-unwanted-re-renders-in-a-nested-component-tree-using-react-context-528g</link>
      <guid>https://dev.to/nitinnair89/fixing-unwanted-re-renders-in-a-nested-component-tree-using-react-context-528g</guid>
      <description>&lt;p&gt;*&lt;em&gt;Not all React performance issues show up as errors.&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Some appear quietly during development, when a simple UI interaction feels heavier than it should. &lt;/p&gt;

&lt;p&gt;This article walks through a real dev-time issue caused by prop drilling and poor state placement — and how restructuring state fixed unnecessary re-renders, API calls, and heavy UI updates.&lt;/p&gt;



&lt;p&gt;This was a &lt;strong&gt;React&lt;/strong&gt; application where a parent component handled:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetching data from an API&lt;/li&gt;
&lt;li&gt;Rendering a &lt;strong&gt;Highcharts&lt;/strong&gt;-based visualization (expensive to re-render)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deep in the component tree (2–3 levels down), there was a child component with a button.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Requirement:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
When the button is clicked, show a modal.&lt;/p&gt;

&lt;p&gt;Simple on paper.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Initial Implementation
&lt;/h2&gt;

&lt;p&gt;The modal state was defined in the parent component.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isModalOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsModalOpen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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;useEffect&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="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Child&lt;/span&gt; &lt;span class="na"&gt;setIsModalOpen&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setIsModalOpen&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;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;setIsModalOpen&lt;/code&gt; function was passed down multiple levels via props until it reached the button.&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setIsModalOpen&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="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  Open modal
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&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;Functionally, this worked.&lt;/p&gt;

&lt;p&gt;Architecturally, it was problematic.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Observed During Testing
&lt;/h2&gt;

&lt;p&gt;While casually testing the feature in development, I noticed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A visible UI pause on button click&lt;/li&gt;
&lt;li&gt;Highcharts re-rendering&lt;/li&gt;
&lt;li&gt;API calls being triggered again&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every click caused a parent state update, which meant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parent component re-rendered&lt;/li&gt;
&lt;li&gt;Side effects tied to the parent executed again&lt;/li&gt;
&lt;li&gt;Expensive UI re-initialized unnecessarily&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Nothing was broken — but the cost was real.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Root Cause
&lt;/h2&gt;

&lt;p&gt;The issue was state placement. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The modal state was UI-only.&lt;/li&gt;
&lt;li&gt;It lived in a parent component responsible for data fetching and heavy rendering. &lt;/li&gt;
&lt;li&gt;Prop drilling tightly coupled a local interaction to global side effects&lt;/li&gt;
&lt;li&gt;This is a common architectural leak in React applications as they grow.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Fix: Isolate UI State with Context
&lt;/h2&gt;

&lt;p&gt;The solution was to remove modal state from the parent entirely and introduce a dedicated Context for modal visibility.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ModalContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ModalProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsOpen&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;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="nc"&gt;ModalContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt; &lt;span class="na"&gt;value&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="nx"&gt;isOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsOpen&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;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ModalContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The provider was placed at the top-most boundary where modal state made sense.&lt;/p&gt;

&lt;p&gt;Components that needed to open or close the modal consumed the context directly.&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setIsOpen&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ModalContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No prop drilling. No parent involvement.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;After this change:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parent components no longer re-rendered on modal open&lt;/li&gt;
&lt;li&gt;API calls executed only when explicitly intended&lt;/li&gt;
&lt;li&gt;Highcharts remained stable&lt;/li&gt;
&lt;li&gt;UI interactions felt immediate and predictable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The behavior was the same for users, but the architecture was cleaner and more performant.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prop drilling can hide performance issues, even when code is correct&lt;/li&gt;
&lt;li&gt;UI-only state should not live alongside data-fetching logic&lt;/li&gt;
&lt;li&gt;If a button click triggers unrelated side effects, state boundaries are wrong&lt;/li&gt;
&lt;li&gt;Context API is not just for global data — it is effective for isolating UI concerns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;*&lt;em&gt;Performance issues in React are often design issues in disguise.&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>frontend</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Why Unit Tests in React Catch Bugs the UI Never Shows (Jest Real-World Example)</title>
      <dc:creator>Nitin N.</dc:creator>
      <pubDate>Sat, 06 Dec 2025 06:24:05 +0000</pubDate>
      <link>https://dev.to/nitinnair89/why-unit-tests-in-react-catch-bugs-the-ui-never-shows-jest-real-world-example-4ak0</link>
      <guid>https://dev.to/nitinnair89/why-unit-tests-in-react-catch-bugs-the-ui-never-shows-jest-real-world-example-4ak0</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;In React applications, many bugs don’t show up as broken UI.&lt;br&gt;
They hide in business logic, only surfacing under specific data conditions.&lt;/p&gt;

&lt;p&gt;This is where unit tests in React — especially with Jest — become critical.&lt;/p&gt;

&lt;p&gt;Recently, while updating a React component’s business logic, I ran into a situation where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the UI worked perfectly&lt;/li&gt;
&lt;li&gt;new tests passed&lt;/li&gt;
&lt;li&gt;existing Jest tests started failing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That failure uncovered a hidden logic bug that manual testing never revealed.&lt;/p&gt;

&lt;p&gt;This post walks through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;why this happens in real projects&lt;/li&gt;
&lt;li&gt;how Jest and React Testing Library expose these issues&lt;/li&gt;
&lt;li&gt;and how unit tests help developers catch bugs before production&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  The Problem: UI Looks Correct, Tests Don’t
&lt;/h2&gt;

&lt;p&gt;The component showed an empty-state message when data was missing.&lt;br&gt;
In the browser, everything worked as expected.&lt;/p&gt;

&lt;p&gt;However, an older Jest test using mocked data failed.&lt;/p&gt;

&lt;p&gt;That inconsistency mattered.&lt;/p&gt;

&lt;p&gt;If deterministic tests fail while the UI passes, it usually means:&lt;br&gt;
👉 logic behaves differently depending on data shape&lt;/p&gt;

&lt;p&gt;This is exactly the kind of issue that unit tests are designed to catch.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Mocked Data Finds Bugs Faster Than Manual Testing
&lt;/h2&gt;

&lt;p&gt;Mocked data often triggers scenarios that real accounts don’t.&lt;/p&gt;

&lt;p&gt;In this case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;real data followed a happy path&lt;/li&gt;
&lt;li&gt;mocked data exposed a processing assumption&lt;/li&gt;
&lt;li&gt;the logic worked accidentally, not reliably&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The test didn’t fail because the UI was wrong.&lt;br&gt;
It failed because the business logic wasn’t robust.&lt;/p&gt;

&lt;p&gt;Without Jest tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;this bug would’ve slipped into production&lt;/li&gt;
&lt;li&gt;or surfaced later during refactoring&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Correct Jest Setup for React (Minimal &amp;amp; Practical)
&lt;/h2&gt;

&lt;p&gt;Install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save-dev jest @testing-library/react @testing-library/jest-dom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure Jest:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;jest.config.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;testEnvironment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jsdom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;setupFilesAfterEnv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;rootDir&amp;gt;/jest.setup.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;jest.setup.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@testing-library/jest-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup mirrors browser behavior closely and avoids false positives.&lt;/p&gt;




&lt;h2&gt;
  
  
  Writing Unit Tests That Actually Catch Bugs
&lt;/h2&gt;

&lt;p&gt;Avoid testing visuals.&lt;br&gt;
Test conditions and outcomes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getProcessedData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@util/helper&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My Component&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shows empty message when data is missing&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="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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getProcessedData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyComponent&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/no data available/i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&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;Tests like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validate business rules&lt;/li&gt;
&lt;li&gt;protect against regressions&lt;/li&gt;
&lt;li&gt;force safer logic paths&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tests like these (the test above is a simplified version for beginners) helped me detect the faulty data processing logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fix Was in the Logic, Not the UI
&lt;/h2&gt;

&lt;p&gt;Once identified:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;data normalization fixed the issue&lt;/li&gt;
&lt;li&gt;all tests passed&lt;/li&gt;
&lt;li&gt;UI remained unchanged&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key win wasn’t visual correctness — it was logical correctness.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why React Unit Tests Matter in Production Code
&lt;/h2&gt;

&lt;p&gt;React unit tests help developers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Catch bugs during development&lt;/li&gt;
&lt;li&gt;Reduce reliance on manual testing&lt;/li&gt;
&lt;li&gt;Refactor safely&lt;/li&gt;
&lt;li&gt;Ship with confidence&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Coverage numbers are secondary. &lt;strong&gt;Confidence and correctness are the real goals.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the UI works but tests don’t exist, the bug is just waiting.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>react</category>
      <category>frontend</category>
      <category>jest</category>
    </item>
    <item>
      <title>I Thought My API Was Secure. I Was Wrong.</title>
      <dc:creator>Nitin N.</dc:creator>
      <pubDate>Wed, 26 Nov 2025 17:39:14 +0000</pubDate>
      <link>https://dev.to/nitinnair89/i-thought-my-api-was-secure-i-was-wrong-1cel</link>
      <guid>https://dev.to/nitinnair89/i-thought-my-api-was-secure-i-was-wrong-1cel</guid>
      <description>&lt;p&gt;I used to think API security was &lt;em&gt;mostly covered&lt;/em&gt; by auth middleware and HTTPS.&lt;/p&gt;

&lt;p&gt;If the token is valid, everything is good — right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wrong.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A few years ago, I was working on an API flow that looked solid on paper:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auth&lt;/li&gt;
&lt;li&gt;Role checks&lt;/li&gt;
&lt;li&gt;Clean endpoints&lt;/li&gt;
&lt;li&gt;Tests passing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then a production issue came in. Not a crash. Not downtime. Something worse.&lt;/p&gt;

&lt;p&gt;Data was being accessed… correctly. But by the wrong people.&lt;/p&gt;

&lt;p&gt;The endpoints were protected. The logic behind them wasn’t.&lt;/p&gt;

&lt;p&gt;We hadn’t violated rules.&lt;br&gt;
We had violated assumptions.&lt;/p&gt;

&lt;p&gt;That single incident rewired how I think about security.&lt;/p&gt;

&lt;p&gt;Not as &lt;em&gt;locking doors&lt;/em&gt; — but as designing systems where misuse is impossible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security is not a feature. It’s architecture.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It’s designing for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the developer who will maintain your code&lt;/li&gt;
&lt;li&gt;the user who will misuse it&lt;/li&gt;
&lt;li&gt;the attacker you never expected&lt;/li&gt;
&lt;li&gt;the API consumer who reads docs too literally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And today, I don’t build APIs assuming users behave correctly.&lt;/p&gt;

&lt;p&gt;I build assuming someone will try to break it.&lt;/p&gt;

&lt;p&gt;If you work with APIs — for money, identity, or user data —&lt;br&gt;
security is not something you “add later.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It’s part of your design signature.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>security</category>
      <category>architecture</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
