<?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: Kafeel Ahmad</title>
    <description>The latest articles on DEV Community by Kafeel Ahmad (@kafeel-ahmad).</description>
    <link>https://dev.to/kafeel-ahmad</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%2F2775308%2F2bb8ccc6-23b3-48c0-a1ce-40fb4fad5052.jpg</url>
      <title>DEV Community: Kafeel Ahmad</title>
      <link>https://dev.to/kafeel-ahmad</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kafeel-ahmad"/>
    <language>en</language>
    <item>
      <title>Building a Simple REST API with Express.js — The Right Way</title>
      <dc:creator>Kafeel Ahmad</dc:creator>
      <pubDate>Thu, 05 Feb 2026 16:20:30 +0000</pubDate>
      <link>https://dev.to/kafeel-ahmad/building-a-simple-rest-api-with-expressjs-the-right-way-5809</link>
      <guid>https://dev.to/kafeel-ahmad/building-a-simple-rest-api-with-expressjs-the-right-way-5809</guid>
      <description>&lt;p&gt;Most Node.js developers start with Express when learning backend development. But even experienced devs often overlook key architectural decisions that impact scalability, maintainability, and security.&lt;/p&gt;
&lt;p&gt;Today, we'll walk through building a clean, modular REST API using &lt;strong&gt;Express.js&lt;/strong&gt;, covering:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API structure&lt;/li&gt;
&lt;li&gt;Routing&lt;/li&gt;
&lt;li&gt;Controllers&lt;/li&gt;
&lt;li&gt;Middlewares&lt;/li&gt;
&lt;li&gt;Error handling&lt;/li&gt;
&lt;li&gt;Environment configs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="369c"&gt;🧱 Project Structure&lt;/h3&gt;
&lt;p&gt;Start with a clean structure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;project-root/
├── controllers/
├── routes/
├── middlewares/
├── models/
├── &lt;span class="hljs-built_in"&gt;config&lt;/span&gt;/
├── utils/
├── app.js
└── server.js&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This modular setup scales well for growing apps.&lt;/p&gt;
&lt;h3 id="8542"&gt;🧪 Step-by-Step: Create a Simple API&lt;/h3&gt;
&lt;h4 id="88cf"&gt;1. Install Express&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;npm &lt;span class="hljs-keyword"&gt;init&lt;/span&gt; -y
npm install express dotenv&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id="b700"&gt;2. Create &lt;code&gt;server.js&lt;/code&gt;
&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; app = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'./app'&lt;/span&gt;);
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; &lt;span class="hljs-variable constant_"&gt;PORT&lt;/span&gt; = process.&lt;span class="hljs-property"&gt;env&lt;/span&gt;.&lt;span class="hljs-property"&gt;PORT&lt;/span&gt; || &lt;span class="hljs-number"&gt;5000&lt;/span&gt;;

app.&lt;span class="hljs-title function_"&gt;listen&lt;/span&gt;(&lt;span class="hljs-variable constant_"&gt;PORT&lt;/span&gt;, &lt;span class="hljs-function"&gt;() =&amp;gt;&lt;/span&gt; {
  &lt;span class="hljs-variable language_"&gt;console&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;log&lt;/span&gt;(&lt;span class="hljs-string"&gt;`Server running on port &lt;span&gt;${PORT}&lt;/span&gt;`&lt;/span&gt;);
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id="6971"&gt;3. Create &lt;code&gt;app.js&lt;/code&gt;
&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; express = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'express'&lt;/span&gt;);
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; app = &lt;span class="hljs-title function_"&gt;express&lt;/span&gt;();
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; userRoutes = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'./routes/userRoutes'&lt;/span&gt;);

app.&lt;span class="hljs-title function_"&gt;use&lt;/span&gt;(express.&lt;span class="hljs-title function_"&gt;json&lt;/span&gt;());
app.&lt;span class="hljs-title function_"&gt;use&lt;/span&gt;(&lt;span class="hljs-string"&gt;'/api/users'&lt;/span&gt;, userRoutes);
&lt;span class="hljs-comment"&gt;// Global error handler&lt;/span&gt;
app.&lt;span class="hljs-title function_"&gt;use&lt;/span&gt;(&lt;span class="hljs-function"&gt;(&lt;span&gt;err, req, res, next&lt;/span&gt;) =&amp;gt;&lt;/span&gt; {
  res.&lt;span class="hljs-title function_"&gt;status&lt;/span&gt;(err.&lt;span class="hljs-property"&gt;status&lt;/span&gt; || &lt;span class="hljs-number"&gt;500&lt;/span&gt;).&lt;span class="hljs-title function_"&gt;json&lt;/span&gt;({ &lt;span class="hljs-attr"&gt;message&lt;/span&gt;: err.&lt;span class="hljs-property"&gt;message&lt;/span&gt; });
});
&lt;span class="hljs-variable language_"&gt;module&lt;/span&gt;.&lt;span class="hljs-property"&gt;exports&lt;/span&gt; = app;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id="2ba9"&gt;4. Add a Controller (&lt;code&gt;controllers/userController.js&lt;/code&gt;)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-built_in"&gt;exports&lt;/span&gt;.&lt;span class="hljs-property"&gt;getAllUsers&lt;/span&gt; = &lt;span class="hljs-function"&gt;(&lt;span&gt;req, res&lt;/span&gt;) =&amp;gt;&lt;/span&gt; {
  res.&lt;span class="hljs-title function_"&gt;json&lt;/span&gt;([{ &lt;span class="hljs-attr"&gt;id&lt;/span&gt;: &lt;span class="hljs-number"&gt;1&lt;/span&gt;, &lt;span class="hljs-attr"&gt;name&lt;/span&gt;: &lt;span class="hljs-string"&gt;'Dipak'&lt;/span&gt; }]);
};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id="1213"&gt;5. Add a Route (&lt;code&gt;routes/userRoutes.js&lt;/code&gt;)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; express = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'express'&lt;/span&gt;);
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; router = express.&lt;span class="hljs-title class_"&gt;Router&lt;/span&gt;();
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; userController = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'../controllers/userController'&lt;/span&gt;);

router.&lt;span class="hljs-title function_"&gt;get&lt;/span&gt;(&lt;span class="hljs-string"&gt;'/'&lt;/span&gt;, userController.&lt;span class="hljs-property"&gt;getAllUsers&lt;/span&gt;);
&lt;span class="hljs-variable language_"&gt;module&lt;/span&gt;.&lt;span class="hljs-property"&gt;exports&lt;/span&gt; = router;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="3c2f"&gt;🛡️ Add Environment Config&lt;/h3&gt;
&lt;ul&gt;&lt;li&gt;Create &lt;code&gt;.env&lt;/code&gt; file:&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-attr"&gt;PORT&lt;/span&gt;=&lt;span class="hljs-number"&gt;5000&lt;/span&gt;
&lt;span class="hljs-attr"&gt;NODE_ENV&lt;/span&gt;=development&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;&lt;li&gt;Install dotenv:&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;npm install dotenv&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;&lt;li&gt;Load it in &lt;code&gt;server.js&lt;/code&gt;:&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'dotenv'&lt;/span&gt;).&lt;span class="hljs-title function_"&gt;config&lt;/span&gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="92de"&gt;🔒 Add Error Handling Middleware&lt;/h3&gt;
&lt;p&gt;In &lt;code&gt;middlewares/errorHandler.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; &lt;span class="hljs-title function_"&gt;errorHandler&lt;/span&gt; = (&lt;span class="hljs-params"&gt;err, req, res, next&lt;/span&gt;) =&amp;gt; {
  &lt;span class="hljs-variable language_"&gt;console&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;error&lt;/span&gt;(err.&lt;span class="hljs-property"&gt;stack&lt;/span&gt;);
  res.&lt;span class="hljs-title function_"&gt;status&lt;/span&gt;(&lt;span class="hljs-number"&gt;500&lt;/span&gt;).&lt;span class="hljs-title function_"&gt;json&lt;/span&gt;({ &lt;span class="hljs-attr"&gt;message&lt;/span&gt;: &lt;span class="hljs-string"&gt;'Something went wrong!'&lt;/span&gt; });
};

&lt;span class="hljs-variable language_"&gt;module&lt;/span&gt;.&lt;span class="hljs-property"&gt;exports&lt;/span&gt; = errorHandler;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And in &lt;code&gt;app.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; errorHandler = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'./middlewares/errorHandler'&lt;/span&gt;);
app.&lt;span class="hljs-title function_"&gt;use&lt;/span&gt;(errorHandler);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="f8ed"&gt;📦 Bonus: Add CORS &amp;amp; Helmet for Security&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;npm install cors helmet
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; cors = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'cors'&lt;/span&gt;);
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; helmet = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'helmet'&lt;/span&gt;);

app.&lt;span class="hljs-title function_"&gt;use&lt;/span&gt;(&lt;span class="hljs-title function_"&gt;cors&lt;/span&gt;());
app.&lt;span class="hljs-title function_"&gt;use&lt;/span&gt;(&lt;span class="hljs-title function_"&gt;helmet&lt;/span&gt;());&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="d827"&gt;✅ Final Output&lt;/h3&gt;
&lt;p&gt;Once set up, run your server:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;node server.&lt;span class="hljs-property"&gt;js&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Visit:
 &lt;strong&gt;&lt;a rel="noopener noreferrer" title="" href="http://localhost:5000/api/users"&gt;http://localhost:5000/api/users&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You'll get:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-punctuation"&gt;[&lt;/span&gt;
  &lt;span class="hljs-punctuation"&gt;{&lt;/span&gt; &lt;span class="hljs-attr"&gt;"id"&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;:&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-attr"&gt;"name"&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;:&lt;/span&gt; &lt;span class="hljs-string"&gt;"Dipak"&lt;/span&gt; &lt;span class="hljs-punctuation"&gt;}&lt;/span&gt;
&lt;span class="hljs-punctuation"&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Clean, modular, and production-ready!&lt;/p&gt;
&lt;h3 id="ba3f"&gt;🔚 Final Thoughts&lt;/h3&gt;
&lt;p&gt;Building REST APIs in Node.js is simple — but doing it right requires planning.&lt;/p&gt;
&lt;p&gt;Start clean, modularize your logic, and build secure endpoints. You're not just learning Express — you're becoming a better backend engineer.&lt;/p&gt;

&lt;p&gt;Author: &lt;a href="https://medium.com/@dipaksahirav" rel="noopener noreferrer"&gt;Dipak Ahirav&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>Node.js in 2026: Modern Practices You Should Be Using</title>
      <dc:creator>Kafeel Ahmad</dc:creator>
      <pubDate>Fri, 09 Jan 2026 18:37:11 +0000</pubDate>
      <link>https://dev.to/kafeel-ahmad/nodejs-in-2026-modern-practices-you-should-be-using-18ik</link>
      <guid>https://dev.to/kafeel-ahmad/nodejs-in-2026-modern-practices-you-should-be-using-18ik</guid>
      <description>&lt;p&gt;Node.js continues to be a powerhouse for backend JavaScript applications, but how we write Node apps in 2025 vastly differs from just a few years ago. With the evolving JavaScript ecosystem and rapid tooling innovations, sticking to old patterns can hold you back.&lt;/p&gt;
&lt;p&gt;Here's a look at the modern Node.js practices you should use in 2025 to write cleaner, faster, and future-proof code.&lt;/p&gt;
&lt;h3 id="200a"&gt;1. ES Modules(ESM) First&lt;/h3&gt;
&lt;p&gt;In 2025, ES Modules are not just supported — they're the default.&lt;/p&gt;
&lt;h4 id="6d56"&gt;Why It Matters:&lt;/h4&gt;
&lt;p&gt;ESM allows for better interop with modern JavaScript tools, supports top-level &lt;code&gt;await&lt;/code&gt;, and aligns Node.js with browser-based module systems. It's time to ditch &lt;code&gt;require&lt;/code&gt; and embrace &lt;code&gt;import&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id="93a1"&gt;Best Practices:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;.mjs&lt;/code&gt; file extensions or set &lt;code&gt;"type": "module"&lt;/code&gt; In your &lt;code&gt;package.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Prefer &lt;code&gt;import&lt;/code&gt;/&lt;code&gt;export&lt;/code&gt; syntax over &lt;code&gt;require&lt;/code&gt;/&lt;code&gt;module.exports&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Take advantage of top-level &lt;code&gt;await&lt;/code&gt; to simplify async bootstrapping code.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-comment"&gt;// file name: app.mjs&lt;/span&gt;
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { express } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;'express'&lt;/span&gt;;

&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; app = &lt;span class="hljs-title function_"&gt;express&lt;/span&gt;();
app.&lt;span class="hljs-title function_"&gt;use&lt;/span&gt;(&lt;span class="hljs-string"&gt;"/"&lt;/span&gt;, &lt;span class="hljs-function"&gt;(&lt;span&gt;req, res&lt;/span&gt;)=&amp;gt;&lt;/span&gt;{
  res.&lt;span class="hljs-title function_"&gt;send&lt;/span&gt;(&lt;span class="hljs-string"&gt;"Hello World"&lt;/span&gt;);
});&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="8583"&gt;2. &lt;strong&gt;Native &lt;/strong&gt;&lt;strong&gt;&lt;code&gt;fetch&lt;/code&gt;&lt;/strong&gt;&lt;strong&gt; API Support&lt;/strong&gt;
&lt;/h3&gt;
&lt;p&gt;Node.js now ships with a native &lt;code&gt;fetch()&lt;/code&gt; implementation, just like in the browser.&lt;/p&gt;
&lt;h4 id="e700"&gt;Why It Matters:&lt;/h4&gt;
&lt;p&gt;No more installing &lt;code&gt;node-fetch&lt;/code&gt;. The global &lt;code&gt;fetch()&lt;/code&gt; API is built-in and fully standards-compliant.&lt;/p&gt;
&lt;h4 id="2c42"&gt;Best Practices:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;fetch()&lt;/code&gt; for HTTP requests instead of legacy HTTP libraries unless you need low-level control.&lt;/li&gt;
&lt;li&gt;Combine with &lt;code&gt;AbortController&lt;/code&gt; for request timeouts and cancellations.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; controller = &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; &lt;span class="hljs-title class_"&gt;AbortController&lt;/span&gt;();
&lt;span class="hljs-built_in"&gt;setTimeout&lt;/span&gt;(&lt;span class="hljs-function"&gt;() =&amp;gt;&lt;/span&gt; controller.&lt;span class="hljs-title function_"&gt;abort&lt;/span&gt;(), &lt;span class="hljs-number"&gt;5000&lt;/span&gt;);

&lt;span class="hljs-keyword"&gt;try&lt;/span&gt; {
  &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; res = &lt;span class="hljs-keyword"&gt;await&lt;/span&gt; &lt;span class="hljs-title function_"&gt;fetch&lt;/span&gt;(&lt;span class="hljs-string"&gt;'https://api.fakeapiexample.com/data'&lt;/span&gt;, {
    &lt;span class="hljs-attr"&gt;signal&lt;/span&gt;: controller.&lt;span class="hljs-property"&gt;signal&lt;/span&gt;
  });
  &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; data = &lt;span class="hljs-keyword"&gt;await&lt;/span&gt; res.&lt;span class="hljs-title function_"&gt;json&lt;/span&gt;();
  &lt;span class="hljs-variable language_"&gt;console&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;log&lt;/span&gt;(data);
} &lt;span class="hljs-keyword"&gt;catch&lt;/span&gt; (err) {
  &lt;span class="hljs-variable language_"&gt;console&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;error&lt;/span&gt;(&lt;span class="hljs-string"&gt;'Error in request is: '&lt;/span&gt;, err);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="7f05"&gt;3. Modern Testing with Vitest&lt;/h3&gt;
&lt;p&gt;The testing ecosystem has shifted. Jest is still relevant, but Vitest is the new favorite among developers working with modern JS and TypeScript.&lt;/p&gt;
&lt;h4 id="8ca8"&gt;Why It Matters:&lt;/h4&gt;
&lt;p&gt;Vitest is fast, supports ESM out of the box, has tight integration with Vite (though not required), and works seamlessly with TypeScript.&lt;/p&gt;
&lt;p&gt;Best Practices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use Vitest for unit, integration, and component testing.&lt;/li&gt;
&lt;li&gt;Leverage features like snapshot testing, mock support, and parallel test execution.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Install with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install -D vitest&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run tests with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx vitest run&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="8cd8"&gt;4. Bun Compatibility&lt;/h3&gt;
&lt;p&gt;Bun has emerged as a performance-focused alternative runtime for JavaScript and TypeScript. It's not a Node.js replacement (yet), but being Bun-compatible is quickly becoming a sign of a forward-thinking project.&lt;/p&gt;
&lt;h4 id="0e8e"&gt;Why It Matters:&lt;/h4&gt;
&lt;p&gt;Bun offers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Blazing-fast runtime and tooling&lt;/li&gt;
&lt;li&gt;Native TypeScript support&lt;/li&gt;
&lt;li&gt;Built-in bundler, test runner, and package manager&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="69b3"&gt;Best Practices:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Ensure your project runs on both Node and Bun.&lt;/li&gt;
&lt;li&gt;Avoid deep dependencies on Node-specific APIs unless necessary.&lt;/li&gt;
&lt;li&gt;Use polyfills or abstraction layers when possible.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;bun install
bun run index.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bun is outstanding and useful for running lightweight servers, build tools, or CLIs.&lt;/p&gt;
&lt;h3 id="efb1"&gt;5. &lt;strong&gt;TypeScript, Even for Small Projects&lt;/strong&gt;
&lt;/h3&gt;
&lt;img alt="None" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2ACHYfDV63-cGjkTrp" width="347" height="145"&gt;Zod can be used with Node.js for schema validations(Image Source: &lt;a rel="nofollow" title="" href="https://dev.to/osalumense/validating-request-data-in-expressjs-using-zod-a-comprehensive-guide-3a0j"&gt;https://dev.to/osalumense/validating-request-data-in-expressjs-using-zod-a-comprehensive-guide-3a0j&lt;/a&gt;)&lt;p&gt;By now, TypeScript isn't just a "nice-to-have." It's standard for serious Node.js development.&lt;/p&gt;
&lt;h4 id="c2c6"&gt;Why It Matters:&lt;/h4&gt;
&lt;p&gt;TypeScript allows to definition of different types of variables, function parameters, and arguments, which indeed help the compiler catch type-related errors during the development phase and help developers coming from other languages to use a Strictly Typed language.&lt;/p&gt;
&lt;p&gt;Although TypeScript will soon be using Golang inside the architecture, which will make it more fast and quick.&lt;/p&gt;
&lt;h4 id="29e3"&gt;Best Practices:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;tsconfig.json&lt;/code&gt; With strict mode enabled.&lt;/li&gt;
&lt;li&gt;Prefer interfaces and types for function contracts and data models.&lt;/li&gt;
&lt;li&gt;Combine with modern runtime validation tools &lt;code&gt;zod&lt;/code&gt; for safe, typed I/O.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { z } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;'zod'&lt;/span&gt;;

&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; &lt;span class="hljs-title class_"&gt;UserSchema&lt;/span&gt; = z.&lt;span class="hljs-title function_"&gt;object&lt;/span&gt;({
  &lt;span class="hljs-attr"&gt;name&lt;/span&gt;: z.&lt;span class="hljs-title function_"&gt;string&lt;/span&gt;(),
  &lt;span class="hljs-attr"&gt;age&lt;/span&gt;: z.&lt;span class="hljs-title function_"&gt;number&lt;/span&gt;().&lt;span class="hljs-title function_"&gt;min&lt;/span&gt;(&lt;span class="hljs-number"&gt;18&lt;/span&gt;),
});

&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; input = { &lt;span class="hljs-attr"&gt;name&lt;/span&gt;: &lt;span class="hljs-string"&gt;'Alice'&lt;/span&gt;, &lt;span class="hljs-attr"&gt;age&lt;/span&gt;: &lt;span class="hljs-number"&gt;25&lt;/span&gt; };
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; parsed = &lt;span class="hljs-title class_"&gt;UserSchema&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;parse&lt;/span&gt;(input);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="66fe"&gt;6. &lt;strong&gt;Dotenv is (Still) Your Friend — But Don't Hardcode&lt;/strong&gt;
&lt;/h3&gt;
&lt;p&gt;Managing configuration with &lt;code&gt;.env&lt;/code&gt; Files are still valid, but pair them with validation tools and secrets managers in production.&lt;/p&gt;
&lt;h4 id="ed22"&gt;&lt;strong&gt;Best Practices:&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;dotenv&lt;/code&gt; only in development or staging.&lt;/li&gt;
&lt;li&gt;Validate &lt;code&gt;config&lt;/code&gt; using libraries like &lt;code&gt;env-var&lt;/code&gt; or &lt;code&gt;zod&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Keep secrets out of version control.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { config } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;'dotenv'&lt;/span&gt;;
&lt;span class="hljs-title function_"&gt;config&lt;/span&gt;();

&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { cleanEnv, str, port } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;'envalid'&lt;/span&gt;;

&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; env = &lt;span class="hljs-title function_"&gt;cleanEnv&lt;/span&gt;(process.&lt;span class="hljs-property"&gt;env&lt;/span&gt;, {
  &lt;span class="hljs-attr"&gt;NODE_ENV&lt;/span&gt;: &lt;span class="hljs-title function_"&gt;str&lt;/span&gt;(),
  &lt;span class="hljs-attr"&gt;PORT&lt;/span&gt;: &lt;span class="hljs-title function_"&gt;port&lt;/span&gt;({ &lt;span class="hljs-attr"&gt;default&lt;/span&gt;: &lt;span class="hljs-number"&gt;3000&lt;/span&gt; }),
});&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="935f"&gt;7. &lt;strong&gt;Zero-Config Dev Environments&lt;/strong&gt;
&lt;/h3&gt;
&lt;p&gt;Tools like &lt;code&gt;tsx&lt;/code&gt; and &lt;code&gt;nodemon&lt;/code&gt; have given way to simpler workflows. Prefer zero-config CLIs like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;tsx:&lt;/code&gt; Run TS files directly with ESM support.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bun:&lt;/code&gt;Directly runs .ts and .js with built-in TypeScript support.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nodemon:&lt;/code&gt; It helps to run &lt;code&gt;.js&lt;/code&gt; file in the Node environment and helps in loading the app within milliseconds whenever we update the code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With tsx:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx tsx app.ts&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With Nodemon:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nodemon app.js&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="a394"&gt;8. &lt;strong&gt;Process Management with PM2: Keep Your Node App Alive&lt;/strong&gt;
&lt;/h3&gt;
&lt;p&gt;In production, your Node.js application needs to be resilient. Crashes, memory leaks, and restarts shouldn't bring your service down. &lt;strong&gt;PM2&lt;/strong&gt; (Process Manager 2) remains one of the most reliable tools for keeping your app alive and well-managed.&lt;/p&gt;
&lt;h4 id="29a7"&gt;Why It Matters:&lt;/h4&gt;
&lt;p&gt;PM2 handles process restarts, monitoring, clustering, and log management with zero downtime. It's especially helpful when running apps outside containerized environments or on self-managed servers.&lt;/p&gt;
&lt;h4 id="30e4"&gt;Best Practices:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Use PM2 to run and monitor your app in the background.&lt;/li&gt;
&lt;li&gt;Enable clustering to take advantage of multi-core CPUs.&lt;/li&gt;
&lt;li&gt;Use log rotation and process auto-restart features.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;npm install -g pm2
pm2 start app.js --name my-app&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cluster Mode Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pm2 start app.js -i max&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-i max:&lt;/code&gt; Spawns as many instances as CPU cores (uses Node.js &lt;code&gt;cluster&lt;/code&gt; module under the hood).&lt;/li&gt;
&lt;li&gt;View logs with pm2 logs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PM2 also supports ecosystem configuration files and integrates with Docker and CI/CD pipelines.&lt;/p&gt;
&lt;h3 id="dd3b"&gt;Final Thoughts:&lt;/h3&gt;
&lt;p&gt;Node.js in 2025 is fast, modern, and cleaner than ever before — but only if you're keeping up. Embrace ES Modules, native APIs, and cutting-edge tools like Vitest and Bun. These practices won't just make your code better — they'll make you a more effective developer.&lt;/p&gt;
&lt;p&gt;Whether you're building REST APIs, CLIs, or full-stack apps, the future of Node.js is modular, fast, and typed.&lt;/p&gt;
&lt;p&gt;Great, you have reached the end. Thanks for Reading.&lt;/p&gt;

&lt;p&gt;Author: &lt;a href="https://medium.com/@gargaryan82000" rel="noopener noreferrer"&gt;Aryan Garg&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>node</category>
      <category>javascript</category>
      <category>backend</category>
    </item>
    <item>
      <title>Node.js Architecture Patterns for Scalable Apps (2026 Guide)</title>
      <dc:creator>Kafeel Ahmad</dc:creator>
      <pubDate>Fri, 09 Jan 2026 18:31:15 +0000</pubDate>
      <link>https://dev.to/kafeel-ahmad/nodejs-architecture-patterns-for-scalable-apps-2026-guide-f3h</link>
      <guid>https://dev.to/kafeel-ahmad/nodejs-architecture-patterns-for-scalable-apps-2026-guide-f3h</guid>
      <description>&lt;p&gt;Node.js has become the go-to choice for building high-performance, scalable apps — especially real-time systems, APIs, and microservices. But as your app grows in traffic and complexity, &lt;strong&gt;architecture matters more than ever&lt;/strong&gt;. Without a proper structure, your codebase becomes a spaghetti mess, bugs multiply, and scaling feels impossible.&lt;/p&gt;
&lt;p&gt;In this guide, we'll explore the &lt;strong&gt;best Node.js architecture patterns for 2026&lt;/strong&gt;, explain when to use them, and provide simple examples so you can pick the right fit for your next big project.&lt;/p&gt;
&lt;h3 id="45be"&gt;Why Architecture Matters in Node.js&lt;/h3&gt;
&lt;p&gt;Node.js is built on a &lt;strong&gt;single-threaded, event-driven model&lt;/strong&gt;. This makes it excellent for handling &lt;strong&gt;thousands of concurrent requests&lt;/strong&gt; without heavy hardware. However:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Poor structure → Harder to maintain as teams grow&lt;/li&gt;
&lt;li&gt;No separation of concerns → Slower development&lt;/li&gt;
&lt;li&gt;Missing scalability strategy → App crashes under high traffic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Choosing the right &lt;strong&gt;architecture pattern&lt;/strong&gt; ensures:
 ✅ Easy feature expansion
 ✅ High code maintainability
 ✅ Better fault tolerance
 ✅ Simple onboarding for new developers&lt;/p&gt;
&lt;h3 id="df46"&gt;1. Layered (Monolithic) Architecture&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Small to medium apps, MVPs, startups with limited resources&lt;/p&gt;
&lt;p&gt;This is the &lt;strong&gt;classic "all-in-one" pattern&lt;/strong&gt; where all app logic (routes, controllers, services, database calls) lives in a single codebase.&lt;/p&gt;
&lt;p&gt;Example Structure:&lt;/p&gt;
&lt;pre&gt;&lt;code class="p-2 bg-gray-100 dark:bg-gray-900 overflow-x-auto nohighlight"&gt;/app
  /controllers
  /models
  /routes
  /services
server.js&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example Route (Express.js):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-comment"&gt;// routes/userRoutes.js&lt;/span&gt;
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; express = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'express'&lt;/span&gt;);
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; router = express.&lt;span class="hljs-title class_"&gt;Router&lt;/span&gt;();
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; userController = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'../controllers/userController'&lt;/span&gt;);

router.&lt;span class="hljs-title function_"&gt;get&lt;/span&gt;(&lt;span class="hljs-string"&gt;'/users'&lt;/span&gt;, userController.&lt;span class="hljs-property"&gt;getUsers&lt;/span&gt;);

&lt;span class="hljs-variable language_"&gt;module&lt;/span&gt;.&lt;span class="hljs-property"&gt;exports&lt;/span&gt; = router;

&lt;span class="hljs-comment"&gt;// controllers/userController.js&lt;/span&gt;
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; &lt;span class="hljs-title class_"&gt;User&lt;/span&gt; = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'../models/User'&lt;/span&gt;);
&lt;span class="hljs-built_in"&gt;exports&lt;/span&gt;.&lt;span class="hljs-property"&gt;getUsers&lt;/span&gt; = &lt;span class="hljs-keyword"&gt;async&lt;/span&gt; (req, res) =&amp;gt; {
  &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; users = &lt;span class="hljs-keyword"&gt;await&lt;/span&gt; &lt;span class="hljs-title class_"&gt;User&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;find&lt;/span&gt;();
  res.&lt;span class="hljs-title function_"&gt;json&lt;/span&gt;(users);
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; Easy to start, simple deployment
 &lt;strong&gt;Cons:&lt;/strong&gt; Harder to scale when traffic or code size grows&lt;/p&gt;


&lt;h3 id="908e"&gt;2. Microservices Architecture&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Large-scale apps, SaaS products, systems with multiple teams&lt;/p&gt;
&lt;p&gt;Microservices split your app into &lt;strong&gt;independent services&lt;/strong&gt; (Auth, Payments, Notifications). Each service runs on its own process, database, and scaling strategy.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auth Service:&lt;/strong&gt; Manages user login/registration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Order Service:&lt;/strong&gt; Handles transactions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notification Service:&lt;/strong&gt; Sends emails/SMS&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-comment"&gt;// Example: Notification microservice&lt;/span&gt;&lt;br&gt;
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; express = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'express'&lt;/span&gt;);&lt;br&gt;
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; app = &lt;span class="hljs-title function_"&gt;express&lt;/span&gt;();

&lt;p&gt;app.&lt;span&gt;post&lt;/span&gt;(&lt;span&gt;'/send-email'&lt;/span&gt;, &lt;span&gt;(&lt;span&gt;req, res&lt;/span&gt;) =&amp;gt;&lt;/span&gt; {&lt;br&gt;
  &lt;span&gt;// Email sending logic&lt;/span&gt;&lt;br&gt;
  res.&lt;span&gt;send&lt;/span&gt;(&lt;span&gt;'Email sent!'&lt;/span&gt;);&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;app.&lt;span&gt;listen&lt;/span&gt;(&lt;span&gt;4000&lt;/span&gt;, &lt;span&gt;() =&amp;gt;&lt;/span&gt; &lt;span&gt;console&lt;/span&gt;.&lt;span&gt;log&lt;/span&gt;(&lt;span&gt;'Notification Service running on port 4000'&lt;/span&gt;));&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Services communicate via &lt;strong&gt;REST, gRPC, or message brokers&lt;/strong&gt; (Kafka, RabbitMQ).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; High scalability, independent deployments&lt;br&gt;
 &lt;strong&gt;Cons:&lt;/strong&gt; More complex infrastructure and DevOps setup&lt;/p&gt;
&lt;h3 id="8f24"&gt;3. Event-Driven Architecture&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Real-time apps (chat, trading platforms, IoT, live dashboards)&lt;/p&gt;
&lt;p&gt;Instead of calling services directly, components &lt;strong&gt;publish and subscribe to events&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;User creates an order → Emits &lt;code&gt;order_created&lt;/code&gt; event&lt;/li&gt;
&lt;li&gt;Payment Service listens to &lt;code&gt;order_created&lt;/code&gt; → Processes payment&lt;/li&gt;
&lt;li&gt;Notification Service listens → Sends confirmation email&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-comment"&gt;// Using Node.js EventEmitter&lt;/span&gt;&lt;br&gt;
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; &lt;span class="hljs-title class_"&gt;EventEmitter&lt;/span&gt; = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'events'&lt;/span&gt;);&lt;br&gt;
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; eventBus = &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; &lt;span class="hljs-title class_"&gt;EventEmitter&lt;/span&gt;();

&lt;p&gt;eventBus.&lt;span&gt;on&lt;/span&gt;(&lt;span&gt;'order_created'&lt;/span&gt;, &lt;span&gt;(&lt;span&gt;order&lt;/span&gt;) =&amp;gt;&lt;/span&gt; {&lt;br&gt;
  &lt;span&gt;console&lt;/span&gt;.&lt;span&gt;log&lt;/span&gt;(&lt;span&gt;&lt;code&gt;Order Received: &amp;lt;span class="hljs-subst"&amp;gt;${order.id}&amp;lt;/span&amp;gt;&lt;/code&gt;&lt;/span&gt;);&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;&lt;span&gt;// Simulate event&lt;/span&gt;&lt;br&gt;
eventBus.&lt;span&gt;emit&lt;/span&gt;(&lt;span&gt;'order_created'&lt;/span&gt;, { &lt;span&gt;id&lt;/span&gt;: &lt;span&gt;123&lt;/span&gt; });&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; Loosely coupled services, great for real-time apps&lt;br&gt;
 &lt;strong&gt;Cons:&lt;/strong&gt; Harder debugging, requires message brokers at scale&lt;/p&gt;
&lt;br&gt;
&lt;h3 id="1156"&gt;4. Serverless Architecture&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Startups, apps with unpredictable traffic, cost optimization&lt;/p&gt;
&lt;p&gt;Instead of managing servers, you deploy &lt;strong&gt;functions&lt;/strong&gt; (AWS Lambda, Azure Functions) that run on demand.&lt;/p&gt;
&lt;p&gt;Example: AWS Lambda Function&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-built_in"&gt;exports&lt;/span&gt;.&lt;span class="hljs-property"&gt;handler&lt;/span&gt; = &lt;span class="hljs-keyword"&gt;async&lt;/span&gt; (event) =&amp;gt; {&lt;br&gt;
  &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; name = event.&lt;span class="hljs-property"&gt;queryStringParameters&lt;/span&gt;.&lt;span class="hljs-property"&gt;name&lt;/span&gt; || &lt;span class="hljs-string"&gt;"Guest"&lt;/span&gt;;&lt;br&gt;
  &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; {&lt;br&gt;
    &lt;span class="hljs-attr"&gt;statusCode&lt;/span&gt;: &lt;span class="hljs-number"&gt;200&lt;/span&gt;,&lt;br&gt;
    &lt;span class="hljs-attr"&gt;body&lt;/span&gt;: &lt;span class="hljs-title class_"&gt;JSON&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;stringify&lt;/span&gt;({ &lt;span class="hljs-attr"&gt;message&lt;/span&gt;: &lt;span class="hljs-string"&gt;&lt;code&gt;Hello, &amp;lt;span class="hljs-subst"&amp;gt;${name}&amp;lt;/span&amp;gt;!&lt;/code&gt;&lt;/span&gt; }),&lt;br&gt;
  };&lt;br&gt;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; No server management, auto-scaling, pay-per-use&lt;br&gt;
 &lt;strong&gt;Cons:&lt;/strong&gt; Cold starts, not ideal for high-frequency APIs&lt;/p&gt;
&lt;h3 id="5c33"&gt;5. Clean / Hexagonal Architecture&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Enterprise-grade apps, long-term maintainability&lt;/p&gt;
&lt;p&gt;Separates &lt;strong&gt;business logic&lt;/strong&gt; from frameworks, making it easier to swap databases, UI frameworks, or third-party APIs.&lt;/p&gt;
&lt;p&gt;Example Layers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Domain Layer:&lt;/strong&gt; Business rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application Layer:&lt;/strong&gt; Service orchestration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure Layer:&lt;/strong&gt; Database, API adapters&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This ensures your &lt;strong&gt;core logic doesn't depend on Express or MongoDB&lt;/strong&gt; — you can change tech stacks without rewriting everything.&lt;/p&gt;
&lt;h3 id="4070"&gt;Scaling Node.js Beyond Architecture&lt;/h3&gt;
&lt;p&gt;Even with the best architecture, scaling Node.js apps requires extra techniques:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Horizontal Scaling&lt;/strong&gt;: Use PM2 or Kubernetes to run multiple Node.js instances&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching&lt;/strong&gt;: Redis or Memcached to reduce DB load&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load Balancing&lt;/strong&gt;: Nginx, AWS ALB, or HAProxy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database Optimization&lt;/strong&gt;: Indexing, sharding, read replicas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring&lt;/strong&gt;: Tools like Prometheus, Grafana, and New Relic&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="496c"&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;Node.js is a powerhouse for building &lt;strong&gt;high-traffic, real-time, and enterprise apps&lt;/strong&gt;. Choosing the right architecture pattern in 2026 depends on your &lt;strong&gt;team size, app complexity, and scaling needs&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;PatternBest ForLayered MonolithMVPs, StartupsMicroservicesLarge, Complex AppsEvent-DrivenReal-Time SystemsServerlessLow-maintenance, Cost-EffectiveClean/HexagonalEnterprise &amp;amp; Long-term Projects&lt;/p&gt;
&lt;p&gt;If you're starting small, begin with &lt;strong&gt;Layered Architecture&lt;/strong&gt;. As your user base grows, evolve into &lt;strong&gt;Microservices&lt;/strong&gt; or &lt;strong&gt;Event-Driven&lt;/strong&gt; systems.&lt;/p&gt;

&lt;p&gt;Author: &lt;a href="https://medium.com/@somendradev23" rel="noopener noreferrer"&gt;Somendradev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Domain-Driven Design (DDD) with Node.js: Best Practices for Large Projects</title>
      <dc:creator>Kafeel Ahmad</dc:creator>
      <pubDate>Wed, 07 Jan 2026 18:11:37 +0000</pubDate>
      <link>https://dev.to/kafeel-ahmad/domain-driven-design-ddd-with-nodejs-best-practices-for-large-projects-2290</link>
      <guid>https://dev.to/kafeel-ahmad/domain-driven-design-ddd-with-nodejs-best-practices-for-large-projects-2290</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Endless spaghetti code spread across controllers and services.&lt;/li&gt;
&lt;li&gt;Business logic mixed with database queries.&lt;/li&gt;
&lt;li&gt;Every bug fix breaking something completely unrelated.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's where &lt;strong&gt;Domain-Driven Design (DDD)&lt;/strong&gt; comes to the rescue.&lt;/p&gt;
&lt;p&gt;DDD isn't just a fancy buzzword — it's a mindset and methodology for organizing complex software. In this post, I'll walk you through &lt;strong&gt;what DDD is, why it matters in Node.js projects, and how to implement it step by step with examples.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="e191"&gt;What is Domain-Driven Design (DDD)?&lt;/h3&gt;
&lt;p&gt;At its core, DDD is about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Focusing on the &lt;strong&gt;business domain&lt;/strong&gt; (the real-world problems your app solves).&lt;/li&gt;
&lt;li&gt;Structuring your code around &lt;strong&gt;domain concepts&lt;/strong&gt; (users, orders, payments) instead of technical layers.&lt;/li&gt;
&lt;li&gt;Creating a &lt;strong&gt;shared language&lt;/strong&gt; between developers and business stakeholders.&lt;p&gt;Instead of thinking in terms of &lt;em&gt;controllers, services, repositories&lt;/em&gt;, you think in terms of &lt;strong&gt;domains, entities, and use cases.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="55fc"&gt;Why Use DDD in Node.js?&lt;/h3&gt;
&lt;p&gt;Large Node.js projects often grow messy because of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rapid feature additions.&lt;/li&gt;
&lt;li&gt;Too much logic in controllers.&lt;/li&gt;
&lt;li&gt;Mixed responsibilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DDD helps by:
✅ Keeping &lt;strong&gt;business logic clean and testable&lt;/strong&gt;.
✅ Making it easier to &lt;strong&gt;scale your team&lt;/strong&gt; — devs can own specific domains.
✅ Allowing &lt;strong&gt;future flexibility&lt;/strong&gt; (swap DBs, frameworks, or message queues without rewriting core logic).&lt;/p&gt;

&lt;h3 id="de0d"&gt;Core Building Blocks of DDD&lt;/h3&gt;

&lt;p&gt;Here are the &lt;strong&gt;main DDD concepts&lt;/strong&gt; and how they translate into Node.js projects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Entity&lt;/strong&gt; → An object with identity (e.g., &lt;code&gt;User&lt;/code&gt;, &lt;code&gt;Order&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value Object&lt;/strong&gt; → Immutable data without identity (e.g., &lt;code&gt;Email&lt;/code&gt;, &lt;code&gt;Price&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aggregate&lt;/strong&gt; → A cluster of entities treated as one unit (e.g., &lt;code&gt;Order&lt;/code&gt; with &lt;code&gt;OrderItems&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repository&lt;/strong&gt; → Interface for data access (DB is an implementation detail).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service / Use Case&lt;/strong&gt; → Business logic operations (e.g., &lt;code&gt;PlaceOrder&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain Events&lt;/strong&gt; → Something that happened in the domain (e.g., &lt;code&gt;OrderPlaced&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id="1ed0"&gt;Node.js Project Structure with DDD&lt;/h3&gt;

&lt;p&gt;Here's how you might structure a DDD-inspired Node.js project:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;src/
├── modules/
│    ├── &lt;span class="hljs-built_in"&gt;users&lt;/span&gt;/
│    │    ├── domain/
│    │    │    ├── User.js
│    │    │    ├── Email.js
│    │    ├── application/
│    │    │    ├── RegisterUser.js
│    │    ├── infrastructure/
│    │    │    ├── MongoUserRepository.js
│    │    └── index.js
│    ├── orders/
│    │    ├── domain/
│    │    ├── application/
│    │    ├── infrastructure/
├── shared/
│    ├── domain/
│    └── utils/&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice how each domain (like &lt;code&gt;users&lt;/code&gt;, &lt;code&gt;orders&lt;/code&gt;) has its own &lt;strong&gt;domain, application, and infrastructure&lt;/strong&gt; layers.&lt;/p&gt;

&lt;h3 id="1c49"&gt;Example: User Registration in DDD&lt;/h3&gt;

&lt;h3 id="5723"&gt;1. Entity: User&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-comment"&gt;// modules/users/domain/User.js&lt;/span&gt;
export &lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;User&lt;/span&gt; {
&lt;span class="hljs-keyword"&gt;constructor&lt;/span&gt;({ id, name, email }) {
&lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.id = id;
&lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.name = name;
&lt;span class="hljs-keyword"&gt;this&lt;/span&gt;.email = email;
}
}&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="8043"&gt;2. Value Object: Email&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-comment"&gt;// modules/users/domain/Email.js&lt;/span&gt;
&lt;span class="hljs-keyword"&gt;export&lt;/span&gt; &lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;Email&lt;/span&gt; {
&lt;span class="hljs-title function_"&gt;constructor&lt;/span&gt;(&lt;span class="hljs-params"&gt;address&lt;/span&gt;) {
&lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (!address.&lt;span class="hljs-title function_"&gt;includes&lt;/span&gt;(&lt;span class="hljs-string"&gt;"@"&lt;/span&gt;)) &lt;span class="hljs-keyword"&gt;throw&lt;/span&gt; &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; &lt;span class="hljs-title class_"&gt;Error&lt;/span&gt;(&lt;span class="hljs-string"&gt;"Invalid email"&lt;/span&gt;);
&lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;address&lt;/span&gt; = address;
}
}&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="3d59"&gt;3. Repository Interface&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-comment"&gt;// modules/users/domain/UserRepository.js&lt;/span&gt;
&lt;span class="hljs-keyword"&gt;export&lt;/span&gt; &lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;UserRepository&lt;/span&gt; {
&lt;span class="hljs-keyword"&gt;async&lt;/span&gt; &lt;span class="hljs-title function_"&gt;save&lt;/span&gt;(&lt;span class="hljs-params"&gt;user&lt;/span&gt;) {
&lt;span class="hljs-keyword"&gt;throw&lt;/span&gt; &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; &lt;span class="hljs-title class_"&gt;Error&lt;/span&gt;(&lt;span class="hljs-string"&gt;"Method not implemented"&lt;/span&gt;);
}&lt;/code&gt;&lt;/pre&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;span&gt;async&lt;/span&gt; &lt;span&gt;findByEmail&lt;/span&gt;(&lt;span&gt;email&lt;/span&gt;) {&lt;br&gt;
    &lt;span&gt;throw&lt;/span&gt; &lt;span&gt;new&lt;/span&gt; &lt;span&gt;Error&lt;/span&gt;(&lt;span&gt;"Method not implemented"&lt;/span&gt;);&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;
&lt;h3 id="52dd"&gt;4. Application Service (Use Case): RegisterUser&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-comment"&gt;// modules/users/application/RegisterUser.js&lt;/span&gt;&lt;br&gt;
&lt;span class="hljs-keyword"&gt;export&lt;/span&gt; &lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;RegisterUser&lt;/span&gt; {&lt;br&gt;
  &lt;span class="hljs-title function_"&gt;constructor&lt;/span&gt;(&lt;span class="hljs-params"&gt;userRepository&lt;/span&gt;) {&lt;br&gt;
    &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;userRepository&lt;/span&gt; = userRepository;&lt;br&gt;
  }

&lt;p&gt;&lt;span&gt;async&lt;/span&gt; &lt;span&gt;execute&lt;/span&gt;(&lt;span&gt;{ name, email }&lt;/span&gt;) {&lt;br&gt;
    &lt;span&gt;const&lt;/span&gt; existing = &lt;span&gt;await&lt;/span&gt; &lt;span&gt;this&lt;/span&gt;.&lt;span&gt;userRepository&lt;/span&gt;.&lt;span&gt;findByEmail&lt;/span&gt;(email);&lt;br&gt;
    &lt;span&gt;if&lt;/span&gt; (existing) &lt;span&gt;throw&lt;/span&gt; &lt;span&gt;new&lt;/span&gt; &lt;span&gt;Error&lt;/span&gt;(&lt;span&gt;"User already exists"&lt;/span&gt;);&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;span class="hljs-keyword"&amp;gt;const&amp;lt;/span&amp;gt; user = { &amp;lt;span class="hljs-attr"&amp;gt;id&amp;lt;/span&amp;gt;: &amp;lt;span class="hljs-title class_"&amp;gt;Date&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-title function_"&amp;gt;now&amp;lt;/span&amp;gt;().&amp;lt;span class="hljs-title function_"&amp;gt;toString&amp;lt;/span&amp;gt;(), name, email };
&amp;lt;span class="hljs-keyword"&amp;gt;await&amp;lt;/span&amp;gt; &amp;lt;span class="hljs-variable language_"&amp;gt;this&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-property"&amp;gt;userRepository&amp;lt;/span&amp;gt;.&amp;lt;span class="hljs-title function_"&amp;gt;save&amp;lt;/span&amp;gt;(user);
&amp;lt;span class="hljs-keyword"&amp;gt;return&amp;lt;/span&amp;gt; user;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;}&lt;br&gt;
}&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="815d"&gt;5. Infrastructure: MongoDB Repository Implementation&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-comment"&gt;// modules/users/infrastructure/MongoUserRepository.js&lt;/span&gt;&lt;br&gt;
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { &lt;span class="hljs-title class_"&gt;UserRepository&lt;/span&gt; } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;"../domain/UserRepository.js"&lt;/span&gt;;

&lt;p&gt;&lt;span&gt;export&lt;/span&gt; &lt;span&gt;class&lt;/span&gt; &lt;span&gt;MongoUserRepository&lt;/span&gt; &lt;span&gt;extends&lt;/span&gt; &lt;span&gt;UserRepository&lt;/span&gt; {&lt;br&gt;
  &lt;span&gt;constructor&lt;/span&gt;(&lt;span&gt;mongoClient&lt;/span&gt;) {&lt;br&gt;
    &lt;span&gt;super&lt;/span&gt;();&lt;br&gt;
    &lt;span&gt;this&lt;/span&gt;.&lt;span&gt;collection&lt;/span&gt; = mongoClient.&lt;span&gt;db&lt;/span&gt;().&lt;span&gt;collection&lt;/span&gt;(&lt;span&gt;"users"&lt;/span&gt;);&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;&lt;span&gt;async&lt;/span&gt; &lt;span&gt;save&lt;/span&gt;(&lt;span&gt;user&lt;/span&gt;) {&lt;br&gt;
    &lt;span&gt;await&lt;/span&gt; &lt;span&gt;this&lt;/span&gt;.&lt;span&gt;collection&lt;/span&gt;.&lt;span&gt;insertOne&lt;/span&gt;(user);&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;&lt;span&gt;async&lt;/span&gt; &lt;span&gt;findByEmail&lt;/span&gt;(&lt;span&gt;email&lt;/span&gt;) {&lt;br&gt;
    &lt;span&gt;return&lt;/span&gt; &lt;span&gt;await&lt;/span&gt; &lt;span&gt;this&lt;/span&gt;.&lt;span&gt;collection&lt;/span&gt;.&lt;span&gt;findOne&lt;/span&gt;({ email });&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="1788"&gt;6. Connecting Everything&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; express &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;"express"&lt;/span&gt;;&lt;br&gt;
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { &lt;span class="hljs-title class_"&gt;MongoClient&lt;/span&gt; } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;"mongodb"&lt;/span&gt;;&lt;br&gt;
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { &lt;span class="hljs-title class_"&gt;MongoUserRepository&lt;/span&gt; } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;"./modules/users/infrastructure/MongoUserRepository.js"&lt;/span&gt;;&lt;br&gt;
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { &lt;span class="hljs-title class_"&gt;RegisterUser&lt;/span&gt; } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;"./modules/users/application/RegisterUser.js"&lt;/span&gt;;

&lt;p&gt;&lt;span&gt;async&lt;/span&gt; &lt;span&gt;function&lt;/span&gt; &lt;span&gt;main&lt;/span&gt;(&lt;span&gt;&lt;/span&gt;) {&lt;br&gt;
  &lt;span&gt;const&lt;/span&gt; app = &lt;span&gt;express&lt;/span&gt;();&lt;br&gt;
  app.&lt;span&gt;use&lt;/span&gt;(express.&lt;span&gt;json&lt;/span&gt;());&lt;/p&gt;

&lt;p&gt;&lt;span&gt;const&lt;/span&gt; mongo = &lt;span&gt;new&lt;/span&gt; &lt;span&gt;MongoClient&lt;/span&gt;(&lt;span&gt;"mongodb://localhost:27017"&lt;/span&gt;);&lt;br&gt;
  &lt;span&gt;await&lt;/span&gt; mongo.&lt;span&gt;connect&lt;/span&gt;();&lt;/p&gt;

&lt;p&gt;&lt;span&gt;const&lt;/span&gt; userRepository = &lt;span&gt;new&lt;/span&gt; &lt;span&gt;MongoUserRepository&lt;/span&gt;(mongo);&lt;br&gt;
  &lt;span&gt;const&lt;/span&gt; registerUser = &lt;span&gt;new&lt;/span&gt; &lt;span&gt;RegisterUser&lt;/span&gt;(userRepository);&lt;/p&gt;

&lt;p&gt;app.&lt;span&gt;post&lt;/span&gt;(&lt;span&gt;"/register"&lt;/span&gt;, &lt;span&gt;async&lt;/span&gt; (req, res) =&amp;gt; {&lt;br&gt;
    &lt;span&gt;try&lt;/span&gt; {&lt;br&gt;
      &lt;span&gt;const&lt;/span&gt; user = &lt;span&gt;await&lt;/span&gt; registerUser.&lt;span&gt;execute&lt;/span&gt;(req.&lt;span&gt;body&lt;/span&gt;);&lt;br&gt;
      res.&lt;span&gt;status&lt;/span&gt;(&lt;span&gt;201&lt;/span&gt;).&lt;span&gt;json&lt;/span&gt;(user);&lt;br&gt;
    } &lt;span&gt;catch&lt;/span&gt; (err) {&lt;br&gt;
      res.&lt;span&gt;status&lt;/span&gt;(&lt;span&gt;400&lt;/span&gt;).&lt;span&gt;json&lt;/span&gt;({ &lt;span&gt;error&lt;/span&gt;: err.&lt;span&gt;message&lt;/span&gt; });&lt;br&gt;
    }&lt;br&gt;
  });&lt;/p&gt;

&lt;p&gt;app.&lt;span&gt;listen&lt;/span&gt;(&lt;span&gt;3000&lt;/span&gt;, &lt;span&gt;() =&amp;gt;&lt;/span&gt; &lt;span&gt;console&lt;/span&gt;.&lt;span&gt;log&lt;/span&gt;(&lt;span&gt;"Server running on port 3000"&lt;/span&gt;));&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;&lt;span&gt;main&lt;/span&gt;();&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="594c"&gt;Best Practices for DDD in Node.js&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Keep domains independent&lt;/strong&gt; → No direct calls between &lt;code&gt;users&lt;/code&gt; and &lt;code&gt;orders&lt;/code&gt;. Use events or application services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate concerns&lt;/strong&gt; → Business logic (domain) should not depend on infrastructure (DB, HTTP).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Value Objects generously&lt;/strong&gt; → They enforce domain rules early.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Introduce Domain Events&lt;/strong&gt; → Let domains communicate via events (&lt;code&gt;UserRegistered&lt;/code&gt;, &lt;code&gt;OrderPlaced&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Think in Ubiquitous Language&lt;/strong&gt; → Name your classes, methods, and events in a way business folks understand.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="0dca"&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;Domain-Driven Design might feel like &lt;strong&gt;extra ceremony&lt;/strong&gt; when you start, but for large projects, it's a lifesaver. You'll end up with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Modular architecture&lt;/strong&gt; that scales with teams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainable business logic&lt;/strong&gt; that's testable and framework-agnostic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Future-proof flexibility&lt;/strong&gt; — swap Express, MongoDB, or Redis without touching core logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So next time you're kicking off a big Node.js project, try structuring it with DDD. It'll keep your codebase healthy for years to come.&lt;/p&gt;

&lt;p&gt;Author: &lt;a href="https://medium.com/@somendradev23" rel="noopener noreferrer"&gt;Somendradev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>node</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Building a Simple REST API with Express.js — The Right Way</title>
      <dc:creator>Kafeel Ahmad</dc:creator>
      <pubDate>Wed, 07 Jan 2026 18:06:43 +0000</pubDate>
      <link>https://dev.to/kafeel-ahmad/building-a-simple-rest-api-with-expressjs-the-right-way-jjk</link>
      <guid>https://dev.to/kafeel-ahmad/building-a-simple-rest-api-with-expressjs-the-right-way-jjk</guid>
      <description>&lt;p&gt;Most Node.js developers start with Express when learning backend development. But even experienced devs often overlook key architectural decisions that impact scalability, maintainability, and security.&lt;/p&gt;
&lt;p&gt;Today, we'll walk through building a clean, modular REST API using &lt;strong&gt;Express.js&lt;/strong&gt;, covering:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API structure&lt;/li&gt;
&lt;li&gt;Routing&lt;/li&gt;
&lt;li&gt;Controllers&lt;/li&gt;
&lt;li&gt;Middlewares&lt;/li&gt;
&lt;li&gt;Error handling&lt;/li&gt;
&lt;li&gt;Environment configs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;a rel="noopener noreferrer" title="" href="https://blog.stackademic.com/building-a-simple-rest-api-with-express-js-the-right-way-ec09ee1c3d37?sk=281b13e98ae11a2a61d790179514deb7"&gt;Not a Member? Read for FREE here.&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="369c"&gt;🧱 Project Structure&lt;/h3&gt;
&lt;p&gt;Start with a clean structure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;project-root/
├── controllers/
├── routes/
├── middlewares/
├── models/
├── &lt;span class="hljs-built_in"&gt;config&lt;/span&gt;/
├── utils/
├── app.js
└── server.js&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This modular setup scales well for growing apps.&lt;/p&gt;
&lt;h3 id="8542"&gt;🧪 Step-by-Step: Create a Simple API&lt;/h3&gt;
&lt;h4 id="88cf"&gt;1. Install Express&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;npm &lt;span class="hljs-keyword"&gt;init&lt;/span&gt; -y
npm install express dotenv&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id="b700"&gt;2. Create &lt;code&gt;server.js&lt;/code&gt;
&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; app = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'./app'&lt;/span&gt;);
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; &lt;span class="hljs-variable constant_"&gt;PORT&lt;/span&gt; = process.&lt;span class="hljs-property"&gt;env&lt;/span&gt;.&lt;span class="hljs-property"&gt;PORT&lt;/span&gt; || &lt;span class="hljs-number"&gt;5000&lt;/span&gt;;

app.&lt;span class="hljs-title function_"&gt;listen&lt;/span&gt;(&lt;span class="hljs-variable constant_"&gt;PORT&lt;/span&gt;, &lt;span class="hljs-function"&gt;() =&amp;gt;&lt;/span&gt; {
  &lt;span class="hljs-variable language_"&gt;console&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;log&lt;/span&gt;(&lt;span class="hljs-string"&gt;`Server running on port &lt;span&gt;${PORT}&lt;/span&gt;`&lt;/span&gt;);
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id="6971"&gt;3. Create &lt;code&gt;app.js&lt;/code&gt;
&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; express = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'express'&lt;/span&gt;);
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; app = &lt;span class="hljs-title function_"&gt;express&lt;/span&gt;();
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; userRoutes = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'./routes/userRoutes'&lt;/span&gt;);

app.&lt;span class="hljs-title function_"&gt;use&lt;/span&gt;(express.&lt;span class="hljs-title function_"&gt;json&lt;/span&gt;());
app.&lt;span class="hljs-title function_"&gt;use&lt;/span&gt;(&lt;span class="hljs-string"&gt;'/api/users'&lt;/span&gt;, userRoutes);
&lt;span class="hljs-comment"&gt;// Global error handler&lt;/span&gt;
app.&lt;span class="hljs-title function_"&gt;use&lt;/span&gt;(&lt;span class="hljs-function"&gt;(&lt;span&gt;err, req, res, next&lt;/span&gt;) =&amp;gt;&lt;/span&gt; {
  res.&lt;span class="hljs-title function_"&gt;status&lt;/span&gt;(err.&lt;span class="hljs-property"&gt;status&lt;/span&gt; || &lt;span class="hljs-number"&gt;500&lt;/span&gt;).&lt;span class="hljs-title function_"&gt;json&lt;/span&gt;({ &lt;span class="hljs-attr"&gt;message&lt;/span&gt;: err.&lt;span class="hljs-property"&gt;message&lt;/span&gt; });
});
&lt;span class="hljs-variable language_"&gt;module&lt;/span&gt;.&lt;span class="hljs-property"&gt;exports&lt;/span&gt; = app;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id="2ba9"&gt;4. Add a Controller (&lt;code&gt;controllers/userController.js&lt;/code&gt;)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-built_in"&gt;exports&lt;/span&gt;.&lt;span class="hljs-property"&gt;getAllUsers&lt;/span&gt; = &lt;span class="hljs-function"&gt;(&lt;span&gt;req, res&lt;/span&gt;) =&amp;gt;&lt;/span&gt; {
  res.&lt;span class="hljs-title function_"&gt;json&lt;/span&gt;([{ &lt;span class="hljs-attr"&gt;id&lt;/span&gt;: &lt;span class="hljs-number"&gt;1&lt;/span&gt;, &lt;span class="hljs-attr"&gt;name&lt;/span&gt;: &lt;span class="hljs-string"&gt;'Dipak'&lt;/span&gt; }]);
};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id="1213"&gt;5. Add a Route (&lt;code&gt;routes/userRoutes.js&lt;/code&gt;)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; express = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'express'&lt;/span&gt;);
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; router = express.&lt;span class="hljs-title class_"&gt;Router&lt;/span&gt;();
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; userController = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'../controllers/userController'&lt;/span&gt;);

router.&lt;span class="hljs-title function_"&gt;get&lt;/span&gt;(&lt;span class="hljs-string"&gt;'/'&lt;/span&gt;, userController.&lt;span class="hljs-property"&gt;getAllUsers&lt;/span&gt;);
&lt;span class="hljs-variable language_"&gt;module&lt;/span&gt;.&lt;span class="hljs-property"&gt;exports&lt;/span&gt; = router;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="3c2f"&gt;🛡️ Add Environment Config&lt;/h3&gt;
&lt;ul&gt;&lt;li&gt;Create &lt;code&gt;.env&lt;/code&gt; file:&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-attr"&gt;PORT&lt;/span&gt;=&lt;span class="hljs-number"&gt;5000&lt;/span&gt;
&lt;span class="hljs-attr"&gt;NODE_ENV&lt;/span&gt;=development&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;&lt;li&gt;Install dotenv:&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;npm install dotenv&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;&lt;li&gt;Load it in &lt;code&gt;server.js&lt;/code&gt;:&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'dotenv'&lt;/span&gt;).&lt;span class="hljs-title function_"&gt;config&lt;/span&gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="92de"&gt;🔒 Add Error Handling Middleware&lt;/h3&gt;
&lt;p&gt;In &lt;code&gt;middlewares/errorHandler.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; &lt;span class="hljs-title function_"&gt;errorHandler&lt;/span&gt; = (&lt;span class="hljs-params"&gt;err, req, res, next&lt;/span&gt;) =&amp;gt; {
  &lt;span class="hljs-variable language_"&gt;console&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;error&lt;/span&gt;(err.&lt;span class="hljs-property"&gt;stack&lt;/span&gt;);
  res.&lt;span class="hljs-title function_"&gt;status&lt;/span&gt;(&lt;span class="hljs-number"&gt;500&lt;/span&gt;).&lt;span class="hljs-title function_"&gt;json&lt;/span&gt;({ &lt;span class="hljs-attr"&gt;message&lt;/span&gt;: &lt;span class="hljs-string"&gt;'Something went wrong!'&lt;/span&gt; });
};

&lt;span class="hljs-variable language_"&gt;module&lt;/span&gt;.&lt;span class="hljs-property"&gt;exports&lt;/span&gt; = errorHandler;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And in &lt;code&gt;app.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; errorHandler = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'./middlewares/errorHandler'&lt;/span&gt;);
app.&lt;span class="hljs-title function_"&gt;use&lt;/span&gt;(errorHandler);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="f8ed"&gt;📦 Bonus: Add CORS &amp;amp; Helmet for Security&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;npm install cors helmet
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; cors = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'cors'&lt;/span&gt;);
&lt;span class="hljs-keyword"&gt;const&lt;/span&gt; helmet = &lt;span class="hljs-built_in"&gt;require&lt;/span&gt;(&lt;span class="hljs-string"&gt;'helmet'&lt;/span&gt;);

app.&lt;span class="hljs-title function_"&gt;use&lt;/span&gt;(&lt;span class="hljs-title function_"&gt;cors&lt;/span&gt;());
app.&lt;span class="hljs-title function_"&gt;use&lt;/span&gt;(&lt;span class="hljs-title function_"&gt;helmet&lt;/span&gt;());&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="d827"&gt;✅ Final Output&lt;/h3&gt;
&lt;p&gt;Once set up, run your server:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;node server.&lt;span class="hljs-property"&gt;js&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Visit:
 &lt;strong&gt;&lt;a rel="noopener noreferrer" title="" href="http://localhost:5000/api/users"&gt;http://localhost:5000/api/users&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You'll get:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-punctuation"&gt;[&lt;/span&gt;
  &lt;span class="hljs-punctuation"&gt;{&lt;/span&gt; &lt;span class="hljs-attr"&gt;"id"&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;:&lt;/span&gt; &lt;span class="hljs-number"&gt;1&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;,&lt;/span&gt; &lt;span class="hljs-attr"&gt;"name"&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;:&lt;/span&gt; &lt;span class="hljs-string"&gt;"Dipak"&lt;/span&gt; &lt;span class="hljs-punctuation"&gt;}&lt;/span&gt;
&lt;span class="hljs-punctuation"&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Clean, modular, and production-ready!&lt;/p&gt;
&lt;h3 id="ba3f"&gt;🔚 Final Thoughts&lt;/h3&gt;
&lt;p&gt;Building REST APIs in Node.js is simple — but doing it right requires planning.&lt;/p&gt;
&lt;p&gt;Start clean, modularize your logic, and build secure endpoints. You're not just learning Express — you're becoming a better backend engineer.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;a rel="noopener follow" href="https://blog.stackademic.com/can-you-optimize-node-js-streams-for-maximum-performance-c4e690bcf051"&amp;gt;


            &amp;lt;h2&amp;gt;Can You Optimize Node.js Streams for Maximum Performance?&amp;lt;/h2&amp;gt;

                &amp;lt;h3&amp;gt;Mastering Node.js streams: the secret to handling massive data efficiently without crashing your app.&amp;lt;/h3&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            &amp;lt;div class="mt-5"&amp;gt;
                &amp;lt;p class="text-xs text-grey-darker"&amp;gt;stackademic.com&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class="relative flex h-40 flew-row w-60"&amp;gt;
            &amp;lt;div class="absolute inset-0 bg-center bg-cover" style="background-image: url('https://miro.medium.com/v2/resize:fit:320/1*oOFYAyqNJmqqcwnI7-1Irw.png'); background-repeat: no-repeat;" referrerpolicy="no-referrer"&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;h3 id="4cfc"&gt;Connect with Me&lt;/h3&gt;

&lt;p&gt;If you enjoyed this post and would like to stay updated with more content like this, feel free to connect with me on social media:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Twitter &lt;/strong&gt;:&lt;a rel="noopener ugc nofollow noreferrer" title="" href="https://x.com/DipakAhirav"&gt; Follow me on Twitter&lt;/a&gt; for quick tips and updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn&lt;/strong&gt; : &lt;a rel="noopener ugc nofollow noreferrer" title="" href="https://www.linkedin.com/in/dipak-ahirav-606bba128/"&gt;Connect with me on LinkedIn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;YouTube &lt;/strong&gt;: &lt;a rel="noopener ugc nofollow noreferrer" title="" href="https://www.youtube.com/@DevDivewithDipak?sub_confirmation=1"&gt;Subscribe to my YouTube Channel&lt;/a&gt; for video tutorials and live coding sessions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dev.to&lt;/strong&gt; :&lt;a rel="noopener ugc nofollow" title="" href="https://dev.to/dipakahirav"&gt; Follow me on Dev.to&lt;/a&gt; where I share more technical articles and insights.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WhatsApp&lt;/strong&gt; : &lt;a rel="noopener ugc nofollow noreferrer" title="" href="https://whatsapp.com/channel/0029Vagey2cE50UnVcHGAw3p"&gt;Join my WhatsApp group&lt;/a&gt; to get instant notifications and chat about the latest in tech&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Email&lt;/strong&gt;: Email me on &lt;em&gt;&lt;strong&gt;&lt;a href="mailto:dipaksahirav@gmail.com"&gt;dipaksahirav@gmail.com&lt;/a&gt;&lt;/strong&gt;&lt;/em&gt;&lt;strong&gt; &lt;/strong&gt;for any questions, collaborations, or just to say hi!&lt;/p&gt;

&lt;p&gt;I appreciate your support and look forward to connecting with you!&lt;/p&gt;

&lt;h3 id="4cf4"&gt;A message from our Founder&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Hey, &lt;/strong&gt;&lt;strong&gt;&lt;a rel="noopener noreferrer" title="" href="https://linkedin.com/in/sunilsandhu"&gt;Sunil&lt;/a&gt;&lt;/strong&gt;&lt;strong&gt; here.&lt;/strong&gt; I wanted to take a moment to thank you for reading until the end and for being a part of this community.&lt;/p&gt;

&lt;p&gt;Did you know that our team run these publications as a volunteer effort to over 3.5m monthly readers? &lt;strong&gt;We don't receive any funding, we do this to support the community. ❤️&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you want to show some love, please take a moment to &lt;strong&gt;follow me on &lt;/strong&gt;&lt;strong&gt;&lt;a rel="noopener noreferrer" title="" href="https://linkedin.com/in/sunilsandhu"&gt;LinkedIn&lt;/a&gt;&lt;/strong&gt;&lt;strong&gt;, &lt;/strong&gt;&lt;strong&gt;&lt;a rel="noopener noreferrer" title="" href="https://tiktok.com/@messyfounder"&gt;TikTok&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;a rel="noopener noreferrer" title="" href="https://instagram.com/sunilsandhu"&gt;Instagram&lt;/a&gt;&lt;/strong&gt;. You can also subscribe to our &lt;strong&gt;&lt;a rel="noopener noreferrer" title="" href="https://newsletter.plainenglish.io/"&gt;weekly newsletter&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And before you go, don't forget to &lt;strong&gt;clap&lt;/strong&gt; and &lt;strong&gt;follow&lt;/strong&gt; the writer️!&lt;/p&gt;

&lt;p&gt;Author: &lt;a href="https://medium.com/@dipaksahirav" rel="noopener noreferrer"&gt;Dipak Ahirav&lt;/a&gt;&lt;/p&gt;



</description>
      <category>webdev</category>
      <category>node</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Become a Pro in Node.js ( Roadmap for Serious Devs)</title>
      <dc:creator>Kafeel Ahmad</dc:creator>
      <pubDate>Wed, 07 Jan 2026 18:04:46 +0000</pubDate>
      <link>https://dev.to/kafeel-ahmad/how-to-become-a-pro-in-nodejs-roadmap-for-serious-devs-2714</link>
      <guid>https://dev.to/kafeel-ahmad/how-to-become-a-pro-in-nodejs-roadmap-for-serious-devs-2714</guid>
      <description>&lt;p&gt;Node.js is more than just JavaScript on the server. It powers everything from &lt;strong&gt;real-time apps&lt;/strong&gt; to &lt;strong&gt;APIs&lt;/strong&gt;, &lt;strong&gt;CLI tools&lt;/strong&gt;, and &lt;strong&gt;microservices&lt;/strong&gt;. But how do you go from "I use Express" to "I &lt;em&gt;own&lt;/em&gt; the backend"?&lt;/p&gt;
&lt;p&gt;Let's break it down — this is your &lt;strong&gt;step-by-step path to mastering Node.js like a pro&lt;/strong&gt;. No fluff. All action. ⚙️&lt;/p&gt;
&lt;h3 id="6e13"&gt;📍 Step 1: Master the Core Before You Touch Frameworks&lt;/h3&gt;
&lt;p&gt;Most devs jump straight to Express, but the real power lies in &lt;strong&gt;Node core modules&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;✅ Understand these first:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;http&lt;/code&gt; — build raw servers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fs&lt;/code&gt; — handle files and streams&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;path&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt;, &lt;code&gt;querystring&lt;/code&gt; — for building real routes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;events&lt;/code&gt; — build your own event-driven logic&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;buffer&lt;/code&gt;, &lt;code&gt;process&lt;/code&gt;, &lt;code&gt;os&lt;/code&gt; — dive deeper into system-level interaction&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;🧠 &lt;strong&gt;Pro Tip:&lt;/strong&gt; Try building an HTTP server &lt;em&gt;without&lt;/em&gt; Express.&lt;/p&gt;
&lt;h3 id="8272"&gt;📦 Step 2: Know the Module System Inside-Out&lt;/h3&gt;
&lt;p&gt;Understanding the difference between:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;require()&lt;/code&gt; (CommonJS)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;import&lt;/code&gt; (ES Modules)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;module.exports&lt;/code&gt; vs &lt;code&gt;exports.foo&lt;/code&gt;
 ...is critical for writing modular, scalable code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, learn how &lt;code&gt;package.json&lt;/code&gt; works&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;"main"&lt;/code&gt; field&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"type": "module"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"scripts"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Semantic versioning rules (&lt;code&gt;^1.0.0&lt;/code&gt;, &lt;code&gt;~1.0.0&lt;/code&gt;) 📁&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="feab"&gt;🧵 Step 3: Understand the Event Loop &amp;amp; Asynchronous Nature&lt;/h3&gt;
&lt;p&gt;Node is &lt;strong&gt;non-blocking&lt;/strong&gt; by default — but if you don't understand how it &lt;em&gt;really&lt;/em&gt; works, you'll write blocking code anyway.&lt;/p&gt;
&lt;p&gt;✅ Study:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Call Stack&lt;/strong&gt;, &lt;strong&gt;Event Loop&lt;/strong&gt;, &lt;strong&gt;Callback Queue&lt;/strong&gt;, &lt;strong&gt;Microtasks&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;process.nextTick()&lt;/code&gt; vs &lt;code&gt;setImmediate()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Why &lt;code&gt;await&lt;/code&gt; in a loop can be dangerous&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Promise.all()&lt;/code&gt; vs &lt;code&gt;Promise.allSettled()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;📚 Use tools like &lt;a rel="noopener noreferrer" title="" href="https://clinicjs.org/"&gt;&lt;code&gt;node-clinic&lt;/code&gt;&lt;/a&gt; or the &lt;strong&gt;Chrome DevTools profiler&lt;/strong&gt; for real practice.&lt;/p&gt;
&lt;h3 id="839f"&gt;⚙️ Step 4: Deep Dive into Express &amp;amp; Middleware&lt;/h3&gt;
&lt;p&gt;Express is a must, but become a &lt;strong&gt;power user&lt;/strong&gt;, not a copy-paster.&lt;/p&gt;
&lt;p&gt;✅ Learn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How middleware works (including &lt;code&gt;next()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Error-handling middlewares&lt;/li&gt;
&lt;li&gt;Route grouping, param middleware&lt;/li&gt;
&lt;li&gt;CORS, body parsing, rate limiting&lt;/li&gt;
&lt;li&gt;Modular routing using &lt;code&gt;Router()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;🔥 Build your own mini Express clone to master the flow.&lt;/p&gt;
&lt;h3 id="0a5e"&gt;🧱 Step 5: Build Real APIs (REST + GraphQL)&lt;/h3&gt;
&lt;p&gt;Now apply what you know to actual API design:&lt;/p&gt;
&lt;p&gt;✅ Learn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;REST conventions (&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Pagination, filtering, sorting&lt;/li&gt;
&lt;li&gt;Status codes (201, 204, 422, etc.)&lt;/li&gt;
&lt;li&gt;Token-based auth (JWT)&lt;/li&gt;
&lt;li&gt;API versioning (&lt;code&gt;/v1/products&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Swagger/OpenAPI docs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;🧠 Bonus: Explore GraphQL using Apollo Server or Mercurius&lt;/p&gt;
&lt;h3 id="4b9c"&gt;🛡️ Step 6: Learn Security Best Practices&lt;/h3&gt;
&lt;p&gt;Security makes you &lt;strong&gt;truly senior&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;🔐 Learn to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sanitize input (&lt;code&gt;express-validator&lt;/code&gt;, &lt;code&gt;xss-clean&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Protect against SQL/NoSQL injection&lt;/li&gt;
&lt;li&gt;Use HTTPS, CORS, and CSRF protection&lt;/li&gt;
&lt;li&gt;Set secure cookies&lt;/li&gt;
&lt;li&gt;Handle JWT refresh tokens properly&lt;/li&gt;
&lt;li&gt;Prevent DOS attacks via rate limiting + helmet&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="55c1"&gt;🧰 Step 7: Work with Databases Like a Pro&lt;/h3&gt;
&lt;p&gt;Node works with any DB — but you should master:&lt;/p&gt;
&lt;p&gt;✅ NoSQL:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MongoDB with &lt;code&gt;mongoose&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Aggregation pipelines&lt;/li&gt;
&lt;li&gt;Schema design for scale&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;✅ SQL:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PostgreSQL/MySQL with &lt;code&gt;sequelize&lt;/code&gt; or &lt;code&gt;knex&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Transactions&lt;/li&gt;
&lt;li&gt;Joins and migrations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;📌 Use tools like Prisma for full-stack workflows.&lt;/p&gt;
&lt;h3 id="e24e"&gt;🪄 Step 8: Use Modern Dev Tools &amp;amp; Patterns&lt;/h3&gt;
&lt;p&gt;Professional Node apps use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dotenv&lt;/code&gt; for config&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pm2&lt;/code&gt; for process management&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;winston&lt;/code&gt; or &lt;code&gt;pino&lt;/code&gt; for logging&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;joi&lt;/code&gt; or &lt;code&gt;zod&lt;/code&gt; for schema validation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Jest&lt;/code&gt; or &lt;code&gt;Supertest&lt;/code&gt; for testing&lt;/li&gt;
&lt;li&gt;Docker for containerization 🐳&lt;/li&gt;
&lt;li&gt;GitHub Actions or Jenkins for CI/CD&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;🔁 Learn monorepo tools like &lt;code&gt;nx&lt;/code&gt;, &lt;code&gt;turbo&lt;/code&gt;, or &lt;code&gt;lerna&lt;/code&gt; for large-scale projects.&lt;/p&gt;
&lt;h3 id="41de"&gt;🧵 Step 9: Explore Advanced Node Concepts&lt;/h3&gt;
&lt;img alt="None" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F0%2AMzVadd4bU7jyMPok" width="700" height="467"&gt;&lt;p&gt;Once you're comfortable, dive deeper:&lt;/p&gt;
&lt;p&gt;🚀 Learn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;worker_threads&lt;/code&gt; and parallel processing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;child_process&lt;/code&gt; and shell scripts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cluster&lt;/code&gt; module for multi-core scaling&lt;/li&gt;
&lt;li&gt;WebSockets with &lt;code&gt;socket.io&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Building CLI tools with &lt;code&gt;commander.js&lt;/code&gt; or &lt;code&gt;yargs&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Rate limiting &amp;amp; caching with Redis&lt;/li&gt;
&lt;li&gt;Message queues like RabbitMQ or BullMQ&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="dbbd"&gt;🛠️ Step 10: Build Something Real&lt;/h3&gt;
&lt;p&gt;Apply everything by building a full project like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;📦 E-commerce backend&lt;/li&gt;
&lt;li&gt;✈️ Booking API with payment&lt;/li&gt;
&lt;li&gt;🧠 Chat app with socket.io&lt;/li&gt;
&lt;li&gt;🔐 Auth system with email + social login&lt;/li&gt;
&lt;li&gt;🚀 SaaS dashboard with user tiers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Push it to GitHub, deploy on &lt;strong&gt;Render&lt;/strong&gt;, &lt;strong&gt;Railway&lt;/strong&gt;, or &lt;strong&gt;Vercel&lt;/strong&gt;, and start your &lt;strong&gt;portfolio&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="f502"&gt;🔁 Bonus: Join the Node.js Community&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Star good repos on GitHub&lt;/li&gt;
&lt;li&gt;Read Node.js RFCs and release notes&lt;/li&gt;
&lt;li&gt;Join forums like &lt;a rel="noopener noreferrer" title="" href="https://reddit.com/r/node"&gt;Node.js Reddit&lt;/a&gt;, Discords, or Indie Hackers&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="4e62"&gt;💬 Final Word&lt;/h3&gt;
&lt;p&gt;🧠 You don't become a Node.js pro by reading tutorials.&lt;/p&gt;
&lt;p&gt;✅ You become one by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Breaking and fixing things&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Asking why things work the way they do&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rebuilding pieces from scratch&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Helping others and sharing your learnings&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;👊 &lt;em&gt;You're not just a dev who knows Node. You're a backend problem solver.&lt;/em&gt;&lt;/p&gt;
        

&lt;p&gt;Author: &lt;a href="https://medium.com/@Amanda0" rel="noopener noreferrer"&gt;Amanda&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>node</category>
      <category>java</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How To Replace Over-Complicated NgRx Stores With Angular Signals — Without Losing Control</title>
      <dc:creator>Kafeel Ahmad</dc:creator>
      <pubDate>Wed, 07 Jan 2026 18:01:03 +0000</pubDate>
      <link>https://dev.to/kafeel-ahmad/how-to-replace-over-complicated-ngrx-stores-with-angular-signals-without-losing-control-18f7</link>
      <guid>https://dev.to/kafeel-ahmad/how-to-replace-over-complicated-ngrx-stores-with-angular-signals-without-losing-control-18f7</guid>
      <description>&lt;p&gt;Hey, picture this: you're knee-deep in an Angular app, drowning in NgRx actions, reducers, effects, and selectors just to flip a simple boolean, load some todos, or filter a list. It's battle-tested for massive enterprise beasts where every change needs auditing across huge teams, but for most real-world projects? It's total overkill — endless boilerplate files that steal your dev joy and slow you down.&lt;/p&gt;
&lt;p&gt;Then Angular Signals burst onto the scene like a breath of fresh air. Native, lightweight reactivity that slashes 70% of that NgRx ceremony while making your app scream faster — no more Zone.js crutches or unpredictable re-renders. Signals track dependencies surgically, updating only what needs to change, and they're downright fun to use.&lt;/p&gt;
&lt;p&gt;What if you could ditch NgRx entirely and build clean, scalable state right in injectable services? That's our mission here: a practical guide to migrating with &lt;code&gt;update()&lt;/code&gt; for bulletproof immutable changes (spread those arrays and objects!), smart patterns for global state sharing, computed values that derive on-the-fly, and side effects that sync with localStorage or APIs without the drama. Skip the heavy NgRx SignalStore—we're going pure Signals for maximum simplicity and speed.&lt;/p&gt;
&lt;p&gt;Whether you're a dev sick of verbosity, a tech lead craving happier teams that ship faster, or a stakeholder eyeing cheaper, zippier apps — this no-nonsense path delivers. Ready to reclaim your weekends? Let's dive in and make state management feel effortless again.&lt;/p&gt;
&lt;h3 id="24fc"&gt;NgRx Pain Points and Signals Advantages&lt;/h3&gt;
&lt;p&gt;Let's get real: NgRx is like hiring a full construction crew to hang a single picture frame — it's powerhouse stuff for massive enterprise cathedrals, but for everyday apps like dashboards, e-commerce carts, or internal tools, it buries you under mountains of boilerplate that kill momentum. Signals flip that script entirely, delivering lightweight reactive magic that slashes ceremony, accelerates development, and turns maintenance into something humans actually enjoy — even for teams dodging RxJS rabbit holes.&lt;/p&gt;
&lt;h4 id="a70b"&gt;Why NgRx Feels Like Overkill in Real-World Projects&lt;/h4&gt;
&lt;p&gt;Imagine kicking off a simple feature: a user counter for your analytics dashboard. NgRx demands the full Redux ritual — create &lt;code&gt;counter.actions.ts&lt;/code&gt; with &lt;code&gt;increment()&lt;/code&gt; and &lt;code&gt;decrement()&lt;/code&gt; creators, craft &lt;code&gt;counter.reducer.ts&lt;/code&gt; to immutably switch on those actions, define &lt;code&gt;counter.state.ts&lt;/code&gt; interfaces, build &lt;code&gt;counter.selectors.ts&lt;/code&gt; for peeking at state, then wire a feature module with &lt;code&gt;StoreModule.forFeature()&lt;/code&gt;. That's 5-10 files minimum, hundreds of lines just to count clicks. New hires stare at the folder explosion, onboarding drags into weeks, and tweaking logic means hunting across a dozen spots ripe for copy-paste bugs. Reddit threads overflow with war stories: "Lost a full week mastering boilerplate for a basic todo list".​&lt;/p&gt;
&lt;p&gt;Non-enterprise reality? Startups and mid-sized teams crave velocity — this overhead murders it. Unit testing drowns in Store mocks and marble diagrams. Debugging? Good luck tracing dispatched actions through the Redux time machine. Small apps rarely need a god-store; local services suffice until regret hits at scale. No surprise savvy teams skip NgRx until truly forced, often never.​&lt;/p&gt;
&lt;h4 id="4aa2"&gt;Signals Fundamentals: Reactive Primitives Anyone Can Grasp&lt;/h4&gt;
&lt;p&gt;Signals bring Angular's reactivity into the 21st century — simple primitives that pack a punch without RxJS esoterica. Kick off with &lt;code&gt;signal(initialValue)&lt;/code&gt;: wrap any data (primitives, objects, arrays) and Angular auto-tracks reads/writes for precise notifications. Update via &lt;code&gt;set(newValue)&lt;/code&gt; for direct swaps, &lt;code&gt;update(fn =&amp;gt; transformedValue)&lt;/code&gt; for safe derivations—Angular figures out exactly what to refresh, no manual plumbing.&lt;/p&gt;
&lt;p&gt;Next, &lt;code&gt;computed(() =&amp;gt; yourLogic())&lt;/code&gt; crafts read-only derived state that smartly recalculates only when dependencies budge, caching aggressively to nix waste. Side effects? &lt;code&gt;effect(() =&amp;gt; { sideEffect(signalValue()) })&lt;/code&gt; fires precisely on changes—log analytics, sync APIs, trigger animations—subscription-free, leak-proof, running in batched microtasks for silky smoothness. Glitches? Banished. The graph ensures consistent ordering. Managers love this: codebases shrink 50%+, bugs plummet, juniors ship Day 1 sans Redux seminary.​&lt;/p&gt;
&lt;h4 id="c60c"&gt;Scalability Showdown: Signals Leave NgRx in the Dust&lt;/h4&gt;
&lt;p&gt;NgRx leans on zone.js change detection — monkey-patching every async event to scan your entire app tree. Small apps? Tolerable. Scale to 100+ components? Sluggish repaint marathons. Signals unlock fine-grained reactivity: only dirty components re-render, turbocharging zoneless Angular (v16 experiments now v18 production). Benchmarks scream victory — 2–5x faster list renders, form updates, dashboard charts, especially on mobile.​&lt;/p&gt;
&lt;p&gt;Signals prioritize immutability via &lt;code&gt;update()&lt;/code&gt;, birthing predictable fresh values—like &lt;code&gt;signalArray.update(arr =&amp;gt; [...arr, newItem])&lt;/code&gt; for clean array growth. NgRx clings to RxJS streams and zone pollution; Signals scale fluidly from component locals to app-wide services, no Big Bang store refactor needed. Growing teams pivot seamlessly as complexity creeps in.​&lt;/p&gt;
&lt;h4 id="4c66"&gt;Hands-On Demo: Counter Face-Off That Converts Skeptics&lt;/h4&gt;
&lt;p&gt;Time to build: simple increment/decrement/reset counter. NgRx gauntlet? Architect a village:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;counter.actions.ts&lt;/code&gt;: Action creators &lt;code&gt;increment()&lt;/code&gt;, &lt;code&gt;decrement()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;counter.reducer.ts&lt;/code&gt;: Immutable switch returning new state slices&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;counter.state.ts&lt;/code&gt;: &lt;code&gt;{ count: number }&lt;/code&gt; interface&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;counter.selectors.ts&lt;/code&gt;: &lt;code&gt;selectCount(state)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;counter.module.ts&lt;/code&gt;: &lt;code&gt;StoreModule.forFeature('counter', reducer)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Component boilerplate: &lt;code&gt;store.dispatch()&lt;/code&gt;, &lt;code&gt;store.select()&lt;/code&gt;, &lt;code&gt;OnDestroy&lt;/code&gt; cleanup&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;300+ lines of ceremony, typo-prone indirection. Signals? One standalone component, pure bliss:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { &lt;span class="hljs-title class_"&gt;Component&lt;/span&gt;, signal, computed } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;'@angular/core'&lt;/span&gt;;

&lt;span class="hljs-meta"&gt;@Component&lt;/span&gt;({
  &lt;span class="hljs-attr"&gt;selector&lt;/span&gt;: &lt;span class="hljs-string"&gt;'app-counter'&lt;/span&gt;,
  &lt;span class="hljs-attr"&gt;template&lt;/span&gt;: &lt;span class="hljs-string"&gt;`
    &amp;lt;h2&amp;gt;Count: {{ count() }} (Double: {{ double() }})&amp;lt;/h2&amp;gt;
    &amp;lt;button (click)="increment()"&amp;gt;+1&amp;lt;/button&amp;gt;
    &amp;lt;button (click)="decrement()"&amp;gt;-1&amp;lt;/button&amp;gt;
    &amp;lt;button (click)="reset()"&amp;gt;Reset&amp;lt;/button&amp;gt;
  `&lt;/span&gt;,
})
&lt;span class="hljs-keyword"&gt;export&lt;/span&gt; &lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;CounterComponent&lt;/span&gt; {
  count = signal&amp;lt;&lt;span class="hljs-built_in"&gt;number&lt;/span&gt;&amp;gt;(&lt;span class="hljs-number"&gt;0&lt;/span&gt;);
  double = &lt;span class="hljs-title function_"&gt;computed&lt;/span&gt;(&lt;span class="hljs-function"&gt;() =&amp;gt;&lt;/span&gt; &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;count&lt;/span&gt;() * &lt;span class="hljs-number"&gt;2&lt;/span&gt;);

  &lt;span class="hljs-title function_"&gt;increment&lt;/span&gt;(): &lt;span class="hljs-built_in"&gt;void&lt;/span&gt; {
    &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;count&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;update&lt;/span&gt;(&lt;span class="hljs-function"&gt;(&lt;span&gt;v: &lt;span&gt;number&lt;/span&gt;&lt;/span&gt;) =&amp;gt;&lt;/span&gt; v + &lt;span class="hljs-number"&gt;1&lt;/span&gt;);
  }

  &lt;span class="hljs-title function_"&gt;decrement&lt;/span&gt;(): &lt;span class="hljs-built_in"&gt;void&lt;/span&gt; {
    &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;count&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;update&lt;/span&gt;(&lt;span class="hljs-function"&gt;(&lt;span&gt;v: &lt;span&gt;number&lt;/span&gt;&lt;/span&gt;) =&amp;gt;&lt;/span&gt; v - &lt;span class="hljs-number"&gt;1&lt;/span&gt;);
  }

  &lt;span class="hljs-title function_"&gt;reset&lt;/span&gt;(): &lt;span class="hljs-built_in"&gt;void&lt;/span&gt; {
    &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;count&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;set&lt;/span&gt;(&lt;span class="hljs-number"&gt;0&lt;/span&gt;);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Spot the revolution?&lt;/strong&gt; Typed TypeScript bliss — IntelliSense dances, refactors fearless, compile-time guards halt bugs. Test? Invoke methods directly, mock nothing. Share via injectable service? &lt;code&gt;provideIn: 'root'&lt;/code&gt;. Scales to carts, forms, auth without breaking sweat. Stakeholders beam at velocity; engineers celebrate simplicity. Ditch the sledgehammer—Signals are your scalpel.​&lt;/p&gt;
&lt;h3 id="52c5"&gt;Building a Signals-Based State Service&lt;/h3&gt;
&lt;p&gt;Imagine converting a basic Angular service into a reactive "signals store" — your app's smart command center. Group related signals, expose public readonly views, control private mutations. Ditch change detection headaches for precise, automatic updates. Perfect for todos, carts, dashboards. Let's build one, then a production todo app under 100 lines.&lt;/p&gt;
&lt;h4 id="1309"&gt;Why Signals Stores Win&lt;/h4&gt;
&lt;p&gt;Skip heavy libraries. Injectable services become &lt;strong&gt;lightweight stores&lt;/strong&gt;: zero deps, pure Angular, fine-grained reactivity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Managers&lt;/strong&gt;: Less code, fewer bugs, faster apps.
&lt;strong&gt;Devs&lt;/strong&gt;: Toggle one item? Only &lt;em&gt;that row&lt;/em&gt; repaints. Scales to enterprise.&lt;/p&gt;
&lt;h4 id="47e1"&gt;Store Foundation (30 lines)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { &lt;span class="hljs-title class_"&gt;Injectable&lt;/span&gt;, signal, computed, effect } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;'@angular/core'&lt;/span&gt;;

&lt;span class="hljs-keyword"&gt;export&lt;/span&gt; &lt;span class="hljs-keyword"&gt;interface&lt;/span&gt; &lt;span class="hljs-title class_"&gt;Todo&lt;/span&gt; {
  &lt;span class="hljs-attr"&gt;id&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;number&lt;/span&gt;;
  &lt;span class="hljs-attr"&gt;text&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;string&lt;/span&gt;;
  &lt;span class="hljs-attr"&gt;completed&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;boolean&lt;/span&gt;;
}

&lt;span class="hljs-meta"&gt;@Injectable&lt;/span&gt;({ &lt;span class="hljs-attr"&gt;providedIn&lt;/span&gt;: &lt;span class="hljs-string"&gt;'root'&lt;/span&gt; })
&lt;span class="hljs-keyword"&gt;export&lt;/span&gt; &lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;TodoStore&lt;/span&gt; {
  &lt;span class="hljs-comment"&gt;// Private state&lt;/span&gt;
  &lt;span class="hljs-keyword"&gt;private&lt;/span&gt; _todos = signal&amp;lt;&lt;span class="hljs-title class_"&gt;Todo&lt;/span&gt;[]&amp;gt;([]);
  &lt;span class="hljs-keyword"&gt;private&lt;/span&gt; _filter = signal&amp;lt;&lt;span class="hljs-string"&gt;'all'&lt;/span&gt;|&lt;span class="hljs-string"&gt;'active'&lt;/span&gt;|&lt;span class="hljs-string"&gt;'completed'&lt;/span&gt;&amp;gt;(&lt;span class="hljs-string"&gt;'all'&lt;/span&gt;);
  &lt;span class="hljs-keyword"&gt;private&lt;/span&gt; _loading = &lt;span class="hljs-title function_"&gt;signal&lt;/span&gt;(&lt;span class="hljs-literal"&gt;false&lt;/span&gt;);
  &lt;span class="hljs-comment"&gt;// Public reads&lt;/span&gt;
  &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;readonly&lt;/span&gt; todos = &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;_todos&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;asReadonly&lt;/span&gt;();
  &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;readonly&lt;/span&gt; filter = &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;_filter&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;asReadonly&lt;/span&gt;();
  &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;readonly&lt;/span&gt; loading = &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;_loading&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;asReadonly&lt;/span&gt;();
  &lt;span class="hljs-comment"&gt;// Derived state&lt;/span&gt;
  &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;readonly&lt;/span&gt; filteredTodos = &lt;span class="hljs-title function_"&gt;computed&lt;/span&gt;(&lt;span class="hljs-function"&gt;() =&amp;gt;&lt;/span&gt; {
    &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; f = &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;_filter&lt;/span&gt;();
    &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; todos = &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;_todos&lt;/span&gt;();
    &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; f === &lt;span class="hljs-string"&gt;'active'&lt;/span&gt; ? todos.&lt;span class="hljs-title function_"&gt;filter&lt;/span&gt;(&lt;span class="hljs-function"&gt;&lt;span&gt;t&lt;/span&gt; =&amp;gt;&lt;/span&gt; !t.&lt;span class="hljs-property"&gt;completed&lt;/span&gt;) :
           f === &lt;span class="hljs-string"&gt;'completed'&lt;/span&gt; ? todos.&lt;span class="hljs-title function_"&gt;filter&lt;/span&gt;(&lt;span class="hljs-function"&gt;&lt;span&gt;t&lt;/span&gt; =&amp;gt;&lt;/span&gt; t.&lt;span class="hljs-property"&gt;completed&lt;/span&gt;) : todos;
  });
  &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;readonly&lt;/span&gt; totalTodos = &lt;span class="hljs-title function_"&gt;computed&lt;/span&gt;(&lt;span class="hljs-function"&gt;() =&amp;gt;&lt;/span&gt; &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;todos&lt;/span&gt;().&lt;span class="hljs-property"&gt;length&lt;/span&gt;);
  &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;readonly&lt;/span&gt; activeCount = &lt;span class="hljs-title function_"&gt;computed&lt;/span&gt;(&lt;span class="hljs-function"&gt;() =&amp;gt;&lt;/span&gt; &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;todos&lt;/span&gt;().&lt;span class="hljs-title function_"&gt;filter&lt;/span&gt;(&lt;span class="hljs-function"&gt;&lt;span&gt;t&lt;/span&gt; =&amp;gt;&lt;/span&gt; !t.&lt;span class="hljs-property"&gt;completed&lt;/span&gt;).&lt;span class="hljs-property"&gt;length&lt;/span&gt;);
  &lt;span class="hljs-keyword"&gt;public&lt;/span&gt; &lt;span class="hljs-keyword"&gt;readonly&lt;/span&gt; completedCount = &lt;span class="hljs-title function_"&gt;computed&lt;/span&gt;(&lt;span class="hljs-function"&gt;() =&amp;gt;&lt;/span&gt; &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;totalTodos&lt;/span&gt;() - &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;activeCount&lt;/span&gt;());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Magic&lt;/strong&gt;: Computeds only rerun when dependencies change.&lt;/p&gt;
&lt;h4 id="b7c5"&gt;Effects: Auto-Sync (5 lines)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-title function_"&gt;constructor&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;/span&gt;) {
  &lt;span class="hljs-title function_"&gt;effect&lt;/span&gt;(&lt;span class="hljs-function"&gt;() =&amp;gt;&lt;/span&gt; {
    &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; todos = &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;_todos&lt;/span&gt;();
    &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (todos.&lt;span class="hljs-property"&gt;length&lt;/span&gt;) &lt;span class="hljs-variable language_"&gt;localStorage&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;setItem&lt;/span&gt;(&lt;span class="hljs-string"&gt;'todos'&lt;/span&gt;, &lt;span class="hljs-title class_"&gt;JSON&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;stringify&lt;/span&gt;(todos));
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Swaps easily for API calls.&lt;/p&gt;
&lt;h4 id="bbc9"&gt;CRUD: Immutable Only (40 lines)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-title function_"&gt;addTodo&lt;/span&gt;(&lt;span class="hljs-attr"&gt;text&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;string&lt;/span&gt;): &lt;span class="hljs-built_in"&gt;void&lt;/span&gt; {
  &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; trimmed = text?.&lt;span class="hljs-title function_"&gt;trim&lt;/span&gt;();
  &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (!trimmed) &lt;span class="hljs-keyword"&gt;return&lt;/span&gt;;
  &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;_todos&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;update&lt;/span&gt;(&lt;span class="hljs-function"&gt;&lt;span&gt;todos&lt;/span&gt; =&amp;gt;&lt;/span&gt; [...todos, {&lt;span class="hljs-attr"&gt;id&lt;/span&gt;: &lt;span class="hljs-title class_"&gt;Date&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;now&lt;/span&gt;(), &lt;span class="hljs-attr"&gt;text&lt;/span&gt;: trimmed, &lt;span class="hljs-attr"&gt;completed&lt;/span&gt;: &lt;span class="hljs-literal"&gt;false&lt;/span&gt;}]);
}

&lt;span class="hljs-title function_"&gt;toggleTodo&lt;/span&gt;(&lt;span class="hljs-attr"&gt;id&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;number&lt;/span&gt;): &lt;span class="hljs-built_in"&gt;void&lt;/span&gt; {
  &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;_todos&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;update&lt;/span&gt;(&lt;span class="hljs-function"&gt;&lt;span&gt;todos&lt;/span&gt; =&amp;gt;&lt;/span&gt; todos.&lt;span class="hljs-title function_"&gt;map&lt;/span&gt;(&lt;span class="hljs-function"&gt;&lt;span&gt;t&lt;/span&gt; =&amp;gt;&lt;/span&gt; 
    t.&lt;span class="hljs-property"&gt;id&lt;/span&gt; === id ? {...t, &lt;span class="hljs-attr"&gt;completed&lt;/span&gt;: !t.&lt;span class="hljs-property"&gt;completed&lt;/span&gt;} : t
  ));
}

&lt;span class="hljs-title function_"&gt;editTodo&lt;/span&gt;(&lt;span class="hljs-attr"&gt;id&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;number&lt;/span&gt;, &lt;span class="hljs-attr"&gt;text&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;string&lt;/span&gt;): &lt;span class="hljs-built_in"&gt;void&lt;/span&gt; {
  &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; trimmed = text?.&lt;span class="hljs-title function_"&gt;trim&lt;/span&gt;();
  &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (!trimmed) &lt;span class="hljs-keyword"&gt;return&lt;/span&gt;;
  &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;_todos&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;update&lt;/span&gt;(&lt;span class="hljs-function"&gt;&lt;span&gt;todos&lt;/span&gt; =&amp;gt;&lt;/span&gt; todos.&lt;span class="hljs-title function_"&gt;map&lt;/span&gt;(&lt;span class="hljs-function"&gt;&lt;span&gt;t&lt;/span&gt; =&amp;gt;&lt;/span&gt; 
    t.&lt;span class="hljs-property"&gt;id&lt;/span&gt; === id ? {...t, &lt;span class="hljs-attr"&gt;text&lt;/span&gt;: trimmed} : t
  ));
}

&lt;span class="hljs-title function_"&gt;removeTodo&lt;/span&gt;(&lt;span class="hljs-attr"&gt;id&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;number&lt;/span&gt;): &lt;span class="hljs-built_in"&gt;void&lt;/span&gt; {
  &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;_todos&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;update&lt;/span&gt;(&lt;span class="hljs-function"&gt;&lt;span&gt;todos&lt;/span&gt; =&amp;gt;&lt;/span&gt; todos.&lt;span class="hljs-title function_"&gt;filter&lt;/span&gt;(&lt;span class="hljs-function"&gt;&lt;span&gt;t&lt;/span&gt; =&amp;gt;&lt;/span&gt; t.&lt;span class="hljs-property"&gt;id&lt;/span&gt; !== id));
}

&lt;span class="hljs-title function_"&gt;setFilter&lt;/span&gt;(&lt;span class="hljs-attr"&gt;filter&lt;/span&gt;: &lt;span class="hljs-string"&gt;'all'&lt;/span&gt;|&lt;span class="hljs-string"&gt;'active'&lt;/span&gt;|&lt;span class="hljs-string"&gt;'completed'&lt;/span&gt;): &lt;span class="hljs-built_in"&gt;void&lt;/span&gt; {
  &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;_filter&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;set&lt;/span&gt;(filter);
}

&lt;span class="hljs-title function_"&gt;clearCompleted&lt;/span&gt;(): &lt;span class="hljs-built_in"&gt;void&lt;/span&gt; {
  &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;_todos&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;set&lt;/span&gt;(&lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;todos&lt;/span&gt;().&lt;span class="hljs-title function_"&gt;filter&lt;/span&gt;(&lt;span class="hljs-function"&gt;&lt;span&gt;t&lt;/span&gt; =&amp;gt;&lt;/span&gt; !t.&lt;span class="hljs-property"&gt;completed&lt;/span&gt;));
}

&lt;span class="hljs-keyword"&gt;async&lt;/span&gt; &lt;span class="hljs-title function_"&gt;loadTodos&lt;/span&gt;(): &lt;span class="hljs-title class_"&gt;Promise&lt;/span&gt;&amp;lt;&lt;span class="hljs-built_in"&gt;void&lt;/span&gt;&amp;gt; {
  &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;_loading&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;set&lt;/span&gt;(&lt;span class="hljs-literal"&gt;true&lt;/span&gt;);
  &lt;span class="hljs-keyword"&gt;try&lt;/span&gt; {
    &lt;span class="hljs-keyword"&gt;await&lt;/span&gt; &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; &lt;span class="hljs-title class_"&gt;Promise&lt;/span&gt;(&lt;span class="hljs-function"&gt;&lt;span&gt;r&lt;/span&gt; =&amp;gt;&lt;/span&gt; &lt;span class="hljs-built_in"&gt;setTimeout&lt;/span&gt;(r, &lt;span class="hljs-number"&gt;800&lt;/span&gt;));
    &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; saved = &lt;span class="hljs-variable language_"&gt;localStorage&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;getItem&lt;/span&gt;(&lt;span class="hljs-string"&gt;'todos'&lt;/span&gt;);
    &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (saved) &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;_todos&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;set&lt;/span&gt;(&lt;span class="hljs-title class_"&gt;JSON&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;parse&lt;/span&gt;(saved));
  } &lt;span class="hljs-keyword"&gt;finally&lt;/span&gt; {
    &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;_loading&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;set&lt;/span&gt;(&lt;span class="hljs-literal"&gt;false&lt;/span&gt;);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Key&lt;/strong&gt;: Always return new arrays/objects. Reactivity guaranteed.&lt;/p&gt;
&lt;h4 id="e275"&gt;Complete Todo App&lt;/h4&gt;
&lt;p&gt;Reactive forms + modern templates. Logic: 25 lines.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { &lt;span class="hljs-title class_"&gt;Component&lt;/span&gt;, signal, inject } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;'@angular/core'&lt;/span&gt;;
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { &lt;span class="hljs-title class_"&gt;FormControl&lt;/span&gt;, &lt;span class="hljs-title class_"&gt;ReactiveFormsModule&lt;/span&gt;, &lt;span class="hljs-title class_"&gt;Validators&lt;/span&gt; } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;'@angular/forms'&lt;/span&gt;;
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { &lt;span class="hljs-title class_"&gt;CommonModule&lt;/span&gt; } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;'@angular/common'&lt;/span&gt;;
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { &lt;span class="hljs-title class_"&gt;TodoStore&lt;/span&gt;, &lt;span class="hljs-title class_"&gt;Todo&lt;/span&gt; } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;'./todo.store'&lt;/span&gt;;

&lt;span class="hljs-meta"&gt;@Component&lt;/span&gt;({
  &lt;span class="hljs-attr"&gt;selector&lt;/span&gt;: &lt;span class="hljs-string"&gt;'app-todo'&lt;/span&gt;,
  &lt;span class="hljs-attr"&gt;standalone&lt;/span&gt;: &lt;span class="hljs-literal"&gt;true&lt;/span&gt;,
  &lt;span class="hljs-attr"&gt;imports&lt;/span&gt;: [&lt;span class="hljs-title class_"&gt;CommonModule&lt;/span&gt;, &lt;span class="hljs-title class_"&gt;ReactiveFormsModule&lt;/span&gt;],
  &lt;span class="hljs-attr"&gt;template&lt;/span&gt;: &lt;span class="hljs-string"&gt;`
    &amp;lt;main class="app"&amp;gt;
      &amp;lt;header&amp;gt;
        &amp;lt;h1&amp;gt;Signal Todos&amp;lt;/h1&amp;gt;
        &amp;lt;div&amp;gt;Total: {{store.totalTodos()}} | Active: {{store.activeCount()}}&amp;lt;/div&amp;gt;
        
        @let loading = store.loading();
        @if (loading) { &amp;lt;div&amp;gt;⏳ Loading...&amp;lt;/div&amp;gt; }
      &amp;lt;/header&amp;gt;
      &amp;lt;form (ngSubmit)="addTodo()" class="add-form"&amp;gt;
        &amp;lt;input [formControl]="newTodoCtrl" placeholder="New todo..." /&amp;gt;
        &amp;lt;button type="submit" [disabled]="newTodoCtrl.invalid"&amp;gt;Add&amp;lt;/button&amp;gt;
      &amp;lt;/form&amp;gt;
      &amp;lt;nav class="filters"&amp;gt;
        @for (let f of filterOptions; track f) {
          &amp;lt;button [class.active]="store.filter() === f" (click)="store.setFilter(f)"&amp;gt;
            {{f[0].toUpperCase() + f.slice(1)}}
          &amp;lt;/button&amp;gt;
        }
      &amp;lt;/nav&amp;gt;

      @let todos = store.filteredTodos();
      @if (todos.length) {
        &amp;lt;ul&amp;gt;
          @for (let todo of todos; track todo.id) {
            &amp;lt;li [class.done]="todo.completed"&amp;gt;
              &amp;lt;input type="checkbox" [checked]="todo.completed" (change)="store.toggleTodo(todo.id)" /&amp;gt;
              @if (!editingId() || editingId() !== todo.id) {
                &amp;lt;span (dblclick)="editTodo(todo)"&amp;gt;{{todo.text}}&amp;lt;/span&amp;gt;
              } @else {
                &amp;lt;input [formControl]="editCtrl" (keyup.enter)="saveEdit()" (blur)="saveEdit()" autofocus /&amp;gt;
              }
              &amp;lt;button (click)="store.removeTodo(todo.id)"&amp;gt;Delete&amp;lt;/button&amp;gt;
            &amp;lt;/li&amp;gt;
          }
        &amp;lt;/ul&amp;gt;

        @let done = store.completedCount();
        @if (done) {
          &amp;lt;button (click)="store.clearCompleted()"&amp;gt;Clear {{done}} done&amp;lt;/button&amp;gt;
        }
      } @else {
        &amp;lt;p&amp;gt;No {{store.filter()}} todos&amp;lt;/p&amp;gt;
      }
    &amp;lt;/main&amp;gt;
  `&lt;/span&gt;,
  &lt;span class="hljs-attr"&gt;styles&lt;/span&gt;: [&lt;span class="hljs-string"&gt;`
    .app { max-width: 600px; margin: 2rem auto; padding: 1rem; }
    .add-form { display: flex; gap: 1rem; margin: 2rem 0; }
    .filters { display: flex; gap: 0.5rem; justify-content: center; margin: 2rem 0; }
    .filters button.active { background: #3b82f6; color: white; }
    li { display: flex; gap: 1rem; align-items: center; padding: 1rem; border: 1px solid #eee; }
    li.done { opacity: 0.6; text-decoration: line-through; }
  `&lt;/span&gt;]
})
&lt;span class="hljs-keyword"&gt;export&lt;/span&gt; &lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;TodoComponent&lt;/span&gt; {
  &lt;span class="hljs-keyword"&gt;protected&lt;/span&gt; store = &lt;span class="hljs-title function_"&gt;inject&lt;/span&gt;(&lt;span class="hljs-title class_"&gt;TodoStore&lt;/span&gt;);
  &lt;span class="hljs-keyword"&gt;protected&lt;/span&gt; newTodoCtrl = &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; &lt;span class="hljs-title class_"&gt;FormControl&lt;/span&gt;(&lt;span class="hljs-string"&gt;''&lt;/span&gt;, &lt;span class="hljs-title class_"&gt;Validators&lt;/span&gt;.&lt;span class="hljs-property"&gt;required&lt;/span&gt;);
  &lt;span class="hljs-keyword"&gt;protected&lt;/span&gt; editCtrl = &lt;span class="hljs-keyword"&gt;new&lt;/span&gt; &lt;span class="hljs-title class_"&gt;FormControl&lt;/span&gt;(&lt;span class="hljs-string"&gt;''&lt;/span&gt;);
  &lt;span class="hljs-keyword"&gt;protected&lt;/span&gt; editingId = signal&amp;lt;&lt;span class="hljs-built_in"&gt;number&lt;/span&gt; | &lt;span class="hljs-literal"&gt;null&lt;/span&gt;&amp;gt;(&lt;span class="hljs-literal"&gt;null&lt;/span&gt;);
  &lt;span class="hljs-keyword"&gt;protected&lt;/span&gt; filterOptions = [&lt;span class="hljs-string"&gt;'all'&lt;/span&gt;, &lt;span class="hljs-string"&gt;'active'&lt;/span&gt;, &lt;span class="hljs-string"&gt;'completed'&lt;/span&gt;] &lt;span class="hljs-keyword"&gt;as&lt;/span&gt; &lt;span class="hljs-keyword"&gt;const&lt;/span&gt;;

  &lt;span class="hljs-title function_"&gt;constructor&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;/span&gt;) {
    &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;store&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;loadTodos&lt;/span&gt;();
  }

  &lt;span class="hljs-title function_"&gt;addTodo&lt;/span&gt;(): &lt;span class="hljs-built_in"&gt;void&lt;/span&gt; {
    &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; text = &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;newTodoCtrl&lt;/span&gt;.&lt;span class="hljs-property"&gt;value&lt;/span&gt;?.&lt;span class="hljs-title function_"&gt;trim&lt;/span&gt;();
    &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (text) {
      &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;store&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;addTodo&lt;/span&gt;(text);
      &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;newTodoCtrl&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;reset&lt;/span&gt;();
    }
  }

  &lt;span class="hljs-title function_"&gt;editTodo&lt;/span&gt;(&lt;span class="hljs-attr"&gt;todo&lt;/span&gt;: &lt;span class="hljs-title class_"&gt;Todo&lt;/span&gt;): &lt;span class="hljs-built_in"&gt;void&lt;/span&gt; {
    &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;editingId&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;set&lt;/span&gt;(todo.&lt;span class="hljs-property"&gt;id&lt;/span&gt;);
    &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;editCtrl&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;setValue&lt;/span&gt;(todo.&lt;span class="hljs-property"&gt;text&lt;/span&gt;);
  }

  &lt;span class="hljs-title function_"&gt;saveEdit&lt;/span&gt;(): &lt;span class="hljs-built_in"&gt;void&lt;/span&gt; {
    &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; id = &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;editingId&lt;/span&gt;();
    &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; text = &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;editCtrl&lt;/span&gt;.&lt;span class="hljs-property"&gt;value&lt;/span&gt;?.&lt;span class="hljs-title function_"&gt;trim&lt;/span&gt;();
    &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; (id &amp;amp;&amp;amp; text) &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;store&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;editTodo&lt;/span&gt;(id, text);
    &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;editingId&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;set&lt;/span&gt;(&lt;span class="hljs-literal"&gt;null&lt;/span&gt;);
    &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;editCtrl&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;reset&lt;/span&gt;();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id="9160"&gt;The Power Unleashed&lt;/h4&gt;
&lt;p&gt;85-line service + 25-line component = production todo app with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ Full CRUD operations&lt;/li&gt;
&lt;li&gt;✅ Smart filtering&lt;/li&gt;
&lt;li&gt;✅ LocalStorage sync&lt;/li&gt;
&lt;li&gt;✅ Loading states&lt;/li&gt;
&lt;li&gt;✅ Inline editing&lt;/li&gt;
&lt;li&gt;✅ Bulk actions&lt;/li&gt;
&lt;li&gt;✅ Form validation&lt;/li&gt;
&lt;li&gt;✅ Modern Angular 18+ (&lt;code&gt;@for/@if&lt;/code&gt;, signals)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Scale it&lt;/strong&gt;: Shopping carts? User profiles? Analytics? Same pattern. Immutable updates + computed derivations = unbeatable performance.&lt;/p&gt;
&lt;p&gt;Signals stores = simple, reactive, scalable. Your new Angular default.&lt;/p&gt;
&lt;h3 id="656d"&gt;Migration Strategy from NgRx to Signals&lt;/h3&gt;
&lt;p&gt;Hey folks, let's dive into migrating from NgRx to Angular Signals. Everyone's buzzing about Signals because they make state management way simpler and faster — no more drowning in actions, reducers, and endless selector chains. The beauty is you don't have to burn your NgRx setup to the ground. Start small with UI state like toggles and forms, keep NgRx for the heavy global stuff like user auth, and gradually shift over. It's less scary than it sounds, and your app gets noticeably snappier along the way.​&lt;/p&gt;
&lt;h4 id="3622"&gt;Step-by-Step Refactor Process&lt;/h4&gt;
&lt;p&gt;First up, grab a coffee and map out your state. Ask yourself: is this dialog open state local to one component, or does it need to sync across the whole app? Local stuff screams for Signals. Global (think shared product catalogs) — leave it with NgRx for now.&lt;/p&gt;
&lt;p&gt;Next, tackle those selectors. Instead of &lt;code&gt;store.select(selectCart) | async&lt;/code&gt; everywhere, use NgRx's own &lt;code&gt;selectSignal()&lt;/code&gt; wrapped in a &lt;code&gt;computed()&lt;/code&gt;. Ditch the async pipes – your templates breathe again. Then swap actions for service methods. Imagine &lt;code&gt;dispatch(addToCart(item))&lt;/code&gt; becomes &lt;code&gt;cartService.add(item)&lt;/code&gt; with a clean &lt;code&gt;this.cart.update(items =&amp;gt; [...items, item])&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here's how I'd tackle it over a few sprints:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Audit your state&lt;/strong&gt;: Local = Signals. Global = NgRx. Use Nx dependency graphs or just grep your codebase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selectors first&lt;/strong&gt;: &lt;code&gt;readonly cartItems = this.store.selectSignal(selectCartItems);&lt;/code&gt; – done, use &lt;code&gt;cartItems()&lt;/code&gt; everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actions to methods&lt;/strong&gt;: Create injectable services as the single source of truth. Test one feature at a time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify with Profiler&lt;/strong&gt;: Angular DevTools shows render wins immediately. Feature flag everything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rinse and repeat&lt;/strong&gt;: One lazy-loaded module per week. Ship, celebrate, move on.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I love how each step peels back NgRx ceremony you didn't really need. Suddenly your components are half the size.&lt;/p&gt;
&lt;h4 id="e53c"&gt;The Smart Hybrid Approach&lt;/h4&gt;
&lt;p&gt;Look, NgRx is battle-tested for crazy complex flows — optimistic updates, cross-tab sync, effects with retries. Don't fight it. Let NgRx own global state, Signals handle everything else. Your renders speed up 2–3x because Signals trigger exactly what's needed, no Zone.js wakeups.&lt;/p&gt;
&lt;p&gt;Here's the real-world breakdown:&lt;/p&gt;
&lt;img alt="None" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F1%2AxoLtIrE98Sk4BT84ZL2AEw.png" width="700" height="237"&gt;NgRx vs Signals&lt;p&gt;Teams mixing both report the best results — NgRx for orchestration, Signals for reactivity. Shopping apps keep order history global, cart UI local. Perfect balance.&lt;/p&gt;
&lt;h4 id="57e6"&gt;Keeping Control with Unidirectional Flow&lt;/h4&gt;
&lt;p&gt;Chaos happens when every component pokes at shared state. Solution? Route everything through services. &lt;code&gt;CartService&lt;/code&gt; becomes your gatekeeper: &lt;code&gt;add(item)&lt;/code&gt;, &lt;code&gt;remove(id)&lt;/code&gt;, &lt;code&gt;clear()&lt;/code&gt;. Want to debounce rapid adds from some legacy Effect? &lt;code&gt;toObservable(this.cart).pipe(debounceTime(300), takeUntilDestroyed()).subscribe(...)&lt;/code&gt;. Boom – RxJS and Signals play nice.&lt;/p&gt;
&lt;p&gt;Steal this service pattern:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-meta"&gt;@Injectable&lt;/span&gt;({&lt;span class="hljs-attr"&gt;providedIn&lt;/span&gt;: &lt;span class="hljs-string"&gt;'root'&lt;/span&gt;})
&lt;span class="hljs-keyword"&gt;export&lt;/span&gt; &lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;CartService&lt;/span&gt; {
  &lt;span class="hljs-keyword"&gt;readonly&lt;/span&gt; cart = signal&amp;lt;&lt;span class="hljs-title class_"&gt;Item&lt;/span&gt;[]&amp;gt;([]);
  
  &lt;span class="hljs-title function_"&gt;add&lt;/span&gt;(&lt;span class="hljs-attr"&gt;item&lt;/span&gt;: &lt;span class="hljs-title class_"&gt;Item&lt;/span&gt;): &lt;span class="hljs-built_in"&gt;void&lt;/span&gt; {
    &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;cart&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;update&lt;/span&gt;(&lt;span class="hljs-function"&gt;&lt;span&gt;items&lt;/span&gt; =&amp;gt;&lt;/span&gt; [...items, item]);
  }
  
  &lt;span class="hljs-comment"&gt;// Bridge old effects&lt;/span&gt;
  &lt;span class="hljs-title function_"&gt;constructor&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;/span&gt;) {
    &lt;span class="hljs-title function_"&gt;toObservable&lt;/span&gt;(&lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;cart&lt;/span&gt;)
      .&lt;span class="hljs-title function_"&gt;pipe&lt;/span&gt;(&lt;span class="hljs-title function_"&gt;debounceTime&lt;/span&gt;(&lt;span class="hljs-number"&gt;250&lt;/span&gt;), &lt;span class="hljs-title function_"&gt;takeUntilDestroyed&lt;/span&gt;())
      .&lt;span class="hljs-title function_"&gt;subscribe&lt;/span&gt;(&lt;span class="hljs-function"&gt;&lt;span&gt;items&lt;/span&gt; =&amp;gt;&lt;/span&gt; &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;saveToLocalStorage&lt;/span&gt;(items));
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add a lint rule: "no direct signal updates outside services." Team stays sane, data flows one way.&lt;/p&gt;
&lt;h4 id="b3e6"&gt;Real-World Test: Shopping Cart Migration&lt;/h4&gt;
&lt;p&gt;Nothing proves this like a shopping cart refactor — everyone has one, and the wins are dramatic. Rip out the NgRx feature module. Build &lt;code&gt;CartService&lt;/code&gt; with &lt;code&gt;cartItems = signal([])&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Template goes from this mess:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;Total: {{ cart$ | async | currency }}&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;
@for (item of cart$ | async; track item.id) { ... }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To pure bliss:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;Total: {{ cartItems().reduce((sum, i) =&amp;gt; sum + i.price, 0) | currency }}&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;
@for (item of cartItems(); track item.id) { ... }
&lt;span class="hljs-tag"&gt;&amp;lt;&lt;span&gt;button&lt;/span&gt; (&lt;span&gt;click&lt;/span&gt;)=&lt;span&gt;"cartService.add(item)"&lt;/span&gt;&amp;gt;&lt;/span&gt;Add&lt;span class="hljs-tag"&gt;&amp;lt;/&lt;span&gt;button&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fire up Angular Profiler:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Renders: 2x faster. Signals skip unchanged DOM.&lt;/li&gt;
&lt;li&gt;Bundle: -15KB. No more NgRx feature bloat.&lt;/li&gt;
&lt;li&gt;Memory: Fewer subscriptions = happier GC.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Feature flag it: &lt;code&gt;@if (useSignalsCart()) { &amp;lt;signal-cart /&amp;gt; }&lt;/code&gt;. A/B test Time to Interactive with real traffic. Users notice the cart feels instant. Green light? Hit wishlist next, then user settings. By checkout, half your app runs on Signals, zero outages.&lt;/p&gt;
&lt;p&gt;This isn't some pipe dream — teams shipping this weekly. Start with that cart tomorrow. You'll wonder why you waited.&lt;/p&gt;
&lt;h3 id="eafb"&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;So there you have it — Angular Signals are your new best friend when it comes to ditching that overwhelming pile of NgRx boilerplate so many apps wrestle with.&lt;/p&gt;
&lt;p&gt;Picture this: a single, elegant &lt;code&gt;update()&lt;/code&gt; call lets you handle immutable state changes—like incrementing a counter or patching user data—without breaking a sweat. Everything flows predictably through lightweight, injectable services, no more chasing side effects or debugging reducer chains.&lt;/p&gt;
&lt;p&gt;Best part? You're perfectly positioned for Angular's full-on shift to reactivity, where signals power fine-grained updates and blazing performance. Honestly, for about 80% of projects, this switch means way less code, fewer late-night bugs, and templates that just… work, skipping the whole action-reducer-effect tango.&lt;/p&gt;
&lt;h4 id="43b2"&gt;Ready to jump in hands-on?&lt;/h4&gt;
&lt;p&gt;Grab the &lt;em&gt;&lt;a rel="noopener noreferrer" title="" href="https://www.angularspace.com/migrating-to-angular-signals/"&gt;AngularSpace article&lt;/a&gt;&lt;/em&gt; packed with migration examples, snag your app's simplest store (basic counter or todo list), and refactor it with &lt;code&gt;update()&lt;/code&gt; magic. Test the waters, clock those performance gains (often 50% less code, zero downsides), and drop your before/after stories right here in the comments.&lt;/p&gt;
&lt;h4 id="d4df"&gt;Deep dive time:&lt;/h4&gt;
&lt;p&gt;Swing by &lt;em&gt;&lt;a rel="noopener noreferrer" title="" href="https://angular.dev/guide/signals"&gt;Angular's official Signals docs&lt;/a&gt;&lt;/em&gt; to master &lt;code&gt;computed()&lt;/code&gt; and &lt;code&gt;effect()&lt;/code&gt;. Check &lt;em&gt;&lt;a rel="noopener noreferrer" title="" href="https://ngrx.io/guide/migration/v21"&gt;NgRx's v21 migration guide&lt;/a&gt;&lt;/em&gt; for smooth signalStore transitions. Explore &lt;em&gt;&lt;a rel="noopener noreferrer" title="" href="https://nx.dev/blog/angular-state-management-2025"&gt;Nx workspace recipes for enterprise-scale patterns&lt;/a&gt;&lt;/em&gt;. Trust me, once you try it, you'll wonder why you waited so long!&lt;/p&gt;
&lt;h3 id="dac4"&gt;Thanks for Reading 🙌&lt;/h3&gt;
&lt;p&gt;I hope these tips help you ship better, faster, and more maintainable frontend projects.&lt;/p&gt;

&lt;p&gt;Author: &lt;a href="https://medium.com/@karol-modelski" rel="noopener noreferrer"&gt;Karol Modelski&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>angular</category>
    </item>
    <item>
      <title>Unlocking Productivity: AI Tools Every Angular Developer Should Know</title>
      <dc:creator>Kafeel Ahmad</dc:creator>
      <pubDate>Wed, 07 Jan 2026 17:56:30 +0000</pubDate>
      <link>https://dev.to/kafeel-ahmad/unlocking-productivity-ai-tools-every-angular-developer-should-know-41l</link>
      <guid>https://dev.to/kafeel-ahmad/unlocking-productivity-ai-tools-every-angular-developer-should-know-41l</guid>
      <description>&lt;p&gt;Imagine having an AI coding assistant right by your side — helping you avoid bugs, write cleaner code, and speed up your workflow.&lt;/p&gt;
&lt;p&gt;This article explores how AI is changing the game for Angular developers. From smart code suggestions to tools that catch errors before they become problems, AI is making Angular development smoother and more productive.&lt;/p&gt;
&lt;p&gt;Get ready to discover how these new tools and techniques can save time and make coding less stressful.&lt;/p&gt;
&lt;h3 id="2333"&gt;🤖 AI Coding Assistants Transforming Angular Development&lt;/h3&gt;
&lt;p&gt;Imagine having a coding partner who instantly suggests code, helps you fix bugs, and even writes documentation — all without taking a coffee break. AI assistants like GitHub Copilot, Claude, and ChatGPT are making this a reality for Angular developers, speeding up workflows and boosting productivity like never before.&lt;/p&gt;
&lt;h4 id="156c"&gt;Meet Your New Angular Sidekicks&lt;/h4&gt;
&lt;p&gt;AI coding assistants are no longer sci-fi — they are powerful tools integrated right inside your favorite IDEs. GitHub Copilot offers seamless autocomplete and chat features directly in VS Code, while Claude Code shines in handling multi-file projects and deep reasoning. ChatGPT jumps in with quick, detailed answers perfect for tricky Angular questions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GitHub Copilot: Your go-to for real-time Angular and TypeScript snippets and test generation.&lt;/li&gt;
&lt;li&gt;Claude Code: Masters big-picture tasks with IDE integration and project context awareness.&lt;/li&gt;
&lt;li&gt;ChatGPT: Great for focused explanations and code fixes on demand.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Takeaway&lt;/strong&gt;: Using these AI sidekicks together lets you code faster with more confidence and clarity.&lt;/p&gt;
&lt;h4 id="d612"&gt;How AI Makes Your Angular Day Easier&lt;/h4&gt;
&lt;p&gt;From spinning up components to writing tests and debugging, AI assistants handle the repetitive grunt work so you can focus on building great apps. They suggest code based on your current file and project context, debug with detailed explanations, and even generate helpful docs and tests.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Instant code suggestions save you from boilerplate headaches.&lt;/li&gt;
&lt;li&gt;Debugging help breaks down issues step-by-step for quick fixes.&lt;/li&gt;
&lt;li&gt;Automated tests and documentation boost code quality without extra effort.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Takeaway&lt;/strong&gt;: Embedded AI tools keep you in the zone by cutting down context switching and repetitive tasks.&lt;/p&gt;
&lt;h4 id="e415"&gt;Real Examples, Real Gains&lt;/h4&gt;
&lt;p&gt;Let's see AI in action with Angular:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Need a new component? Ask AI to create a standalone counter with inputs, outputs, and slick OnPush change detection — minutes saved!&lt;/li&gt;
&lt;li&gt;Updating to Angular 16's modern HttpClient? AI refactors your code and updates imports flawlessly.&lt;/li&gt;
&lt;li&gt;Writing unit tests for form validations or mapping functions? AI drafts them complete with coverage for edge cases.&lt;/li&gt;
&lt;li&gt;Crafting a typed UserService with RxJS operators and HttpParams? AI delivers production-ready code that fits right into your app.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Takeaway&lt;/strong&gt;: Developers report up to 30% faster coding and testing, with cleaner code and fewer bugs thanks to AI helpers.&lt;/p&gt;
&lt;p&gt;AI coding assistants have evolved from neat tools to essential teammates in Angular development. They're accelerating everything from scaffolding to debugging, all inside your IDE. With these tools in hand and Angular's AI best practices as a guide, building high-quality apps becomes faster and more enjoyable.&lt;/p&gt;
&lt;h3 id="d32d"&gt;🔧 Angular Libraries and Developer Tools Leveraging AI&lt;/h3&gt;
&lt;p&gt;AI is shaking up the world of Angular development in exciting ways! From speeding up UI creation to catching bugs before they cause trouble, AI-powered tools are changing how developers work. This chapter dives into the coolest AI-driven libraries and tools making Angular development faster, smarter, and more fun.&lt;/p&gt;
&lt;h4 id="8918"&gt;Smart Angular Libraries That Think Ahead&lt;/h4&gt;
&lt;p&gt;Imagine a library that writes some of your UI code for you or suggests better ways to handle user input — sounds like magic, right? AI-enhanced Angular libraries do just that. They learn from patterns and automate tasks like creating components or validating forms with AI-powered insights. This means you spend less time on repetitive coding and more on building great features.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key Takeaway 1&lt;/strong&gt;: AI-powered libraries help you build UIs faster by automating common tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Takeaway 2&lt;/strong&gt;: They make apps smarter by adding features that adapt based on user behavior.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="6f56"&gt;Testing and Debugging — Now with AI Superpowers!&lt;/h4&gt;
&lt;p&gt;Testing can be a headache, but AI is here to make it easier. AI-driven tools automatically create test cases by understanding your app's code and how users interact with it. They also spot bugs and performance issues early, often suggesting how to fix them. These smart helpers save hours, if not days, of manual testing and hunting for bugs.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key Takeaway 1&lt;/strong&gt;: AI tools cut down testing time by generating tests automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Takeaway 2&lt;/strong&gt;: Debugging gets faster with AI that pinpoints issues and suggests fixes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="ecae"&gt;Boosting Your Workflow with AI Integrations&lt;/h4&gt;
&lt;p&gt;What if your development tools could watch your app's performance and whisper ideas on how to make it better? AI integrations do just that by analyzing builds, runtime speed, and code quality right inside your workflow. They provide tips like lazy loading or smarter bundling to keep apps lightning-fast. Plus, AI-powered CI/CD pipelines catch build problems before they hit production, so you deploy with confidence.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key Takeaway 1&lt;/strong&gt;: AI keeps your Angular app running smoothly by spotting performance tips in real-time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Takeaway 2&lt;/strong&gt;: Continuous integration powered by AI means fewer surprises and faster releases.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI is the new teammate every Angular developer wants. It takes care of the boring stuff and gives you supercharged insights so you can focus on what really matters — building amazing apps. Embracing AI tools means happier coding, fewer bugs, and better-performing Angular projects.&lt;/p&gt;
&lt;h3 id="acb4"&gt;📋 Best Practices for Integrating AI Tools into Angular Workflows&lt;/h3&gt;
&lt;p&gt;AI is revolutionizing how we build apps, and Angular developers are right in the middle of this exciting change. But how can teams make the most of AI without losing control of code quality? This chapter is a quick guide to blending AI's power with Angular's best practices for smooth, safe, and smart workflows.&lt;/p&gt;
&lt;h4 id="3551"&gt;Managing AI-Generated Code Quality&lt;/h4&gt;
&lt;p&gt;AI can be a great coding partner, but it's not perfect. Think of AI-generated code as a draft that needs your expert polish. Always:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Double-check AI code with thorough reviews.&lt;/li&gt;
&lt;li&gt;Use Angular-friendly linters and static analysis to catch issues.&lt;/li&gt;
&lt;li&gt;Stick to strict typing—never settle for unclear &lt;code&gt;any&lt;/code&gt; types.&lt;/li&gt;
&lt;li&gt;Test everything with unit and integration tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Takeaway 1&lt;/strong&gt;: AI code helps you start faster, but your review keeps it reliable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Takeaway 2&lt;/strong&gt;: Keep your tools sharp to maintain clean, consistent Angular code.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="03db"&gt;Combining AI with Signals in Angular&lt;/h4&gt;
&lt;p&gt;Angular Signals bring reactive magic to your app's state. When mixing AI and Signals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use Signals to keep state changes clear and reactive.&lt;/li&gt;
&lt;li&gt;Let AI handle repetitive tasks but keep manual control on core state logic.&lt;/li&gt;
&lt;li&gt;Avoid shortcuts that make your state unpredictable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Takeaway 1&lt;/strong&gt;: Signals keep your app's state neat and responsive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Takeaway 2&lt;/strong&gt;: AI is your helper, not your boss—stay in control of your state.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="3070"&gt;Avoiding Common Pitfalls&lt;/h4&gt;
&lt;p&gt;AI is tempting—fast suggestions, easy code—but beware! Don't let AI:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Push you to skip Angular best practices like OnPush change detection.&lt;/li&gt;
&lt;li&gt;Flood your templates with complicated AI-generated logic.&lt;/li&gt;
&lt;li&gt;Replace good architecture with quick fixes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Takeaway 1&lt;/strong&gt;: Understand every AI line before you include it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Takeaway 2&lt;/strong&gt;: Stick to solid design principles; AI is a tool, not a shortcut.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="5c00"&gt;Practical AI + Angular Workflows&lt;/h4&gt;
&lt;p&gt;Here's a winning formula:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Kickstart your component or test with AI-generated code.&lt;/li&gt;
&lt;li&gt;Review, refactor, and tune that code for your app's style.&lt;/li&gt;
&lt;li&gt;Use AI-driven tools for catching bugs early.&lt;/li&gt;
&lt;li&gt;Combine AI with Signals for slick, reactive apps.&lt;/li&gt;
&lt;li&gt;Keep the human eye sharp with code reviews and CI checks.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key Takeaway 1&lt;/strong&gt;: AI speeds you up, but discipline keeps you on track.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key Takeaway 2&lt;/strong&gt;: Teamwork between AI and developers builds the best apps.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Embracing AI in Angular development unlocks new productivity levels—when done right. With solid reviews, Angular best practices, and a respectful partnership with AI, developers build great apps faster without losing quality.&lt;/p&gt;
&lt;h3 id="98cf"&gt;🎯 Conclusion&lt;/h3&gt;
&lt;p&gt;AI is making life easier for Angular developers by helping them code faster and smarter.&lt;/p&gt;
&lt;p&gt;Tools like GitHub Copilot give real-time suggestions, handle repetitive tasks, and catch mistakes early, saving valuable time. It's important to use AI as a helpful assistant — always double-check its suggestions and combine them with your own knowledge to keep your code clean and reliable.&lt;/p&gt;
&lt;p&gt;While AI isn't perfect and sometimes makes errors, staying curious and connected with other developers can help you get the most out of these tools.&lt;/p&gt;
&lt;p&gt;Try using AI in your Angular projects and explore resources like official documentation, workshops, and community forums to boost your skills and build great apps more quickly.&lt;/p&gt;
&lt;h3 id="dac4"&gt;Thanks for Reading 🙌&lt;/h3&gt;
&lt;p&gt;I hope these tips help you ship better, faster, and more maintainable frontend projects.&lt;/p&gt;

&lt;p&gt;Author: &lt;a href="https://medium.com/@karol-modelski" rel="noopener noreferrer"&gt;Karol Modelski&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>angular</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How we stopped shipping broken Angular code by making mistakes impossible</title>
      <dc:creator>Kafeel Ahmad</dc:creator>
      <pubDate>Wed, 07 Jan 2026 17:33:03 +0000</pubDate>
      <link>https://dev.to/kafeel-ahmad/how-we-stopped-shipping-broken-angular-code-by-making-mistakes-impossible-2bc5</link>
      <guid>https://dev.to/kafeel-ahmad/how-we-stopped-shipping-broken-angular-code-by-making-mistakes-impossible-2bc5</guid>
      <description>&lt;p&gt;&lt;em&gt;Shipping bugs doesn't usually happen because developers are careless. It happens because teams rely on individual discipline instead of enforced systems.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Most teams are made up of developers with different backgrounds, learning paths, and mental models. That diversity is valuable — but without strict guardrails, it turns codebases into negotiation spaces. One developer tolerates any, another ignores lint warnings, someone else promises to "fix it later" and actually believes it. The product absorbs all of it.&lt;/p&gt;
&lt;p&gt;Early in your career, this feels normal. You assume structure will emerge organically. It won't.&lt;/p&gt;
&lt;p&gt;We've seen this story before with TypeScript itself. Many of us resisted it at first, complained about friction, and eventually realized it wasn't slowing us down — it was preventing entire classes of bugs. The same pattern repeats with every serious quality boundary we avoid enforcing.&lt;/p&gt;
&lt;p&gt;I've started projects where junior developers defaulted to any, enabled ESLint only to silence it, skipped formatting rules, and treated lint errors as optional. Not because they were careless — but because the system quietly permitted it.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;These are not people's problems. They are system failures.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;When you design a product, you deliberately choose constraints. Code quality deserves the same level of intent. Rules should be explicit, enforced early, and impossible to bypass.&lt;/p&gt;
&lt;p&gt;In this article, I'll walk through the tooling and discipline we use in Angular to make code predictable, scalable, and unemotional about quality — so mistakes are caught immediately, not negotiated in pull requests later.&lt;/p&gt;
&lt;h3 id="df93"&gt;Why Angular needs stricter enforcement than most frontend stacks&lt;/h3&gt;
&lt;p&gt;Angular is not fragile, but it is &lt;strong&gt;forgiving in dangerous ways&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;On the surface, Angular feels structured: modules, decorators, dependency injection, lifecycle hooks. That apparent structure gives teams a false sense of safety. People assume the framework will "handle things" for them. It won't.&lt;/p&gt;
&lt;p&gt;Angular applications hide complexity behind abstractions. A small mistake rarely fails loudly. It degrades quietly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Templates are a second language.
&lt;/strong&gt;They look like HTML, but they behave like TypeScript — except the compiler won't save you unless you force it to. Without strict template checking, it's easy to ship runtime errors that no test or build step ever caught.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RxJS amplifies this problem.&lt;/strong&gt;
Subscriptions don't fail immediately when misused. They leak memory, duplicate side effects, and introduce timing bugs that only appear under load. Angular won't stop you from subscribing incorrectly. It will happily let the problem grow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dependency injection adds another layer of indirection.
&lt;/strong&gt;A service with loose typing or unsafe assumptions spreads risk across the entire application. One careless &lt;code&gt;any&lt;/code&gt; doesn't stay local — it infects everything downstream.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;None of this makes Angular bad.
&lt;/strong&gt;It makes it powerful. But power without enforcement is how technical debt becomes invisible.&lt;/p&gt;
&lt;p&gt;In loosely structured stacks, mistakes are obvious early. In Angular, mistakes age quietly. By the time they hurt, they're baked into templates, services, and shared abstractions.&lt;/p&gt;
&lt;p&gt;This is why Angular teams cannot rely on "best practices" as guidelines. They have to be &lt;strong&gt;rules&lt;/strong&gt;. Enforced at build time. Enforced before code reaches review. Enforced before humans are asked to notice what tools could have caught instantly.&lt;/p&gt;
&lt;h3 id="91d1"&gt;Tools don't create discipline — enforcement does&lt;/h3&gt;
&lt;p&gt;Most frontend teams already use the "right" tools.&lt;/p&gt;
&lt;p&gt;TypeScript is enabled.
 ESLint exists.
 Prettier is installed.
 Tests run sometimes.&lt;/p&gt;
&lt;p&gt;And yet the same problems persist.&lt;/p&gt;
&lt;p&gt;That's because tools, by themselves, are passive. They suggest. They warn. They politely complain. Developers can ignore all of it and still ship code.&lt;/p&gt;
&lt;p&gt;Discipline appears only when the system removes choice.&lt;/p&gt;
&lt;p&gt;In our setup, tools are not helpers. They are &lt;strong&gt;gates&lt;/strong&gt;. Each one exists at a specific stage of development, with a specific job: catch a class of mistakes as early as possible and stop the process immediately.&lt;/p&gt;
&lt;p&gt;This isn't about trust.
It's about economics. Fixing mistakes earlier is cheaper than discussing them later.&lt;/p&gt;
&lt;h3 id="8f21"&gt;A note on configuration&lt;/h3&gt;
&lt;p&gt;This article focuses on &lt;em&gt;why&lt;/em&gt; these constraints matter and how they work together as a system.&lt;/p&gt;
&lt;p&gt;Full configuration files are intentionally not inlined here. They're verbose, change over time, and are better consumed directly. Complete setup references and links are provided at the end for anyone who wants to apply this approach in their own Angular project.&lt;/p&gt;
&lt;h3 id="c9fb"&gt;TypeScript strictness: the non-negotiable foundation&lt;/h3&gt;
&lt;p&gt;Before adding a single tool, strict typing has to be mandatory.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;strict&lt;/code&gt; mode is off, everything else is cosmetic. ESLint becomes advisory. Reviews turn subjective. Bugs escape silently.&lt;/p&gt;
&lt;p&gt;Strict TypeScript forces the codebase to answer uncomfortable questions early:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What can actually be &lt;code&gt;null&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;What assumptions are unsafe?&lt;/li&gt;
&lt;li&gt;Which APIs are lying about their contracts?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Turning strict mode on late feels painful because it exposes debt that was already there. That pain isn't a tooling issue — it's feedback.&lt;/p&gt;
&lt;p&gt;In Angular, this also means enabling strict template type checking. Without it, a large portion of your application is effectively untyped. Runtime template errors become inevitable, and debugging them is far more expensive than preventing them.&lt;/p&gt;
&lt;p&gt;Strictness doesn't slow teams down.
It removes ambiguity — and ambiguity is where bugs hide.&lt;/p&gt;
&lt;h3 id="d593"&gt;ESLint: from style checker to design enforcer&lt;/h3&gt;
&lt;p&gt;Most teams stop too early with ESLint.&lt;/p&gt;
&lt;p&gt;Used poorly, ESLint argues about formatting and naming.
Used correctly, it enforces architecture.&lt;/p&gt;
&lt;p&gt;In Angular, this means rules that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Block &lt;code&gt;any&lt;/code&gt; and unsafe type assertions&lt;/li&gt;
&lt;li&gt;Prevent forgotten subscription teardowns&lt;/li&gt;
&lt;li&gt;Catch floating promises and ignored async work&lt;/li&gt;
&lt;li&gt;Enforce predictable RxJS patterns&lt;/li&gt;
&lt;li&gt;Fail builds when unsafe assumptions leak into components or templates&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If ESLint errors are allowed to pass "just this once", the system is already compromised.&lt;/p&gt;
&lt;p&gt;ESLint is not there to nag developers.
 It exists so humans don't have to.&lt;/p&gt;
&lt;h3 id="e23b"&gt;Prettier: eliminating meaningless choice&lt;/h3&gt;
&lt;p&gt;Formatting is not a quality signal. It's noise.&lt;/p&gt;
&lt;p&gt;Prettier exists to eliminate that noise completely. No preferences. No debates. No pull-request comments about spacing.&lt;/p&gt;
&lt;p&gt;It runs automatically, before code is committed.
If formatting ever fails in CI, something upstream is broken.&lt;/p&gt;
&lt;p&gt;This isn't about aesthetics.
It's about preserving attention for problems that actually matter.&lt;/p&gt;
&lt;h3 id="1792"&gt;Husky and pre-commit hooks: enforcing standards at the only time that matters&lt;/h3&gt;
&lt;p&gt;Code review is too late to catch basic mistakes.&lt;/p&gt;
&lt;p&gt;By the time a human sees the code, the system has already failed. At that point, the only options left are comments, rewrites, and follow-up commits — all avoidable work.&lt;/p&gt;
&lt;p&gt;Pre-commit hooks move feedback to the moment code is written. When ESLint fails or formatting is wrong, the commit simply doesn't happen. There's no discussion, no negotiation, no "I'll fix it later". The system enforces the rule.&lt;/p&gt;
&lt;p&gt;The key idea is &lt;strong&gt;graduated enforcement&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Not every check belongs at the same stage.&lt;/p&gt;
&lt;p&gt;Fast, local feedback should happen continuously while writing code.
Slightly heavier checks should block commits. The strictest checks should block pushes and fail CI. Each layer exists to catch mistakes at the cheapest possible point.&lt;/p&gt;
&lt;h4 id="f3ed"&gt;Continuous feedback while coding&lt;/h4&gt;
&lt;p&gt;Waiting until commit time is already late.&lt;/p&gt;
&lt;p&gt;Linting should run while the developer is typing, not after they think they're done. This is where watch mode matters.&lt;/p&gt;
&lt;p&gt;For example, running Angular and ESLint together:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-punctuation"&gt;{&lt;/span&gt;
  &lt;span class="hljs-attr"&gt;"scripts"&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;:&lt;/span&gt; &lt;span class="hljs-punctuation"&gt;{&lt;/span&gt;
    &lt;span class="hljs-attr"&gt;"dev"&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;:&lt;/span&gt; &lt;span class="hljs-string"&gt;"ng lint &amp;amp;&amp;amp; ng serve"&lt;/span&gt;
  &lt;span class="hljs-punctuation"&gt;}&lt;/span&gt;
&lt;span class="hljs-punctuation"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This ensures the application doesn't even start if linting fails. The feedback is immediate.&lt;/p&gt;
&lt;p&gt;For teams that want even tighter loops, ESLint can run independently in watch mode alongside the dev server:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-punctuation"&gt;{&lt;/span&gt;
  &lt;span class="hljs-attr"&gt;"scripts"&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;:&lt;/span&gt; &lt;span class="hljs-punctuation"&gt;{&lt;/span&gt;
    &lt;span class="hljs-attr"&gt;"dev"&lt;/span&gt;&lt;span class="hljs-punctuation"&gt;:&lt;/span&gt; &lt;span class="hljs-string"&gt;"concurrently \"ng serve\" \"eslint . --ext .ts,.html --watch\""&lt;/span&gt;
  &lt;span class="hljs-punctuation"&gt;}&lt;/span&gt;
&lt;span class="hljs-punctuation"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This turns linting into a live signal instead of a delayed punishment. Errors appear while code is being written, not after context has already shifted.&lt;/p&gt;
&lt;p&gt;This is especially effective when combined with Angular template linting and RxJS rules, where subtle mistakes are easy to miss and expensive to debug later.&lt;/p&gt;
&lt;h4 id="41d4"&gt;Pre-commit: fast, unavoidable checks&lt;/h4&gt;
&lt;p&gt;Pre-commit hooks exist to stop bad code from entering history.&lt;/p&gt;
&lt;p&gt;At this stage, only &lt;strong&gt;fast checks&lt;/strong&gt; belong here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ESLint on changed files&lt;/li&gt;
&lt;li&gt;Prettier formatting&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nothing slow. Nothing flaky.&lt;/p&gt;
&lt;p&gt;If a commit fails, the fix should take seconds, not minutes. Otherwise, developers will bypass the system — and a bypassable system is already broken.&lt;/p&gt;
&lt;p&gt;This is where tools like Husky and lint-staged matter, not as setup details, but as enforcement mechanisms. The rule is simple:
&lt;em&gt;&lt;strong&gt;If the code doesn't meet the baseline, it doesn't get committed.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h4 id="6180"&gt;Pre-push and CI: stricter, slower, final&lt;/h4&gt;
&lt;p&gt;More expensive checks belong later.&lt;/p&gt;
&lt;p&gt;Before pushing — and again in CI — the system can afford to be strict:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Full &lt;code&gt;ng lint&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Type checking with &lt;code&gt;tsc --noEmit&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Tests&lt;/li&gt;
&lt;li&gt;Build validation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point, failure is intentional. It protects the shared branch, not the individual developer.&lt;/p&gt;
&lt;p&gt;Importantly, CI should never be the &lt;em&gt;first&lt;/em&gt; place an error is discovered. If CI catches issues developers never saw locally, feedback loops are too slow.&lt;/p&gt;
&lt;h4 id="6944"&gt;RxJS and Angular-specific enforcement&lt;/h4&gt;
&lt;p&gt;This setup becomes far more valuable when combined with Angular- and RxJS-specific lint rules.&lt;/p&gt;
&lt;p&gt;Rules from &lt;code&gt;eslint-plugin-rxjs&lt;/code&gt; and &lt;code&gt;@angular-eslint/schematics&lt;/code&gt; catch issues humans routinely miss:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Forgotten subscription teardowns&lt;/li&gt;
&lt;li&gt;Nested subscriptions&lt;/li&gt;
&lt;li&gt;Unsafe async flows&lt;/li&gt;
&lt;li&gt;Template misuse that compiles but fails at runtime&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are not stylistic preferences. They are bug prevention mechanisms.&lt;/p&gt;
&lt;p&gt;Once enforced at commit time, these mistakes simply stop happening.&lt;/p&gt;
&lt;h3 id="8959"&gt;Commit discipline: code is not the only artifact&lt;/h3&gt;
&lt;p&gt;Quality doesn't stop at source files.&lt;/p&gt;
&lt;p&gt;Commit history is part of the system. Vague messages like "fix", "final", or "working now" destroy context, slow debugging, and make releases harder than they need to be.&lt;/p&gt;
&lt;p&gt;Structured commit messages turn history into documentation. They make intent explicit long after the code has changed.&lt;/p&gt;
&lt;p&gt;This isn't bureaucracy.
It's professional hygiene.&lt;/p&gt;
&lt;h3 id="906b"&gt;References and setup&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Angular&lt;a rel="noopener noreferrer" title="" href="https://v17.angular.io/guide/strict-mode"&gt; strict mode&lt;/a&gt; and &lt;a rel="noopener noreferrer" title="" href="https://angular.dev/tools/cli/template-typecheck#strict-mode"&gt;strict templates&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a rel="None noopener noreferrer" title="None" href="https://www.npmjs.com/package/angular-eslint"&gt;@angular-eslint&lt;/a&gt; and &lt;a rel="None noopener noreferrer" title="None" href="https://www.npmjs.com/package/@typescript-eslint/eslint-plugin"&gt;@typescript-eslint/strict&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a rel="None noopener noreferrer" title="None" href="https://www.npmjs.com/package/eslint-plugin-rxjs-angular-x"&gt;RxJS ESLint rules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel="None noopener noreferrer" title="None" href="https://prettier.io/"&gt;Prettier&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a rel="None noopener noreferrer" title="None" href="https://typicode.github.io/husky/"&gt;Husky&lt;/a&gt;, &lt;a rel="None noopener noreferrer" title="None" href="https://pre-commit.com/"&gt;pre-commit&lt;/a&gt; and &lt;a rel="None noopener noreferrer" title="None" href="https://www.npmjs.com/package/lint-staged"&gt;lint-staged&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a rel="None noopener noreferrer" title="None" href="https://commitlint.js.org/"&gt;Commitlint&lt;/a&gt; and &lt;a rel="None noopener noreferrer" title="None" href="https://www.conventionalcommits.org/en/v1.0.0/"&gt;Conventional Commits&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you enjoyed reading this, a clap, follow, or comment would mean a lot. It literally takes a minute, but it's motivating for me to write more content like this.&lt;/p&gt;

&lt;p&gt;Author: &lt;a href="https://medium.com/@ayushmaurya461" rel="noopener noreferrer"&gt;Ayush Maurya&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>angular</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Understanding afterNextRender(), afterEveryRender(), and afterRenderEffect() in Angular 18+</title>
      <dc:creator>Kafeel Ahmad</dc:creator>
      <pubDate>Thu, 01 Jan 2026 05:47:25 +0000</pubDate>
      <link>https://dev.to/kafeel-ahmad/understanding-afternextrender-aftereveryrender-and-afterrendereffect-in-angular-18-3bjj</link>
      <guid>https://dev.to/kafeel-ahmad/understanding-afternextrender-aftereveryrender-and-afterrendereffect-in-angular-18-3bjj</guid>
      <description>&lt;p&gt;Angular's reactivity model has evolved dramatically since the introduction of Signals. Alongside this fine-grained change detection came a new generation of &lt;em&gt;render-aware hooks: &lt;/em&gt;&lt;code&gt;afterNextRender()&lt;/code&gt;, &lt;code&gt;afterEveryRender()&lt;/code&gt;, &lt;code&gt;afterRenderEffect()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These hooks bridge the gap between &lt;strong&gt;reactivity&lt;/strong&gt; and &lt;strong&gt;the actual DOM rendering phase&lt;/strong&gt;, replacing many workarounds that previously relied on &lt;code&gt;setTimeout()&lt;/code&gt; or heavy lifecycle juggling.&lt;/p&gt;
&lt;p&gt;In this article, we'll explore how each hook works — not through dry theory, but via &lt;strong&gt;four hands-on exercises&lt;/strong&gt; that reveal when and why to use them.
 By the end, we'll compare &lt;code&gt;afterNextRender()&lt;/code&gt; with the old &lt;code&gt;ngAfterViewInit()&lt;/code&gt; to see exactly why the new model is more predictable and fine-grained.&lt;/p&gt;
&lt;h4 id="c6d3"&gt;Task 1 — The First Render with afterNextRender()&lt;/h4&gt;
&lt;p&gt;Let's start simple:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { &lt;span class="hljs-title class_"&gt;Component&lt;/span&gt;, afterNextRender } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;'@angular/core'&lt;/span&gt;;

&lt;span class="hljs-meta"&gt;@Component&lt;/span&gt;({
  &lt;span class="hljs-attr"&gt;selector&lt;/span&gt;: &lt;span class="hljs-string"&gt;'app-timer'&lt;/span&gt;,
  &lt;span class="hljs-attr"&gt;standalone&lt;/span&gt;: &lt;span class="hljs-literal"&gt;true&lt;/span&gt;,
  &lt;span class="hljs-attr"&gt;template&lt;/span&gt;: &lt;span class="hljs-string"&gt;`&amp;lt;div #el&amp;gt;Timer works!&amp;lt;/div&amp;gt;`&lt;/span&gt;,
})
&lt;span class="hljs-keyword"&gt;export&lt;/span&gt; &lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;TimerComponent&lt;/span&gt; {
  &lt;span class="hljs-title function_"&gt;constructor&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;/span&gt;) {
    &lt;span class="hljs-title function_"&gt;afterNextRender&lt;/span&gt;(&lt;span class="hljs-function"&gt;() =&amp;gt;&lt;/span&gt; {
      &lt;span class="hljs-variable language_"&gt;console&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;log&lt;/span&gt;(&lt;span class="hljs-string"&gt;'DOM ready'&lt;/span&gt;);
    });
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt; When does &lt;code&gt;console.log('DOM ready')&lt;/code&gt; fire?
&lt;strong&gt;Answer:&lt;/strong&gt; After the component's first DOM render — not immediately, and not during change detection.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;afterNextRender()&lt;/code&gt; waits for the &lt;em&gt;commit phase&lt;/em&gt; (the point when Angular finishes rendering to the DOM). It's perfect for DOM measurements, focus handling, or library initialization that must happen &lt;strong&gt;after&lt;/strong&gt; the HTML is painted.&lt;/p&gt;
&lt;h4 id="3e41"&gt;Task 2 — Continuous Updates with afterEveryRender()&lt;/h4&gt;
&lt;p&gt;Now let's see what happens when we re-render a component multiple times:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { &lt;span class="hljs-title class_"&gt;Component&lt;/span&gt;, signal, afterEveryRender } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;'@angular/core'&lt;/span&gt;;

&lt;span class="hljs-meta"&gt;@Component&lt;/span&gt;({
  &lt;span class="hljs-attr"&gt;selector&lt;/span&gt;: &lt;span class="hljs-string"&gt;'app-counter'&lt;/span&gt;,
  &lt;span class="hljs-attr"&gt;standalone&lt;/span&gt;: &lt;span class="hljs-literal"&gt;true&lt;/span&gt;,
  &lt;span class="hljs-attr"&gt;template&lt;/span&gt;: &lt;span class="hljs-string"&gt;`
    &amp;lt;div&amp;gt;{{ count() }}&amp;lt;/div&amp;gt;
    &amp;lt;button (click)="increment()"&amp;gt;+&amp;lt;/button&amp;gt;
  `&lt;/span&gt;
})
&lt;span class="hljs-keyword"&gt;export&lt;/span&gt; &lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;CounterComponent&lt;/span&gt; {
  count = &lt;span class="hljs-title function_"&gt;signal&lt;/span&gt;(&lt;span class="hljs-number"&gt;0&lt;/span&gt;);

  &lt;span class="hljs-title function_"&gt;constructor&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;/span&gt;) {
    &lt;span class="hljs-title function_"&gt;afterEveryRender&lt;/span&gt;(&lt;span class="hljs-function"&gt;() =&amp;gt;&lt;/span&gt; {
      &lt;span class="hljs-variable language_"&gt;console&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;log&lt;/span&gt;(&lt;span class="hljs-string"&gt;'Rendered with count ='&lt;/span&gt;, &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;count&lt;/span&gt;());
    });
  }

  &lt;span class="hljs-title function_"&gt;increment&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;/span&gt;) {
    &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;count&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;update&lt;/span&gt;(&lt;span class="hljs-function"&gt;&lt;span&gt;v&lt;/span&gt; =&amp;gt;&lt;/span&gt; v + &lt;span class="hljs-number"&gt;1&lt;/span&gt;);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;What happens:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Logs once after the first render,&lt;/li&gt;
&lt;li&gt;Then again after &lt;strong&gt;every&lt;/strong&gt; re-render triggered by a signal update.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;afterEveryRender()&lt;/code&gt; doesn't react to all signals in the app — only when &lt;em&gt;this component&lt;/em&gt; is actually re-rendered.&lt;/p&gt;
&lt;p&gt;It's ideal for syncing DOM state or performing measurements when the view changes.&lt;/p&gt;
&lt;h4 id="647b"&gt;Task 3 — Reactive DOM Effects with afterRenderEffect()&lt;/h4&gt;
&lt;p&gt;This one brings everything together — reactivity &lt;em&gt;and&lt;/em&gt; rendering:&lt;/p&gt;
&lt;pre&gt;code class="p-2 bg-gray-100 dark:bg-gray-900 overflow-x-auto language-typescript hljs" data-highlighted="yes"&amp;gt;&lt;span&gt;import&lt;/span&gt; { &lt;span&gt;Component&lt;/span&gt;, signal, afterRenderEffect } &lt;span&gt;from&lt;/span&gt; &lt;span&gt;'@angular/core'&lt;/span&gt;;

&lt;span&gt;@Component&lt;/span&gt;({
  &lt;span&gt;selector&lt;/span&gt;: &lt;span&gt;'app-progress'&lt;/span&gt;,
  &lt;span&gt;standalone&lt;/span&gt;: &lt;span&gt;true&lt;/span&gt;,
  &lt;span&gt;template&lt;/span&gt;: &lt;span&gt;`&amp;lt;progress [value]="progress()" max="100"&amp;gt;&amp;lt;/progress&amp;gt;`&lt;/span&gt;,
})
&lt;span&gt;export&lt;/span&gt; &lt;span&gt;class&lt;/span&gt; &lt;span&gt;ProgressComponent&lt;/span&gt; {
  progress = &lt;span&gt;signal&lt;/span&gt;(&lt;span&gt;0&lt;/span&gt;);

  &lt;span&gt;constructor&lt;/span&gt;(&lt;span&gt;&lt;/span&gt;) {
    &lt;span&gt;afterRenderEffect&lt;/span&gt;(&lt;span&gt;() =&amp;gt;&lt;/span&gt; {
      &lt;span&gt;console&lt;/span&gt;.&lt;span&gt;log&lt;/span&gt;(&lt;span&gt;'Render effect with progress'&lt;/span&gt;, &lt;span&gt;this&lt;/span&gt;.&lt;span&gt;progress&lt;/span&gt;());
      &lt;span&gt;if&lt;/span&gt; (&lt;span&gt;this&lt;/span&gt;.&lt;span&gt;progress&lt;/span&gt;() &amp;lt; &lt;span&gt;100&lt;/span&gt;) {
        &lt;span&gt;this&lt;/span&gt;.&lt;span&gt;progress&lt;/span&gt;.&lt;span&gt;update&lt;/span&gt;(&lt;span&gt;&lt;span&gt;v&lt;/span&gt; =&amp;gt;&lt;/span&gt; v + &lt;span&gt;25&lt;/span&gt;);
      }
    });
  }
}&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Logs five times — &lt;code&gt;0 → 25 → 50 → 75 → 100&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Each time, Angular commits the DOM and then re-runs the effect because the signal changed.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;afterRenderEffect()&lt;/code&gt; automatically tracks signal dependencies and re-runs whenever they change &lt;strong&gt;and&lt;/strong&gt; a new render occurs.&lt;/p&gt;
&lt;p&gt;It's like a &lt;em&gt;render-aware effect&lt;/em&gt; — the cleanest way to tie DOM updates to reactive data.&lt;/p&gt;
&lt;h4 id="b22a"&gt;Task 4 — Comparing ngAfterViewInit() vs afterNextRender()&lt;/h4&gt;
&lt;p&gt;Let's look at a side-by-side example — a classic certification trap.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { &lt;span class="hljs-title class_"&gt;Component&lt;/span&gt;, &lt;span class="hljs-title class_"&gt;ElementRef&lt;/span&gt;, &lt;span class="hljs-title class_"&gt;ViewChild&lt;/span&gt;, afterNextRender } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;'@angular/core'&lt;/span&gt;;

&lt;span class="hljs-meta"&gt;@Component&lt;/span&gt;({
  &lt;span class="hljs-attr"&gt;selector&lt;/span&gt;: &lt;span class="hljs-string"&gt;'app-render-vs-viewinit'&lt;/span&gt;,
  &lt;span class="hljs-attr"&gt;standalone&lt;/span&gt;: &lt;span class="hljs-literal"&gt;true&lt;/span&gt;,
  &lt;span class="hljs-attr"&gt;template&lt;/span&gt;: &lt;span class="hljs-string"&gt;`
    &amp;lt;div #box class="demo-box"&amp;gt;
      &amp;lt;h2&amp;gt;Demo&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;Content with font and padding.&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  `&lt;/span&gt;,
  &lt;span class="hljs-attr"&gt;styles&lt;/span&gt;: [&lt;span class="hljs-string"&gt;`
    .demo-box {
      padding: 24px;
      border: 1px solid #ccc;
      /* simulate delayed style application */
      animation: applyHeight 0s 1 forwards;
    }
    @keyframes applyHeight {
      to { margin-top: 20px; }
    }
  `&lt;/span&gt;]
})
&lt;span class="hljs-keyword"&gt;export&lt;/span&gt; &lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title class_"&gt;RenderVsViewInitComponent&lt;/span&gt; {
  &lt;span class="hljs-meta"&gt;@ViewChild&lt;/span&gt;(&lt;span class="hljs-string"&gt;'box'&lt;/span&gt;, { &lt;span class="hljs-attr"&gt;static&lt;/span&gt;: &lt;span class="hljs-literal"&gt;true&lt;/span&gt; }) box!: &lt;span class="hljs-title class_"&gt;ElementRef&lt;/span&gt;&amp;lt;&lt;span class="hljs-title class_"&gt;HTMLDivElement&lt;/span&gt;&amp;gt;;

  &lt;span class="hljs-title function_"&gt;constructor&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;/span&gt;) {
    &lt;span class="hljs-title function_"&gt;afterNextRender&lt;/span&gt;(&lt;span class="hljs-function"&gt;() =&amp;gt;&lt;/span&gt; {
      &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; h = &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;box&lt;/span&gt;.&lt;span class="hljs-property"&gt;nativeElement&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;getBoundingClientRect&lt;/span&gt;().&lt;span class="hljs-property"&gt;height&lt;/span&gt;;
      &lt;span class="hljs-variable language_"&gt;console&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;log&lt;/span&gt;(&lt;span class="hljs-string"&gt;'[afterNextRender] height ='&lt;/span&gt;, h);
    });
  }

  &lt;span class="hljs-title function_"&gt;ngAfterViewInit&lt;/span&gt;(&lt;span class="hljs-params"&gt;&lt;/span&gt;) {
    &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; h = &lt;span class="hljs-variable language_"&gt;this&lt;/span&gt;.&lt;span class="hljs-property"&gt;box&lt;/span&gt;.&lt;span class="hljs-property"&gt;nativeElement&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;getBoundingClientRect&lt;/span&gt;().&lt;span class="hljs-property"&gt;height&lt;/span&gt;;
    &lt;span class="hljs-variable language_"&gt;console&lt;/span&gt;.&lt;span class="hljs-title function_"&gt;log&lt;/span&gt;(&lt;span class="hljs-string"&gt;'[ngAfterViewInit] height ='&lt;/span&gt;, h);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The first log (&lt;code&gt;ngAfterViewInit&lt;/code&gt;) appears earlier — during the same change detection cycle.&lt;/li&gt;
&lt;li&gt;The second (&lt;code&gt;afterNextRender&lt;/code&gt;) runs later, after Angular has committed the DOM and processed layout.&lt;/li&gt;
&lt;li&gt;The height measured in &lt;code&gt;afterNextRender()&lt;/code&gt; is often larger or more accurate (reflecting applied CSS/layout changes).&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="12dc"&gt;Summary — Old vs. New Hooks&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ngAfterViewInit()&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fires: after the view is created, &lt;strong&gt;before DOM commit&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Best for: getting references (&lt;code&gt;ViewChild&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Works in SSR: &lt;strong&gt;Yes&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;afterNextRender()&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fires: after the DOM is fully rendered &lt;strong&gt;and styles applied&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Best for: DOM measurements, focus, third-party libs&lt;/li&gt;
&lt;li&gt;Works in SSR: &lt;strong&gt;No&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;afterEveryRender()&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fires: &lt;strong&gt;after every re-render&lt;/strong&gt; of the component&lt;/li&gt;
&lt;li&gt;Best for: logging, syncing, UI instrumentation&lt;/li&gt;
&lt;li&gt;Works in SSR: &lt;strong&gt;No&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;afterRenderEffect()&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fires: after each render caused by signal changes&lt;/li&gt;
&lt;li&gt;Best for: reactive side effects tied to DOM&lt;/li&gt;
&lt;li&gt;Works in SSR: &lt;strong&gt;No&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="eb4e"&gt;Takeaways&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;afterNextRender()&lt;/code&gt; is &lt;strong&gt;asynchronous&lt;/strong&gt; relative to change detection — it "waits" until the DOM is ready.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;afterEveryRender()&lt;/code&gt; tracks &lt;strong&gt;actual renders&lt;/strong&gt;, not all reactive updates.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;afterRenderEffect()&lt;/code&gt; automatically re-runs based on &lt;strong&gt;signal dependencies&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;These hooks do &lt;strong&gt;not&lt;/strong&gt; run during SSR, which helps prevent hydration errors.&lt;/li&gt;
&lt;li&gt;Don't mutate state in &lt;code&gt;afterEveryRender()&lt;/code&gt; — it can lead to render loops. Use &lt;code&gt;afterRenderEffect()&lt;/code&gt; instead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="5c85"&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Techniques once considered standard — like &lt;code&gt;ngAfterViewInit()&lt;/code&gt; and &lt;code&gt;setTimeout()&lt;/code&gt; — may now be seen as legacy workarounds. The modern Angular render scheduler and its trio of hooks bring true fine-grained reactivity: predictable, DOM-aware, and cleanly separated from change detection.&lt;/p&gt;
&lt;h3 id="d631"&gt;A message from our Founder&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Hey, &lt;/strong&gt;&lt;strong&gt;&lt;a rel="noopener noreferrer" title="" href="https://linkedin.com/in/sunilsandhu"&gt;Sunil&lt;/a&gt;&lt;/strong&gt;&lt;strong&gt; here.&lt;/strong&gt; I wanted to take a moment to thank you for reading until the end and for being a part of this community.&lt;/p&gt;
&lt;p&gt;Did you know that our team run these publications as a volunteer effort to over 3.5m monthly readers? &lt;strong&gt;We don't receive any funding, we do this to support the community. ❤️&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you want to show some love, please take a moment to &lt;strong&gt;follow me on &lt;/strong&gt;&lt;strong&gt;&lt;a rel="noopener noreferrer" title="" href="https://linkedin.com/in/sunilsandhu"&gt;LinkedIn&lt;/a&gt;&lt;/strong&gt;&lt;strong&gt;, &lt;/strong&gt;&lt;strong&gt;&lt;a rel="noopener noreferrer" title="" href="https://tiktok.com/@messyfounder"&gt;TikTok&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;a rel="noopener noreferrer" title="" href="https://instagram.com/sunilsandhu"&gt;Instagram&lt;/a&gt;&lt;/strong&gt;. You can also subscribe to our &lt;strong&gt;&lt;a rel="noopener noreferrer" title="" href="https://newsletter.plainenglish.io/"&gt;weekly newsletter&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;And before you go, don't forget to &lt;strong&gt;clap&lt;/strong&gt; and &lt;strong&gt;follow&lt;/strong&gt; the writer️!&lt;/p&gt;

&lt;p&gt;Author: &lt;a href="https://javascript.plainenglish.io/understanding-afternextrender-aftereveryrender-and-afterrendereffect-in-angular-18-6610f4ee87af" rel="noopener noreferrer"&gt;Maciej Osytek&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>angular</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How Angular Signal Forms Handle Validation CSS Classes</title>
      <dc:creator>Kafeel Ahmad</dc:creator>
      <pubDate>Wed, 31 Dec 2025 19:23:22 +0000</pubDate>
      <link>https://dev.to/kafeel-ahmad/how-angular-signal-forms-handle-validation-css-classes-jf1</link>
      <guid>https://dev.to/kafeel-ahmad/how-angular-signal-forms-handle-validation-css-classes-jf1</guid>
      <description>&lt;h3 id="7561"&gt;Understanding CSS Classes in Angular Signal Forms&lt;/h3&gt;
&lt;p&gt;If you've started experimenting with &lt;strong&gt;Angular's new Signal Forms API&lt;/strong&gt;, you might have &lt;strong&gt;experienced&lt;/strong&gt; a small panic moment :&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;"Wait… where are &lt;/em&gt;&lt;em&gt;&lt;code&gt;ng-invalid&lt;/code&gt;&lt;/em&gt;&lt;em&gt;, &lt;/em&gt;&lt;em&gt;&lt;code&gt;ng-touched&lt;/code&gt;&lt;/em&gt;&lt;em&gt;, and the other familiar CSS classes?"&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;You're not alone.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;For years, Angular developers have relied on these built-in status classes to:&lt;/p&gt;&lt;/blockquote&gt;
&lt;ul&gt;

&lt;li&gt;Style validation errors&lt;/li&gt;

&lt;li&gt;Trigger UI feedback&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;Integrate with design systems&lt;/strong&gt;&lt;/li&gt;

&lt;/ul&gt;
&lt;p&gt;So when they suddenly disappear, it &lt;em&gt;feels&lt;/em&gt; like a step backward.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;But it's not.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;In fact, &lt;strong&gt;Signal Forms give us something better&lt;/strong&gt;: &lt;strong&gt;explicit, fully controlled CSS class binding&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Let's break it down&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id="14cf"&gt;Why Signal Forms Don't Auto-Apply &lt;code&gt;ng-*&lt;/code&gt; Classes&lt;/h3&gt;
&lt;p&gt;Signal Forms were designed with &lt;strong&gt;predictability and explicitness&lt;/strong&gt; in mind.&lt;/p&gt;
&lt;p&gt;Instead of Angular &lt;strong&gt;automatically&lt;/strong&gt; mutating the DOM with implicit CSS classes, Signal Forms require &lt;strong&gt;you to decide&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;

&lt;li&gt;Which &lt;strong&gt;classes&lt;/strong&gt; exist&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;When&lt;/strong&gt; they are applied&lt;/li&gt;

&lt;li&gt;What logic &lt;strong&gt;controls&lt;/strong&gt; them&lt;/li&gt;

&lt;/ul&gt;
&lt;p&gt;This avoids:&lt;/p&gt;
&lt;ul&gt;

&lt;li&gt;Hidden side effects&lt;/li&gt;

&lt;li&gt;Implicit DOM mutations&lt;/li&gt;

&lt;li&gt;Tight coupling to legacy CSS conventions&lt;/li&gt;

&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;But don't worry — Angular didn't abandon us.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id="44f1"&gt;Reintroducing Status Classes (On Your Terms)&lt;/h3&gt;
&lt;p&gt;Angular provides a way to &lt;strong&gt;define your own CSS status classes&lt;/strong&gt; using a global configuration.&lt;/p&gt;
&lt;p&gt;You do this via a &lt;strong&gt;provider&lt;/strong&gt; that tells Angular:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;"Apply this &lt;/em&gt;&lt;em&gt;&lt;strong&gt;class&lt;/strong&gt;&lt;/em&gt;&lt;em&gt; when this condition is true."&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id="979f"&gt;The Shape of the Configuration&lt;/h3&gt;
&lt;p&gt;At a high level, the &lt;strong&gt;configuration&lt;/strong&gt; looks like this:&lt;/p&gt;
&lt;pre&gt;Copy&lt;code&gt;{&lt;br&gt;
  &lt;span class="hljs-attr"&gt;classes&lt;/span&gt;: {&lt;br&gt;
    [&lt;span class="hljs-attr"&gt;className&lt;/span&gt;: &lt;span class="hljs-built_in"&gt;string&lt;/span&gt;]: &lt;span class="hljs-function"&gt;(&lt;span&gt;state: Field&amp;lt;&lt;span&gt;unknown&lt;/span&gt;&amp;gt;, element?: HTMLElement&lt;/span&gt;) =&amp;gt;&lt;/span&gt; &lt;span class="hljs-built_in"&gt;boolean&lt;/span&gt;;&lt;br&gt;
  }&lt;br&gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;

&lt;li&gt;

&lt;strong&gt;Keys&lt;/strong&gt; → CSS class names&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Values&lt;/strong&gt; → Predicate functions&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Return value&lt;/strong&gt; → &lt;code&gt;true&lt;/code&gt; means "apply this class"&lt;/li&gt;

&lt;/ul&gt;
&lt;h3 id="8b19"&gt;Example 1: Restoring Classic Validation Classes&lt;/h3&gt;
&lt;p&gt;Let's start with the familiar ones — renamed for clarity and originality.&lt;/p&gt;
&lt;pre&gt;Copy&lt;code&gt;&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; { provideSignalFormsConfig, &lt;span class="hljs-variable constant_"&gt;DEFAULT_STATUS_CLASSES&lt;/span&gt; } &lt;span class="hljs-keyword"&gt;from&lt;/span&gt; &lt;span class="hljs-string"&gt;'@angular/forms'&lt;/span&gt;;

&lt;p&gt;&lt;span class="hljs-keyword"&gt;export&lt;/span&gt; &lt;span class="hljs-keyword"&gt;const&lt;/span&gt; appFormProviders = [&lt;br&gt;
  &lt;span class="hljs-title function_"&gt;provideSignalFormsConfig&lt;/span&gt;({&lt;br&gt;
    &lt;span class="hljs-attr"&gt;classes&lt;/span&gt;: {&lt;br&gt;
      ...&lt;span class="hljs-variable constant_"&gt;DEFAULT_STATUS_CLASSES&lt;/span&gt;&lt;br&gt;
    }&lt;br&gt;
  })&lt;br&gt;
];&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This immediately brings back behavior similar to:&lt;/p&gt;
&lt;ul&gt;

&lt;li&gt;invalid&lt;/li&gt;

&lt;li&gt;touched&lt;/li&gt;

&lt;li&gt;dirty&lt;/li&gt;

&lt;li&gt;pristine&lt;/li&gt;

&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;Without Angular &lt;em&gt;forcing&lt;/em&gt; them onto every field.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id="995b"&gt;Example 2: Custom Error Styling Logic&lt;/h3&gt;
&lt;p&gt;Now let's do something we &lt;strong&gt;couldn't easily do before&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Apply a CSS class only when:&lt;/p&gt;
&lt;ul&gt;

&lt;li&gt;The field is invalid&lt;/li&gt;

&lt;li&gt;And the user has interacted with it&lt;/li&gt;

&lt;/ul&gt;
&lt;pre&gt;Copy&lt;code&gt;&lt;span class="hljs-title function_"&gt;provideSignalFormsConfig&lt;/span&gt;({&lt;br&gt;
  &lt;span class="hljs-attr"&gt;classes&lt;/span&gt;: {&lt;br&gt;
    &lt;span class="hljs-string"&gt;'field-error-visible'&lt;/span&gt;: &lt;span class="hljs-function"&gt;(&lt;span&gt;controlState&lt;/span&gt;) =&amp;gt;&lt;/span&gt;&lt;br&gt;
      &lt;span class="hljs-title function_"&gt;controlState&lt;/span&gt;().&lt;span class="hljs-title function_"&gt;invalid&lt;/span&gt;() &amp;amp;&amp;amp; &lt;span class="hljs-title function_"&gt;controlState&lt;/span&gt;().&lt;span class="hljs-title function_"&gt;interacted&lt;/span&gt;()&lt;br&gt;
  }&lt;br&gt;
});&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="e18e"&gt;Result&lt;/h3&gt;
&lt;p&gt;Your UI stays clean until the user actually engages with the form — no more premature red borders 👌&lt;/p&gt;
&lt;h3 id="8e36"&gt;Example 3: Conditional Classes Based on Element Type&lt;/h3&gt;
&lt;p&gt;You can even apply classes based on the &lt;strong&gt;actual DOM element&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;Copy&lt;code&gt;&lt;span class="hljs-title function_"&gt;provideSignalFormsConfig&lt;/span&gt;({&lt;br&gt;
  &lt;span class="hljs-attr"&gt;classes&lt;/span&gt;: {&lt;br&gt;
    &lt;span class="hljs-string"&gt;'multi-line-input'&lt;/span&gt;: &lt;span class="hljs-function"&gt;(&lt;span&gt;&lt;em&gt;, domNode&lt;/em&gt;&lt;/span&gt;) =&amp;gt;&lt;/span&gt;&lt;br&gt;
      domNode?.&lt;span class="hljs-property"&gt;tagName&lt;/span&gt;.&amp;lt;span class="hljs-title function"&amp;gt;toLowerCase() === &lt;span class="hljs-string"&gt;'textarea'&lt;/span&gt;&lt;br&gt;
  }&lt;br&gt;
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is incredibly powerful for:&lt;/p&gt;
&lt;ul&gt;

&lt;li&gt;

&lt;strong&gt;Design&lt;/strong&gt; systems&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Component&lt;/strong&gt; libraries&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Advanced&lt;/strong&gt; form layouts&lt;/li&gt;

&lt;/ul&gt;
&lt;h3 id="84ad"&gt;Comparison: Old Forms vs Signal Forms&lt;/h3&gt;
&lt;p&gt;Here's a quick breakdown 👇&lt;/p&gt;
&lt;img alt="None" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F1%2AQDjhfVKewDZZ9l3xgC-1Tw.png" width="700" height="355"&gt;&lt;h3 id="ad25"&gt;Why This Is Actually a Big Upgrade&lt;/h3&gt;
&lt;p&gt;At first glance, losing automatic CSS classes feels uncomfortable.&lt;/p&gt;
&lt;p&gt;But Signal Forms give us:&lt;/p&gt;
&lt;p&gt;Explicit UI logic&lt;br&gt;
Better &lt;strong&gt;separation&lt;/strong&gt; of concerns&lt;br&gt;
More &lt;strong&gt;predictable&lt;/strong&gt; DOM behavior&lt;br&gt;
Powerful conditional styling&lt;br&gt;
Cleaner design system integration&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Instead of Angular deciding for you, &lt;strong&gt;you're fully in control now&lt;/strong&gt;.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id="7a84"&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;Signal Forms don't remove functionality — they &lt;strong&gt;promote intentional design&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Once you start defining your own class logic:&lt;/p&gt;
&lt;ul&gt;

&lt;li&gt;You'll write clearer styling rules&lt;/li&gt;

&lt;li&gt;Debugging becomes easier&lt;/li&gt;

&lt;li&gt;Your forms scale better across large apps&lt;/li&gt;

&lt;/ul&gt;
&lt;p&gt;The initial surprise fades quickly — and what's left is &lt;strong&gt;a cleaner, more expressive form system&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;This article builds upon patterns shared by &lt;a rel="noopener ugc nofollow noreferrer" title="" href="https://www.linkedin.com/in/roberto-heckers-2313453b/"&gt;Roberto Hecker&lt;/a&gt; in his exploration of Signal forms&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Author: &lt;a href="https://medium.com/@Angular_With_Awais" rel="noopener noreferrer"&gt;Angular_with_Awais&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>angular</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Developer's Guide to Stunning UI: How to Build Without a Designer</title>
      <dc:creator>Kafeel Ahmad</dc:creator>
      <pubDate>Wed, 31 Dec 2025 19:20:32 +0000</pubDate>
      <link>https://dev.to/kafeel-ahmad/the-developers-guide-to-stunning-ui-how-to-build-without-a-designer-29je</link>
      <guid>https://dev.to/kafeel-ahmad/the-developers-guide-to-stunning-ui-how-to-build-without-a-designer-29je</guid>
      <description>&lt;h4 id="0db7"&gt;Hi there 👋&lt;/h4&gt;
&lt;p&gt;Have a great app idea, but uncertain about design and UX? Not a designer yourself, and don't have one on your team? This article is for you!&lt;/p&gt;
&lt;p&gt;Many developers have brilliant ideas and can build apps with their preferred tech stack and code quality standards. But let's be honest — we're not always great at design, and product teams need both developers and designers.&lt;/p&gt;
&lt;p&gt;Here's the good news: You can autonomously build outstanding UIs that rival professional UX/UI designs. The solution? Leverage AI trained on millions of interfaces, giving it collective experience that's hard to beat.&lt;/p&gt;
&lt;h4 id="db35"&gt;Tools I Used for This Workflow&lt;/h4&gt;
&lt;p&gt;Here's my stack for this design-to-code journey:&lt;/p&gt;
&lt;p&gt;— &lt;strong&gt;No-code platforms: &lt;/strong&gt;Lovable, Blink, Bolt (for brainstorming initial UI generation)&lt;/p&gt;
&lt;p&gt;— &lt;strong&gt;Antigravity: Google's&lt;/strong&gt; agentic platform (like Cursor, for example)&lt;/p&gt;
&lt;p&gt;— &lt;strong&gt;Gemini 1.5 Pro &amp;amp; Claude:&lt;/strong&gt; LLMS&lt;/p&gt;
&lt;p&gt;— &lt;strong&gt;My vibe coding setup:&lt;/strong&gt; Angular 21 + Tailwind v4 + DaisyUI v5&lt;/p&gt;
&lt;p&gt;Ready? Let's dive in.&lt;/p&gt;
&lt;h3 id="811d"&gt;The Real Use Case&lt;/h3&gt;
&lt;p&gt;I'll use a real project I vibe-coded for my niece to show you how this works. (This article focuses on design, not the vibe coding whole app process itself.)&lt;/p&gt;
&lt;p&gt;Alright, so how are we going to use AI? Vibe coding right away? &lt;strong&gt;Nope.&lt;/strong&gt; Actually, vibe coding comes next, after you have your app's mockup and system design ready.&lt;/p&gt;
&lt;p&gt;The secret? &lt;strong&gt;Use no-code platforms.&lt;/strong&gt; And fortunately, there are a LOT of them, which gives us tons of different designs and options to choose from.&lt;/p&gt;
&lt;p&gt;Here are my favorites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Lovable&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blink&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bolt&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I've vibe-coded a lot of Angular apps, and I can say that out of these platforms, Lovable was often outstanding. There are plenty of other choices you can add to the list, but these are my go-to platforms.&lt;/p&gt;
&lt;p&gt;Now, knowing what AI tools can help us with app design is not enough. We also need to know &lt;strong&gt;how&lt;/strong&gt; to use them to get the best results!&lt;/p&gt;
&lt;h3 id="9a9b"&gt;Step 1: Be Smart at Dealing with Your AI App Builder&lt;/h3&gt;
&lt;h4 id="fd8f"&gt;Build a Strong Prompt for the MVP Version of Your App&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Overly complex prompts overwhelm the agent's context window, leading to incomplete implementations, hallucinations, or ignored features as the model struggles to prioritize.&lt;/p&gt;
&lt;p&gt;We don't want that. We want our coding agent to be in good health so it can perform at its best. And speaking of good health 😂, we also need to ask it to do what it excels at — meaning let it &lt;strong&gt;choose the stack&lt;/strong&gt; to use. Don't specify any stack yourself. Using a technology stack unfamiliar to a coding agent increases the chance of generating flawed, non-functional code. Although our goal here is design, not code quality, getting stuck solving error problems with follow-up prompts will waste time, effort, and cause unnecessary struggle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can list all your &lt;strong&gt;MVP&lt;/strong&gt; features with short descriptions to an LLM (ChatGPT, Gemini, or Claude) and ask it to polish and clarify them, since this will be used as a prompt for a no-code platform to build your app.&lt;/p&gt;
&lt;h4 id="08fb"&gt;My Real Use Case Prompt&lt;/h4&gt;
&lt;p&gt;For my project, here's the prompt I used:&lt;/p&gt;
&lt;pre&gt;Copy&lt;code&gt;Kids&lt;span class="hljs-comment"&gt;' Language Pronunciation Learning App&lt;/span&gt;

Features Implemented
&lt;span class="hljs-number"&gt;1&lt;/span&gt;. Image-&lt;span class="hljs-keyword"&gt;to&lt;/span&gt;-&lt;span class="hljs-keyword"&gt;Text&lt;/span&gt; Flow
Home Page: Users can upload an image (mocked) &lt;span class="hljs-built_in"&gt;or&lt;/span&gt; use sample &lt;span class="hljs-keyword"&gt;text&lt;/span&gt;.
OCR Service: Mocked service simulates &lt;span class="hljs-keyword"&gt;text&lt;/span&gt; extraction.
&lt;span class="hljs-keyword"&gt;Text&lt;/span&gt; Editor: Users can edit the extracted &lt;span class="hljs-keyword"&gt;text&lt;/span&gt; before starting.

&lt;span class="hljs-number"&gt;2&lt;/span&gt;. Learning Modes
Listen Mode: TTS reads the &lt;span class="hljs-keyword"&gt;text&lt;/span&gt; aloud. Words are highlighted &lt;span class="hljs-keyword"&gt;as&lt;/span&gt; they are spoken (simulated). Speed controls available.
Practice Mode: Users can record their voice (mocked). STT simulates recognition.

&lt;span class="hljs-number"&gt;3&lt;/span&gt;. Interactive Words
Word Modal: Clicking a word opens a modal &lt;span class="hljs-keyword"&gt;with&lt;/span&gt; definition &lt;span class="hljs-built_in"&gt;and&lt;/span&gt; syllable breakdown (mocked).
&lt;span class="hljs-symbol"&gt;Pronunciation:&lt;/span&gt; Users can listen &lt;span class="hljs-keyword"&gt;to&lt;/span&gt; individual words.
Save &lt;span class="hljs-keyword"&gt;to&lt;/span&gt; Difficult Words: Users can save words &lt;span class="hljs-keyword"&gt;to&lt;/span&gt; a list.

&lt;span class="hljs-number"&gt;4&lt;/span&gt;. Progress &amp;amp; Dashboard
Difficult Words Page: Displays saved words. Users can listen &lt;span class="hljs-keyword"&gt;to&lt;/span&gt; &lt;span class="hljs-built_in"&gt;or&lt;/span&gt; remove words.

&lt;span class="hljs-symbol"&gt;Navigation:&lt;/span&gt; Easy navigation between Home, Learning, &lt;span class="hljs-built_in"&gt;and&lt;/span&gt; Difficult Words pages.
Use mock data at the bigining&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;💡 &lt;/strong&gt;Key Powers of This Approach&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mock-First Approach:&lt;/strong&gt; Notice the duplicated usage of "mock" and "simulate" words. Mandating "mock data at the beginning" enables rapid iteration — my app ships with simulated OCR/TTS/STT/recording, yielding a working UI/flow instantly. You get extractable design assets right away, bypassing real API hurdles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modularity:&lt;/strong&gt; The bullet-point format mirrors agent strengths, creating navigable pages (Home, Learning, Difficult Words) and interactions (word modals, speed controls) as discrete, testable units.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anti-Bloat Guardrails:&lt;/strong&gt; Keeping things ultra-concise with "no expansions" eliminates feature creep, focusing only on high-value paths like text editing → highlighting → recording → saving.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The use of &lt;strong&gt;"mock data"&lt;/strong&gt; is really a key here. It tells the agent: "Don't worry about getting feature logic working at the beginning — just focus on the UI."&lt;/p&gt;
&lt;h3 id="2aac"&gt;Step 2: Compare, Improve, Adjust, and Take Screenshots&lt;/h3&gt;
&lt;p&gt;Now, at this stage, you have 3 choices (or more, depending on how many no-code platforms you used). You can compare them — how do you feel about each one? You can even publish these and share them with test users who can give you quick feedback on their user experience.&lt;/p&gt;
&lt;p&gt;When you've found the one you like (for me, it's Lovable), you can fine-tune it if needed with a few prompts or add more features. &lt;strong&gt;Remember:&lt;/strong&gt; we don't want to bombard the agent at the beginning. Now that we have a solid base, we can add features one by one (always in mock mode).&lt;/p&gt;
&lt;p&gt;When you're done, take screenshots, save them in a mockup folder, and download the app's &lt;a rel="noopener noreferrer" title="" href="https://lovable.dev/projects/b9a50763-fafd-49ac-9452-4ad7e4f71d75?view=codeEditor"&gt;CSS style file.&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="10df"&gt;💡 Reverse-Engineering the Design&lt;/h4&gt;
&lt;p&gt;We'll use the CSS style file to reverse-engineer the design from it.&lt;/p&gt;
&lt;p&gt;By the way, you could use it as-is if it matches the UI library you'll use. However, for my case, it doesn't reflect my requirements. Even if I use only Tailwind CSS without DaisyUI, the current no-code platforms are trained heavily on Tailwind CSS v3. But I want to use the latest version, v4, which has a completely different setup compared to v3:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;ℹ️ &lt;strong&gt;Tailwind CSS v4&lt;/strong&gt; shifts to a CSS-first configuration with faster Oxide engine builds, unlike v3's JavaScript config and PostCSS approach.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h4 id="ef90"&gt;What I Added in My Use Case:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;i18n support&lt;/strong&gt; with a new settings tab&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The possibility of taking a picture&lt;/strong&gt; in the upload image feature&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In the learning phase,&lt;/strong&gt; I added a seek bar below the speed slider so users can go backward or forward when listening to the speaker&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time feedback:&lt;/strong&gt; Instead of displaying "what I heard," I want the mispoken word highlighted with a wavy underline so users know which words need to be pronounced properly&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="5212"&gt;Results:&lt;/h4&gt;
&lt;h4 id="24ef"&gt;Links to other designs:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel="noopener ugc nofollow noreferrer" title="" href="https://kids-language-pronun-w6pb.bolt.host/"&gt;Bolt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel="noopener ugc nofollow noreferrer" title="" href="https://kids-language-pronunciation-app-grhdcbym.sites.blink.new/"&gt;Blink&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="c2ad"&gt;Step 4: Generate Your App's Design System Requirements Prompt&lt;/h3&gt;
&lt;p&gt;With the style file and mockups, you have the perfect ingredients to generate a design system for your app. This design system can be used as a relevant context when vibe coding.&lt;/p&gt;
&lt;p&gt;Again, we can rely on AI (I used Gemini Pro 3) to generate a solid prompt that will create this design system requirement and take into consideration the UI library you want to use.&lt;/p&gt;
&lt;h4 id="4f19"&gt;The Workflow&lt;/h4&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;a rel="noopener follow" href="https://github.com/famzila/word-wonder/blob/main/.agent/workflows/design-system-specs-generator.md"&amp;gt;


            &amp;lt;h2&amp;gt;word-wonder/.agent/workflows/design-system-specs-generator.md at main · famzila/word-wonder&amp;lt;/h2&amp;gt;

                &amp;lt;h3&amp;gt;An interactive, AI-powered language pronunciation learning tool designed specifically for children aged 6-10. It…&amp;lt;/h3&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            &amp;lt;div class="mt-5"&amp;gt;
                &amp;lt;p class="text-xs text-grey-darker"&amp;gt;github.com&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class="relative flex h-40 flew-row w-60"&amp;gt;
            &amp;lt;div class="absolute inset-0 bg-center bg-cover" style="background-image: url('https://miro.medium.com/v2/resize:fit:320/0*_otijISa8GEApMtT'); background-repeat: no-repeat;" referrerpolicy="no-referrer"&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&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%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F1%2AqxYvgGnbPVUQu8BXOjzOIA.png" class="article-body-image-wrapper"&gt;&lt;img alt="None" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A700%2F1%2AqxYvgGnbPVUQu8BXOjzOIA.png" width="700" height="442"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;💡 &lt;strong&gt;Assuming you want to use Angular Material:&lt;/strong&gt; You'll need to adapt the design system constraints. Use something like "The design system must conform to Material Design (M3) principles and terminology." But the most important thing here is &lt;strong&gt;CONTEXT.&lt;/strong&gt; To avoid surprises or outdated code, you need context — meaning a &lt;code&gt;material-llms.txt&lt;/code&gt; file where you gather how the latest Material version (or the version you want to use) works, its best practices, dos and don'ts. &lt;strong&gt;This is super important.&lt;/strong&gt;&lt;/p&gt;

&lt;h4 id="ce2d"&gt;Feeding It to the AI&lt;/h4&gt;

&lt;p&gt;The result was fed to &lt;strong&gt;Gemini 3 Pro &lt;/strong&gt;in Antigravity (it has a 1 million token context window).&lt;/p&gt;

&lt;h4 id="b335"&gt;The Output&lt;/h4&gt;

&lt;p&gt;And…the output is a &lt;strong&gt;very strong design system requirements doc&lt;/strong&gt; that translates visual intent into concrete, reusable, and enforceable primitives that developers can actually build against.&lt;/p&gt;

&lt;h4 id="164c"&gt;Key Powers as a Developer Resource&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;— Strong Tokenization&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Colors, radius, shadows, gradients, and motion are all defined as reusable tokens&lt;/li&gt;
&lt;li&gt;Enables scalability, theming, and framework/UI library-agnostic implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;— Clear Semantic Intent&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tokens and utilities are tied to usage, not just appearance&lt;/li&gt;
&lt;li&gt;Reduces ambiguity and prevents inconsistent UI decisions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;— Behavior Included (Not Just Styles)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Animations and shadows are documented as interaction primitives&lt;/li&gt;
&lt;li&gt;Motion has meaning, improving UX consistency and accessibility readiness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Utility-First Friendly&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maps cleanly to Tailwind/atomic CSS workflows&lt;/li&gt;
&lt;li&gt;Developers can implement without a design tool dependency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Implicit Constraints&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strong visual rules (rounding, colored shadows) guide consistency&lt;/li&gt;
&lt;li&gt;Though explicit "do/don't" constraints could strengthen it further&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Overall:&lt;/strong&gt; This document functions as a solid design-system specification extracted from code. It's implementation-ready, scalable, and minimizes developer guesswork — well above average for CSS-derived requirements.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;⚠️ &lt;strong&gt;Important note:&lt;/strong&gt; My prompt was guided toward a result that can help my coding agent implement the app's design properly based on specific stacks like Tailwind CSS, DaisyUI, etc. You can adjust it to fit your needs.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Here's the design system requirements doc:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;a rel="noopener follow" href="https://github.com/famzila/word-wonder/blob/main/public/specs/DESIGN_SYSTEM.md"&amp;gt;


            &amp;lt;h2&amp;gt;word-wonder/public/specs/DESIGN_SYSTEM.md at main · famzila/word-wonder&amp;lt;/h2&amp;gt;

                &amp;lt;h3&amp;gt;An interactive, AI-powered language pronunciation learning tool designed specifically for children aged 6-10. It…&amp;lt;/h3&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            &amp;lt;div class="mt-5"&amp;gt;
                &amp;lt;p class="text-xs text-grey-darker"&amp;gt;github.com&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class="relative flex h-40 flew-row w-60"&amp;gt;
            &amp;lt;div class="absolute inset-0 bg-center bg-cover" style="background-image: url('https://miro.medium.com/v2/resize:fit:320/0*EzOSqnt47jF0cyJW'); background-repeat: no-repeat;" referrerpolicy="no-referrer"&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;h3 id="13ce"&gt;Step 5: How You Can Use This Design System Requirements File&lt;/h3&gt;

&lt;p&gt;Now that we have the design system requirements document, we can move to implementation planning. I created another prompt specifically for this task (adjust it based on the stack you want to use).&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;a rel="noopener follow" href="https://github.com/famzila/word-wonder/blob/main/.agent/workflows/frontend-system-architect.md"&amp;gt;


            &amp;lt;h2&amp;gt;word-wonder/.agent/workflows/frontend-system-architect.md at main · famzila/word-wonder&amp;lt;/h2&amp;gt;

                &amp;lt;h3&amp;gt;An interactive, AI-powered language pronunciation learning tool designed specifically for children aged 6-10. It…&amp;lt;/h3&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            &amp;lt;div class="mt-5"&amp;gt;
                &amp;lt;p class="text-xs text-grey-darker"&amp;gt;github.com&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class="relative flex h-40 flew-row w-60"&amp;gt;
            &amp;lt;div class="absolute inset-0 bg-center bg-cover" style="background-image: url('https://miro.medium.com/v2/resize:fit:320/0*aCdKHMADe2GC4plp'); background-repeat: no-repeat;" referrerpolicy="no-referrer"&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;As you can see in this prompt, I need the following inputs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The system design requirements doc&lt;/li&gt;
&lt;li&gt;The mockups (screenshots)&lt;/li&gt;
&lt;li&gt;The UI library's llms.txt (for my case, DaisyUI — from the official DaisyUI docs, which also covers Tailwind CSS specifications, so no need to add them separately)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;&lt;p&gt;⚠️ &lt;strong&gt;Antigravity supports 5 images.&lt;/strong&gt; This might seem like a limitation, but actually, providing more mockups will only result in poor results. Remember: asking for too much always causes the LLM to not perform at its best.&lt;/p&gt;&lt;/blockquote&gt;

&lt;h4 id="3ecc"&gt;The Output&lt;/h4&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;a rel="noopener follow" href="https://github.com/famzila/word-wonder/blob/main/public/specs/FRONTEND_PLANNING.md"&amp;gt;


            &amp;lt;h2&amp;gt;word-wonder/public/specs/FRONTEND_PLANNING.md at main · famzila/word-wonder&amp;lt;/h2&amp;gt;

                &amp;lt;h3&amp;gt;An interactive, AI-powered language pronunciation learning tool designed specifically for children aged 6-10. It…&amp;lt;/h3&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            &amp;lt;div class="mt-5"&amp;gt;
                &amp;lt;p class="text-xs text-grey-darker"&amp;gt;github.com&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class="relative flex h-40 flew-row w-60"&amp;gt;
            &amp;lt;div class="absolute inset-0 bg-center bg-cover" style="background-image: url('https://miro.medium.com/v2/resize:fit:320/0*rOUbngEMSuPjxd7q'); background-repeat: no-repeat;" referrerpolicy="no-referrer"&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;strong&gt;— Perfect Translation Layer:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maps every design token (&lt;code&gt;color.coral&lt;/code&gt;) directly to implementation&lt;/li&gt;
&lt;li&gt;Zero ambiguity — coding agent knows exactly which daisyUI class to use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;— Component Blueprint:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each component has clear inputs/outputs/responsibilities.&lt;/li&gt;
&lt;li&gt;Smart vs Dumb separation prevents spaghetti code&lt;/li&gt;
&lt;li&gt;Styling strategy defined per component type (buttons use &lt;code&gt;btn btn-primary shadow-button rounded-box&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;— Implementation-Ready:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exact CSS variable names for Tailwind v4 + daisyUI v5&lt;/li&gt;
&lt;li&gt;Animation keyframe names specified&lt;/li&gt;
&lt;li&gt;Responsive patterns defined&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;— Risk Mitigation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rules section prevents common mistakes ("Never use &lt;code&gt;bg-[#...]&lt;/code&gt;")&lt;/li&gt;
&lt;li&gt;Accessibility requirements explicit&lt;/li&gt;
&lt;li&gt;Framework migration gotchas documented&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;— Efficiency Boost:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No back-and-forth asking "what should this look like?"&lt;/li&gt;
&lt;li&gt;No redesigning components mid-development&lt;/li&gt;
&lt;li&gt;Vibe coding agent has complete context to generate correct code first try&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This doc transforms mockups into buildable specifications. It's the Rosetta Stone between design and code — your vibe coding agent can reference this and generate components that match your design system perfectly without hallucinating styles or guessing structure. And of course, you can always adapt it after review, &lt;strong&gt;human in the loop is important&lt;/strong&gt;. For example, some of the dumb components are not necessary from my point of view.&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;💡 Pro Tip: Convert Prompts to Reusable Workflows&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;

&lt;blockquote&gt;&lt;p&gt;Convert all the prompts we've used so far into Antigravity workflows, Claude SKILLs, or slash commands to avoid repeating yourself. With this, you'll only need to call the prompt with "/" instead of copy-pasting it every time you need it.&lt;/p&gt;&lt;/blockquote&gt;

&lt;h3 id="006d"&gt;Step 6: Execute the Frontend Plan&lt;/h3&gt;

&lt;p&gt;Now that you have the Frontend Plan, don't just let it sit there. It is your roadmap for the actual coding phase. Here is how to use it effectively:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate Global Styles &amp;amp; Tokens: Use the plan to generate your global CSS variables and theme immediately. Get the colors, typography, and spacing utilities defined before writing a single line of feature logic.&lt;/li&gt;
&lt;li&gt;Scaffold "Dumb" Components First: Before building complex features, ask the coding agent to "prepare the ground". Have it implement your shared, presentational components.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;&lt;p&gt;ℹ️ Why? This solves a common AI problem where agents duplicate code (eg. tab menu, stepper, …) for every new feature. By pre-building these "dumb" components, you force the agent to reuse them, keeping your codebase clean and consistent.&lt;/p&gt;&lt;/blockquote&gt;

&lt;ul&gt;&lt;li&gt;Use it as a Context Guide: Whenever you start a new feature (e.g., "Build the Learn"), paste the relevant section of the Frontend Plan into the chat context. This ensures the agent adheres to the architectural decisions you've already made, rather than guessing.&lt;/li&gt;&lt;/ul&gt;

&lt;blockquote&gt;&lt;p&gt;⚠️ It's important to note that when implementing features in Angular, always feed your agent the mockup along with the frontend planning doc. Generative AI is non-deterministic, so don't freak out if the first output isn't perfect. You'll probably need 2–3 follow-up prompts to fine-tune it and get the UI to match your mockup. That's normal — iteration is the name of the game!&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Ah, and by the way, you can also let your coding agent suggest design alternatives and test them out. If you don't like it, just revert. That was my case with the favorite words modal. I actually liked Gemini 3 Pro's suggestion better than my original lovable mockup!&lt;/p&gt;

&lt;h3 id="c039"&gt;Final Touch — Audit &amp;amp; Review&lt;/h3&gt;

&lt;p&gt;Even with a perfect plan, AI is not perfect. Models often have a "knowledge cutoff" or strong training bias toward older versions of libraries, leading them to generate outdated or deprecated code (e.g., using custon css class when the latest docs suggest a different utility).&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;💡 Pro Tip: The "Second Opinion" Strategy&lt;/strong&gt;&lt;br&gt;
Always perform your audit using a &lt;strong&gt;different LLM&lt;/strong&gt; than the one that generated the code.&lt;/p&gt;&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why?&lt;/strong&gt; If the first model (e.g., Claude 3.5 Sonnet) made a mistake, it is biased to defend it. A fresh model (e.g., Gemini 1.5 Pro or other) will spot the error immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be Specific:&lt;/strong&gt; Don't just dump the whole documentation. Provide the llms.txt file and &lt;strong&gt;targeted links&lt;/strong&gt; to the specific components involved.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Example:&lt;/em&gt; If the agent built a Card component, include the direct URL to the DaisyUI Card API documentation in your prompt. This forces the auditor to cross-reference the code against the exact official source.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is my implemented UI with Antigravity, Gemini 3 PRO, and Claude:&lt;/p&gt;

&lt;p&gt;It's almost identical to the Lovable UI, isn't it? 😎&lt;/p&gt;

&lt;h4 id="455e"&gt;Here is a demo&lt;/h4&gt;



&lt;p&gt;I have already shared my experiment to build this app in this article. Check it out here if you are interested ;)&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;a rel="noopener follow" href="https://levelup.gitconnected.com/when-antigravity-meets-production-ready-modern-angular-app-42b0c7d94388"&amp;gt;


            &amp;lt;h2&amp;gt;When Antigravity Meets Production-Ready Modern Angular App&amp;lt;/h2&amp;gt;

                &amp;lt;h3&amp;gt;Embracing Antigravity workflows and the Playground&amp;lt;/h3&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            &amp;lt;div class="mt-5"&amp;gt;
                &amp;lt;p class="text-xs text-grey-darker"&amp;gt;gitconnected.com&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class="relative flex h-40 flew-row w-60"&amp;gt;
            &amp;lt;div class="absolute inset-0 bg-center bg-cover" style="background-image: url('https://miro.medium.com/v2/resize:fit:320/1*QnjWtCkcn210uTMM2oHS3A.png'); background-repeat: no-repeat;" referrerpolicy="no-referrer"&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;h3 id="52d0"&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;And there you have it! A complete workflow to design stunning apps without being a designer yourself. The key takeaways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use AI-powered no-code platforms&lt;/strong&gt; to generate multiple design options quickly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start with MVP and mock data&lt;/strong&gt; to keep your agent focused&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compare and iterate&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extract and reverse-engineer&lt;/strong&gt; the design into a proper system requirements doc&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use that doc as context&lt;/strong&gt; for your actual implementation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now go build something beautiful! 🚀&lt;/p&gt;

&lt;p&gt;That's it for today 👋&lt;br&gt;
If you enjoyed this article, consider liking or &lt;strong&gt;&lt;a rel="noopener noreferrer" title="" href="https://famzil.medium.com/subscribe"&gt;subscribing&lt;/a&gt;&lt;/strong&gt; so you don't miss the next one 😉&lt;/p&gt;

&lt;p&gt;Let's stay connected! You can find me on &lt;strong&gt;&lt;a rel="noopener ugc nofollow noreferrer" title="" href="https://www.linkedin.com/in/fatima-amzil-9031ba95/"&gt;LinkedIn&lt;/a&gt;&lt;/strong&gt;&lt;strong&gt;, &lt;/strong&gt;&lt;strong&gt;&lt;a rel="noopener ugc nofollow noreferrer" title="" href="https://www.instagram.com/_web_stories"&gt;Instagram&lt;/a&gt;&lt;/strong&gt;&lt;strong&gt;, &lt;/strong&gt;&lt;strong&gt;&lt;a rel="noopener ugc nofollow noreferrer" title="" href="https://www.youtube.com/channel/UCaxr-f9r6P1u7Y7SKFHi12g"&gt;YouTube&lt;/a&gt;&lt;/strong&gt;,&lt;strong&gt; or &lt;/strong&gt;&lt;strong&gt;&lt;a rel="noopener ugc nofollow noreferrer" title="" href="http://www.twitter.com/fati_amzil"&gt;X&lt;/a&gt;&lt;/strong&gt;&lt;strong&gt;.&lt;/strong&gt;&lt;br&gt;
And hey, if you feel like sending a &lt;strong&gt;&lt;a rel="noopener ugc nofollow noreferrer" title="" href="https://www.buymeacoffee.com/fatimaamzil"&gt;virtual coffee&lt;/a&gt;&lt;/strong&gt; my way ☕️ 😊&lt;/p&gt;

&lt;p&gt;Thank you ❤️&lt;/p&gt;



&lt;p&gt;Author: &lt;a href="https://medium.com/@famzil" rel="noopener noreferrer"&gt;FAM&lt;/a&gt;&lt;/p&gt;



</description>
      <category>webdev</category>
      <category>ai</category>
      <category>design</category>
      <category>ui</category>
    </item>
  </channel>
</rss>
