<?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: 𝗝𝗼𝗵𝗻</title>
    <description>The latest articles on DEV Community by 𝗝𝗼𝗵𝗻 (@johnnylemonny).</description>
    <link>https://dev.to/johnnylemonny</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3868757%2F52777a3a-3f32-4bcd-8f3c-002c1983dcac.jpg</url>
      <title>DEV Community: 𝗝𝗼𝗵𝗻</title>
      <link>https://dev.to/johnnylemonny</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/johnnylemonny"/>
    <language>en</language>
    <item>
      <title>Stop Over-Optimizing Performance: The Modern Full-Stack Toolkit in 2026</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Wed, 01 Jul 2026 12:35:00 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/stop-over-optimizing-performance-the-modern-full-stack-toolkit-in-2026-2i97</link>
      <guid>https://dev.to/johnnylemonny/stop-over-optimizing-performance-the-modern-full-stack-toolkit-in-2026-2i97</guid>
      <description>&lt;p&gt;Let’s face it: if your current frontend optimization strategy still involves manually auditing codebases for missing &lt;code&gt;useMemo&lt;/code&gt; hooks, micro-managing dependency arrays, or aggressively fighting layout shifts with complex client-side state management, you are wasting your engineering leverage.&lt;/p&gt;

&lt;p&gt;As we cross the midpoint of 2026, web framework architecture has quietly undergone a massive shift. We have firmly moved out of the era of manual performance tweaking and entered the era of &lt;strong&gt;automated, compile-time optimization&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal of modern development is no longer just shipping fewer kilobytes to human users—it's also about optimizing data chunk delivery for AI web crawlers that evaluate your site in real-time. &lt;/p&gt;

&lt;p&gt;Here is how the modern full-stack ecosystem redefined performance this year, and what you should focus on instead.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Death of Manual Memoization (Thanks, React Compiler)
&lt;/h2&gt;

&lt;p&gt;For years, React developers bore the cognitive load of rendering performance. One misplaced reference and your entire component tree re-rendered down to the root.&lt;/p&gt;

&lt;p&gt;With the absolute maturity and default adoption of the &lt;strong&gt;React Compiler&lt;/strong&gt; across production frameworks, that paradigm is officially legacy code. The compiler handles component memoization automatically at the build step by analyzing javascript structures directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ THE OLD WAY (Pre-2026 Manual Overhead)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ExpensiveComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;computeHeavyMetrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleAction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DataGrid&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{processedData}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;onAction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{handleAction}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;//   THE MODERN WAY (Zero Performance Boilerplate)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ModernComponent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computeHeavyMetrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleAction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DataGrid&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{processedData}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;onAction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{handleAction}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Because the compiler injects optimization markers directly into the output code, human engineers can stop arguing about code architecture aesthetics and write clean, plain, idiomatically correct JavaScript.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Streaming SSR is the New Baseline for AEO
&lt;/h2&gt;

&lt;p&gt;Server-Side Rendering (SSR) used to be an all-or-nothing game. The server fetched all data, rendered the full HTML string, sent it to the browser, and then hydrated the entire view.&lt;/p&gt;

&lt;p&gt;Today, production setups rely heavily on &lt;strong&gt;Streaming SSR&lt;/strong&gt; mixed with hybrid data fetching architectures (like Next.js Server Components and SvelteKit Runes). Instead of blocking the page load for slow API responses, pages are delivered in independent streaming chunks.&lt;/p&gt;

&lt;p&gt;This has become vital not just for your Core Web Vitals, but for &lt;strong&gt;AEO (Answer Engine Optimization)&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔍 &lt;strong&gt;Why this matters right now:&lt;/strong&gt; Modern search indexes and AI aggregators scrape live data using real-time LLM agents. If your main page content is trapped behind a client-side loading spinner, an AI crawler won't wait for the hydration cycle. It reads the initial streamed server chunk and leaves. High performance means instant discoverability.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. Radical Architectural Minimalism
&lt;/h2&gt;

&lt;p&gt;Look at the trending boilerplate setups making waves in production this season. The visual noise is disappearing, and that translates directly to better performance strategy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fewer DOM Nodes:&lt;/strong&gt; Complex layout hacks are being replaced by native CSS features (like Container Queries and dynamic anchor positioning), cutting out deep wrappers that bloat the virtual DOM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build-Time Shift:&lt;/strong&gt; Frameworks like SvelteKit and Astro have proved that running heavy logic during compilation rather than execution yields smaller bundle sizes, ensuring great UX even on low-powered mobile devices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless Edge Runtimes:&lt;/strong&gt; Computing is moving away from centralized data centers closer to the end user via Edge functions, keeping time-to-first-byte (TTFB) low globally.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Where You Should Actually Spend Your Time
&lt;/h2&gt;

&lt;p&gt;If the toolchains and compilers handle runtime rendering optimizations, where do you look for engineering impact?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Strict Type Contracts:&lt;/strong&gt; Spend time establishing rock-solid TypeScript definitions between your client and backend APIs. Predictable data types prevent downstream hydration mismatches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Robust Test Coverage:&lt;/strong&gt; Write end-to-end tests that validate data persistence and edge-case rendering boundaries. If the compiler optimizes your code, your tests must ensure the business logic remains intact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Hydration Architecture:&lt;/strong&gt; Focus on optimizing &lt;em&gt;when&lt;/em&gt; and &lt;em&gt;where&lt;/em&gt; data is fetched. Organize your databases, design efficient indexes, and ensure your APIs return predictable payloads.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Let's connect and discuss!
&lt;/h3&gt;

&lt;p&gt;What performance optimizations have you safely delegated to your compiler this year? Are you embracing the shift to server-driven architecture or keeping your logic client-side? Let me know in the comments!&lt;/p&gt;




&lt;h3&gt;
  
  
  Connect with Me
&lt;/h3&gt;

&lt;p&gt;If you found this guide helpful, let's connect and discuss modern development workflows!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💻 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/johnnylemonny" rel="noopener noreferrer"&gt;johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;✍️ &lt;strong&gt;DEV.to:&lt;/strong&gt; &lt;a href="https://dev.to/johnnylemonny"&gt;johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This article was created with the help of AI&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Stop Paying for GitHub Copilot: Build a Free, 100% Private AI Assistant Locally</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Tue, 23 Jun 2026 12:15:00 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/stop-paying-for-github-copilot-build-a-free-100-private-ai-assistant-locally-5dpd</link>
      <guid>https://dev.to/johnnylemonny/stop-paying-for-github-copilot-build-a-free-100-private-ai-assistant-locally-5dpd</guid>
      <description>&lt;p&gt;The landscape of AI coding assistants has completely shifted. While cloud-based subscriptions like GitHub Copilot or Claude Pro are excellent, they come with two major pain points: &lt;strong&gt;recurring monthly costs&lt;/strong&gt; and &lt;strong&gt;privacy concerns&lt;/strong&gt; regarding your proprietary code.&lt;/p&gt;

&lt;p&gt;Thanks to the incredible advancements in efficiency, you can now run state-of-the-art coding LLMs &lt;em&gt;directly on your local machine&lt;/em&gt; with zero latency, absolute privacy, and absolutely no cost. &lt;/p&gt;

&lt;p&gt;In this guide, we will set up a blazing-fast, context-aware local AI coding assistant using &lt;strong&gt;Ollama&lt;/strong&gt; and &lt;strong&gt;Continue.dev&lt;/strong&gt; in VS Code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Go Local?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;💰 &lt;strong&gt;Cost-Efficient:&lt;/strong&gt; $0/month forever.&lt;/li&gt;
&lt;li&gt;🔒 &lt;strong&gt;100% Private:&lt;/strong&gt; Your code never leaves your local machine. Perfect for NDA-protected or enterprise projects.&lt;/li&gt;
&lt;li&gt;✈️ &lt;strong&gt;Offline Capability:&lt;/strong&gt; Code with full AI assistance on a plane, a train, or anywhere without internet.&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Customization:&lt;/strong&gt; Swap models instantly depending on whether you need quick autocomplete or deep architectural reasoning.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To get a smooth experience, you ideally need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A modern machine (Apple Silicon M-series chips or a Windows/Linux PC with a dedicated Nvidia RTX GPU).&lt;/li&gt;
&lt;li&gt;16GB of RAM/VRAM minimum (8GB can work with highly compressed models, but 16GB+ is the sweet spot).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Install Ollama and the Coding Model
&lt;/h2&gt;

&lt;p&gt;Ollama is the easiest way to manage and run LLMs locally.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download and install &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; for your OS.&lt;/li&gt;
&lt;li&gt;Open your terminal and run the following command to download &lt;strong&gt;Qwen2.5-Coder (7B)&lt;/strong&gt; or &lt;strong&gt;DeepSeek-Coder&lt;/strong&gt;. For most modern setups, the 7B (7-billion parameter) model offers the ultimate balance between speed and ChatGPT-4 level coding intelligence.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run qwen2.5-coder:7b

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

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: If your machine has lower specs, try &lt;code&gt;ollama run qwen2.5-coder:1.5b&lt;/code&gt; for lightning-fast autocompletion.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once the download is complete, you can test it in the terminal, then minimize it. Ollama will run quietly in the background as a local API.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Set Up Continue.dev in Your IDE
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.continue.dev/" rel="noopener noreferrer"&gt;Continue&lt;/a&gt; is an open-source AI code assistant extension that seamlessly replaces the Copilot UI in VS Code or JetBrains IDEs.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;VS Code&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Go to the Extensions marketplace (&lt;code&gt;Ctrl+Shift+X&lt;/code&gt; or &lt;code&gt;Cmd+Shift+X&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Search for &lt;strong&gt;Continue&lt;/strong&gt; and click &lt;strong&gt;Install&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 3: Configure Continue to Use Your Local Model
&lt;/h2&gt;

&lt;p&gt;Once installed, a new icon will appear in your sidebar. Click it, then click the gear icon (⚙️) at the bottom right of the Continue panel to open your &lt;code&gt;config.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Replace the contents of the file with the following configuration to link it to your local Ollama instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"models"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Qwen2.5-Coder 7B"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ollama"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"qwen2.5-coder:7b"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tabAutocompleteModel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Qwen2.5-Coder 1.5b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ollama"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"qwen2.5-coder:1.5b"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"customCommands"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{{ input }}}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;Write a comprehensive unit test suite for the code above using Jest/Vitest."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write unit tests"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contextProviders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"codebase"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"params"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"openFiles"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"params"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  What this configuration does:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chat Model:&lt;/strong&gt; Uses the robust &lt;code&gt;7b&lt;/code&gt; model for complex tasks, refactoring, and general chat conversations in the sidebar (&lt;code&gt;Cmd+L&lt;/code&gt; or &lt;code&gt;Ctrl+L&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tab Autocomplete:&lt;/strong&gt; Uses the ultra-lightweight &lt;code&gt;1.5b&lt;/code&gt; model to instantly suggest inline code as you type, ensuring zero lag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context Awareness:&lt;/strong&gt; Allows you to type &lt;code&gt;@codebase&lt;/code&gt; in the chat to let the local AI read your entire project repository securely.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How to Maximize Your Local AI Workflow
&lt;/h2&gt;

&lt;p&gt;Now that you are fully set up, here are three essential shortcuts to replace your paid workflow:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Inline Edit (&lt;code&gt;Cmd+I&lt;/code&gt; / &lt;code&gt;Ctrl+I&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Highlight a block of code, press &lt;code&gt;Cmd+I&lt;/code&gt;, and ask the local model to modify it directly. For example: &lt;em&gt;"Refactor this fetch request to use async/await and add error handling."&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Full Project Context (&lt;code&gt;@codebase&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;If you are debugging a tricky bug that spans multiple files, open the chat and type:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;@codebase why is my authentication state resetting on page refresh?&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The extension will index your local files locally, feed relevant snippets to Ollama, and give you an exact answer without a single byte uploaded to the cloud.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Automatic Doc Generation
&lt;/h3&gt;

&lt;p&gt;Highlight a function, hit your chat shortcut, and ask: &lt;em&gt;"Add JSDoc comments to this function explaining the parameters."&lt;/em&gt;&lt;/p&gt;




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

&lt;p&gt;The era of paying $10-$20 a month per developer for basic AI autocompletion is coming to an end. By leveraging open-weights models and local orchestration tools, you gain total control over your development environment, secure your data, and save money.&lt;/p&gt;

&lt;p&gt;Give it a spin and see how it handles your toughest codebases!&lt;/p&gt;




&lt;h3&gt;
  
  
  Connect with Me
&lt;/h3&gt;

&lt;p&gt;If you found this guide helpful, let's connect and discuss modern development workflows!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💻 &lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/johnnylemonny" rel="noopener noreferrer"&gt;johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;✍️ &lt;strong&gt;DEV.to:&lt;/strong&gt; &lt;a href="https://dev.to/johnnylemonny"&gt;johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>tooling</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Beyond Code Autocomplete: How to Build a Modern Agentic Workflow in 2026</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Tue, 16 Jun 2026 13:07:00 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/beyond-code-autocomplete-how-to-build-a-modern-agentic-workflow-in-2026-566f</link>
      <guid>https://dev.to/johnnylemonny/beyond-code-autocomplete-how-to-build-a-modern-agentic-workflow-in-2026-566f</guid>
      <description>&lt;p&gt;Let’s be honest: the era of copy-pasting code snippets from a chat UI or blindly accepting basic ghost-text autocomplete is officially over. &lt;/p&gt;

&lt;p&gt;As we move through 2026, the developer landscape has fundamentally shifted. We’ve leapfrogged from AI &lt;em&gt;augmentation&lt;/em&gt; to AI &lt;em&gt;delegation&lt;/em&gt;. With over 70% of engineers using advanced AI tools daily, the competitive advantage isn't knowing how to prompt AI to write a quick utility function. It's knowing how to orchestrate &lt;strong&gt;Autonomous AI Agents&lt;/strong&gt; to manage entire feature lifecycles.&lt;/p&gt;

&lt;p&gt;If you want to stay ahead of the curve, save your cognitive load, and prevent your codebase from turning into an AI-generated spaghetti monster, you need to upgrade your setup. &lt;/p&gt;

&lt;p&gt;Here is what a modern, high-leverage developer workflow looks like today.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Shift to Bounded Agentic Workflows
&lt;/h2&gt;

&lt;p&gt;We’ve moved past simple extensions. Modern tools like &lt;strong&gt;Claude Code&lt;/strong&gt;, &lt;strong&gt;Cursor’s Agent Mode&lt;/strong&gt;, and next-gen &lt;strong&gt;Copilot&lt;/strong&gt; don’t just sit in your sidebar waiting for a prompt. They have &lt;strong&gt;repository intelligence&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;They can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read your entire file structure and understand architectural intent.&lt;/li&gt;
&lt;li&gt;Independently formulate multi-step execution plans.&lt;/li&gt;
&lt;li&gt;Write code across multiple files, run tests in your terminal, read the error output, and self-correct before you even look at the PR.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;The 2026 Mindset:&lt;/strong&gt; You are no longer a "line mechanic" writing every block of boilerplate by hand. You are a &lt;strong&gt;System Architect&lt;/strong&gt; and a &lt;strong&gt;Code Reviewer&lt;/strong&gt;. Your job is to define constraints, review intent, and validate outputs.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. Enter MCP: The "USB-C Port" for AI Context
&lt;/h2&gt;

&lt;p&gt;One of the quietest yet biggest revolutions happening right now is the widespread adoption of the &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Originally introduced as an open standard to connect AI models to secure data sources, MCP has become the integration layer of choice. Instead of you copying and pasting logs, database schemas, or API docs into an LLM, your development environment uses MCP servers to securely stream that context directly to the agent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
┌──────────────┐          MCP          ┌─────────────────────────┐
│              │◄─────────────────────►│ Your local filesystem   │
│   AI Agent   │          MCP          ├─────────────────────────┤
│ (Cursor/IDE) │◄─────────────────────►│ Production logs (Sentry)│
│              │          MCP          ├─────────────────────────┤
│              │◄─────────────────────►│ DB Schemas / App APIs   │
└──────────────┘                       └─────────────────────────┘

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

&lt;/div&gt;



&lt;p&gt;When your AI agent can safely inspect a distributed trace in Sentry, cross-reference it with your database schema, and apply a patch directly to your repository, debugging friction drops to near zero.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Why "Boring" Codebase Qualities are Your Superpower
&lt;/h2&gt;

&lt;p&gt;There’s a common misconception that because AI can write code, humans can stop worrying about code quality. &lt;strong&gt;The exact opposite is true.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;AI agents are highly sensitive to context. If your codebase is a chaotic mess of unstructured files and implicit types, the AI’s hallucination rate skyrockets. In 2026, building "agent-friendly" codebases is a core engineering skill.&lt;/p&gt;

&lt;p&gt;To get the most out of modern AI workflows, prioritize these "boring" but strategic qualities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strict Type Safety:&lt;/strong&gt; TypeScript has become the absolute baseline. Strong types and explicit data contracts act as guardrails for AI agents, preventing them from generating breaking changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictable Architecture:&lt;/strong&gt; Stick to clean, conventional folder structures (like those enforced by modern meta-frameworks like Next.js or Nuxt).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comprehensive Test Suites:&lt;/strong&gt; If an agent can't run a test suite to verify its work, you cannot trust its autonomy. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit Documentation:&lt;/strong&gt; Write clear &lt;code&gt;README.md&lt;/code&gt; files and code comments explaining &lt;em&gt;why&lt;/em&gt; something was built a certain way, not just &lt;em&gt;how&lt;/em&gt;. AI reads your docs to understand your business logic constraints.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Let the Tooling Handle the Micro-Optimizations
&lt;/h2&gt;

&lt;p&gt;Another reason to shift your focus to high-level architecture is that modern frameworks have become incredibly smart. &lt;/p&gt;

&lt;p&gt;With the maturity of tools like the &lt;strong&gt;React Compiler&lt;/strong&gt;, manual performance optimization patterns (like aggressively littering your code with &lt;code&gt;useMemo&lt;/code&gt; and &lt;code&gt;useCallback&lt;/code&gt;) are largely things of the past. The compiler handles the optimization at build time, freeing you up to think about data flow, edge computing deployment strategies, and user experience.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Your 30-Day Action Plan to Upgrade Your Workflow
&lt;/h2&gt;

&lt;p&gt;If you want to compound your engineering leverage this month, stop doing things manually and try this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Adopt an Agent-First Tool:&lt;/strong&gt; If you're still on a legacy editor, spend a week working inside Cursor's Agent Mode or experiment with Claude Code via your CLI. Force yourself to delegate full tasks (e.g., &lt;em&gt;"Refactor this component to use Tailwind v4 and add unit tests"&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explore MCP:&lt;/strong&gt; Set up an MCP server to connect your development environment to your tool stack (like your issue tracker or database client). See how much faster bugs get resolved when the AI has direct context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit for Autonomy:&lt;/strong&gt; Look at your current repository. If an automated agent tried to implement a feature right now, where would it fail? Fix your missing types, update your outdated setup scripts, and make your codebase machine-readable.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What are your thoughts?
&lt;/h3&gt;

&lt;p&gt;Are you embracing "Vibe Coding" and agentic workflows, or are you keeping a tight manual grip on your codebase? Let's discuss in the comments below!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>productivity</category>
      <category>architecture</category>
    </item>
    <item>
      <title>AI Can Build Your UI — But Can It Maintain It?</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Thu, 04 Jun 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/ai-can-build-your-ui-but-can-it-maintain-it-d2l</link>
      <guid>https://dev.to/johnnylemonny/ai-can-build-your-ui-but-can-it-maintain-it-d2l</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Modern Web Development in 2026&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A practical series about building faster, cleaner, more maintainable web applications without chasing every shiny thing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI can build a surprisingly convincing UI in a few minutes.&lt;/p&gt;

&lt;p&gt;That is useful. It is also dangerous in the same way a good mockup is dangerous: it can look finished before the engineering decisions are finished.&lt;/p&gt;

&lt;p&gt;The question is no longer whether AI can generate components.&lt;/p&gt;

&lt;p&gt;The better question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can your team maintain what AI generated six months from now?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Generated UI has a smell
&lt;/h2&gt;

&lt;p&gt;AI-generated frontend often looks impressive at first glance and strange on review.&lt;/p&gt;

&lt;p&gt;Common signs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;duplicated components,&lt;/li&gt;
&lt;li&gt;inconsistent spacing,&lt;/li&gt;
&lt;li&gt;random animation choices,&lt;/li&gt;
&lt;li&gt;inaccessible markup,&lt;/li&gt;
&lt;li&gt;weak loading and error states,&lt;/li&gt;
&lt;li&gt;hard-coded colors,&lt;/li&gt;
&lt;li&gt;unclear state ownership,&lt;/li&gt;
&lt;li&gt;no component contract,&lt;/li&gt;
&lt;li&gt;tests that only prove rendering happened.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this means the tool is bad. It means the output needs engineering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give AI constraints, not vibes
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build a beautiful dashboard.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Build a dashboard page using our existing Card, Button, Badge, and Table components.
Use semantic HTML. Include loading, empty, and error states.
Do not introduce new colors outside the design tokens.
Keep state local unless it is shared by more than one component.
Add keyboard-accessible interactions.
Return the component and a short review checklist.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI is much more useful when you give it boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask for states, not just screens
&lt;/h2&gt;

&lt;p&gt;A screen is not a component.&lt;/p&gt;

&lt;p&gt;A component has states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;loading
empty
success
error
permission denied
partial data
saving
saved
validation failed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the generated UI only includes the happy path, it is not ready for production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Require component contracts
&lt;/h2&gt;

&lt;p&gt;Before accepting generated UI, define the contract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;InvoiceTableProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;invoices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InvoiceSummary&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;empty&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ready&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;onRetry&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;onOpenInvoice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;invoiceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InvoiceId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This forces the component to have a clear boundary.&lt;/p&gt;

&lt;p&gt;Without a contract, generated UI tends to reach outward: global state, random fetches, hidden assumptions, and props that grow without discipline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep design tokens non-negotiable
&lt;/h2&gt;

&lt;p&gt;AI loves inventing colors.&lt;/p&gt;

&lt;p&gt;Do not let it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color-bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0b1020&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-surface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#121a2f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f8fafc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-muted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#94a3b8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#22c55e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tell the tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use only existing design tokens. Do not invent new hex colors, spacing values, shadows, or font sizes unless explicitly requested.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Consistency is not decoration. It is maintainability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make accessibility part of the prompt
&lt;/h2&gt;

&lt;p&gt;Do not ask for accessibility after the component is generated.&lt;/p&gt;

&lt;p&gt;Include it at the start:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use semantic HTML.
All interactive elements must be keyboard accessible.
Use visible focus states.
Do not use divs as buttons.
Connect form labels and error messages properly.
Avoid ARIA unless native HTML is not enough.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will not guarantee perfect accessibility, but it raises the floor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review generated UI like a pull request
&lt;/h2&gt;

&lt;p&gt;Use this checklist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## AI-generated UI review&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Does it use existing components and tokens?
&lt;span class="p"&gt;-&lt;/span&gt; Are loading, empty, error, and permission states handled?
&lt;span class="p"&gt;-&lt;/span&gt; Is the markup semantic?
&lt;span class="p"&gt;-&lt;/span&gt; Is keyboard interaction supported?
&lt;span class="p"&gt;-&lt;/span&gt; Are props explicit and typed?
&lt;span class="p"&gt;-&lt;/span&gt; Is state owned by the right component?
&lt;span class="p"&gt;-&lt;/span&gt; Are side effects isolated?
&lt;span class="p"&gt;-&lt;/span&gt; Are tests meaningful?
&lt;span class="p"&gt;-&lt;/span&gt; Is there duplicated logic?
&lt;span class="p"&gt;-&lt;/span&gt; Could another developer safely change this later?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last question is the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Good uses of AI in frontend
&lt;/h2&gt;

&lt;p&gt;AI is genuinely helpful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;first drafts,&lt;/li&gt;
&lt;li&gt;test scaffolding,&lt;/li&gt;
&lt;li&gt;refactoring repetitive markup,&lt;/li&gt;
&lt;li&gt;converting rough UI ideas into components,&lt;/li&gt;
&lt;li&gt;writing stories for component states,&lt;/li&gt;
&lt;li&gt;finding accessibility issues,&lt;/li&gt;
&lt;li&gt;generating edge-case checklists,&lt;/li&gt;
&lt;li&gt;explaining unfamiliar code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is less reliable for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;architecture boundaries,&lt;/li&gt;
&lt;li&gt;product intent,&lt;/li&gt;
&lt;li&gt;design consistency,&lt;/li&gt;
&lt;li&gt;long-term ownership,&lt;/li&gt;
&lt;li&gt;subtle accessibility behavior,&lt;/li&gt;
&lt;li&gt;performance tradeoffs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use it where speed helps. Slow down where judgment matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;AI can generate UI. That is no longer the impressive part.&lt;/p&gt;

&lt;p&gt;The impressive part is building a frontend system where generated code has to pass through the same standards as human-written code.&lt;/p&gt;

&lt;p&gt;A fast draft is useful. A maintainable product is better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Syncfusion, “Frontend Development Trends 2026,” published April 28, 2026: &lt;a href="https://www.syncfusion.com/blogs/post/frontend-development-trends" rel="noopener noreferrer"&gt;https://www.syncfusion.com/blogs/post/frontend-development-trends&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;2025 JavaScript Rising Stars, published 2026: &lt;a href="https://risingstars.js.org/2025/en" rel="noopener noreferrer"&gt;https://risingstars.js.org/2025/en&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Stack Overflow Blog, “Domain expertise still wanted: the latest trends in AI-assisted knowledge for developers,” published March 16, 2026: &lt;a href="https://stackoverflow.blog/2026/03/16/domain-expertise-still-wanted-the-latest-trends-in-ai/" rel="noopener noreferrer"&gt;https://stackoverflow.blog/2026/03/16/domain-expertise-still-wanted-the-latest-trends-in-ai/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading.&lt;/p&gt;

&lt;p&gt;You can find me here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/johnnylemonny" rel="noopener noreferrer"&gt;https://github.com/johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DEV: &lt;a href="https://dev.to/johnnylemonny"&gt;https://dev.to/johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Accessibility Is Not a Lighthouse Checkbox</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Tue, 02 Jun 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/accessibility-is-not-a-lighthouse-checkbox-2a1n</link>
      <guid>https://dev.to/johnnylemonny/accessibility-is-not-a-lighthouse-checkbox-2a1n</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Modern Web Development in 2026&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A practical series about building faster, cleaner, more maintainable web applications without chasing every shiny thing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Automated accessibility checks are useful.&lt;/p&gt;

&lt;p&gt;They catch missing labels, obvious contrast failures, invalid ARIA, and a lot of mistakes that should never reach production.&lt;/p&gt;

&lt;p&gt;But accessibility is not finished when a tool gives you a green score.&lt;/p&gt;

&lt;p&gt;A green score can still hide a page that is confusing, exhausting, or impossible to use with a keyboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with semantic HTML
&lt;/h2&gt;

&lt;p&gt;The most underrated accessibility tool is still HTML.&lt;/p&gt;

&lt;p&gt;Use the element that matches the job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Save changes&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/settings"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Account settings&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Main navigation"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not turn everything into a &lt;code&gt;div&lt;/code&gt; and then rebuild the platform with ARIA.&lt;/p&gt;

&lt;p&gt;ARIA is powerful. It is also easy to misuse. Native semantics are usually safer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keyboard test every important flow
&lt;/h2&gt;

&lt;p&gt;Put the mouse away.&lt;/p&gt;

&lt;p&gt;Can you complete the flow with only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tab
Shift + Tab
Enter
Space
Arrow keys
Escape
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;opening menus,&lt;/li&gt;
&lt;li&gt;closing dialogs,&lt;/li&gt;
&lt;li&gt;submitting forms,&lt;/li&gt;
&lt;li&gt;moving through filters,&lt;/li&gt;
&lt;li&gt;skipping repeated navigation,&lt;/li&gt;
&lt;li&gt;reaching error messages,&lt;/li&gt;
&lt;li&gt;recovering from mistakes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your product cannot be used from a keyboard, it is broken for more people than you think.&lt;/p&gt;

&lt;h2&gt;
  
  
  Focus is part of the interface
&lt;/h2&gt;

&lt;p&gt;Removing focus styles is not polish. It is damage.&lt;/p&gt;

&lt;p&gt;Good focus indicators are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;visible,&lt;/li&gt;
&lt;li&gt;consistent,&lt;/li&gt;
&lt;li&gt;high contrast,&lt;/li&gt;
&lt;li&gt;not hidden behind shadows,&lt;/li&gt;
&lt;li&gt;not dependent on color alone.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:focus-visible&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--focus-ring&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;outline-offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is not to make focus pretty enough for a screenshot. The goal is to make the current location obvious.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forms need more care than we give them
&lt;/h2&gt;

&lt;p&gt;A good form explains itself before, during, and after input.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Email address&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;autocomplete=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;aria-describedby=&lt;/span&gt;&lt;span class="s"&gt;"email-hint"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"email-hint"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Use the address where you want to receive account updates.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For errors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;place the message near the field,&lt;/li&gt;
&lt;li&gt;describe how to fix it,&lt;/li&gt;
&lt;li&gt;connect it programmatically,&lt;/li&gt;
&lt;li&gt;summarize errors for long forms,&lt;/li&gt;
&lt;li&gt;move focus intentionally after failed submission.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;“Invalid input” is not a helpful error. It is a shrug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dialogs are traps unless designed carefully
&lt;/h2&gt;

&lt;p&gt;A modal dialog should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;move focus into the dialog when opened,&lt;/li&gt;
&lt;li&gt;trap focus while open,&lt;/li&gt;
&lt;li&gt;close with Escape when appropriate,&lt;/li&gt;
&lt;li&gt;return focus to the triggering element,&lt;/li&gt;
&lt;li&gt;have a clear accessible name,&lt;/li&gt;
&lt;li&gt;avoid background interaction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If that sounds like a lot, use a well-tested primitive. Dialogs are one of the places where “simple custom code” often becomes inaccessible custom code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do not use color alone
&lt;/h2&gt;

&lt;p&gt;Color is useful. It should not be the only signal.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Required fields are red.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Better:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Required fields have text, an icon, and programmatic state.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For charts, statuses, validation, and alerts, combine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;text,&lt;/li&gt;
&lt;li&gt;shape,&lt;/li&gt;
&lt;li&gt;position,&lt;/li&gt;
&lt;li&gt;iconography,&lt;/li&gt;
&lt;li&gt;semantic markup.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Automated tests are a floor
&lt;/h2&gt;

&lt;p&gt;Add accessibility checks to CI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;AxeBuilder&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@axe-core/playwright&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;page has no obvious accessibility violations&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/settings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AxeBuilder&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;violations&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is good. It is not enough.&lt;/p&gt;

&lt;p&gt;Automated tools cannot fully judge whether the flow makes sense, whether focus movement feels logical, or whether the content is understandable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The human checklist
&lt;/h2&gt;

&lt;p&gt;Before release, test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Can the full flow be completed with keyboard only?&lt;/li&gt;
&lt;li&gt;[ ] Is focus always visible?&lt;/li&gt;
&lt;li&gt;[ ] Are headings meaningful and ordered?&lt;/li&gt;
&lt;li&gt;[ ] Are form labels explicit?&lt;/li&gt;
&lt;li&gt;[ ] Are errors helpful and connected to fields?&lt;/li&gt;
&lt;li&gt;[ ] Are dialogs focus-managed?&lt;/li&gt;
&lt;li&gt;[ ] Is color supported by another signal?&lt;/li&gt;
&lt;li&gt;[ ] Does the page make sense with CSS disabled?&lt;/li&gt;
&lt;li&gt;[ ] Does the page make sense with images unavailable?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;Accessibility is not a separate layer you add after the “normal” product is done.&lt;/p&gt;

&lt;p&gt;It is part of the product being usable.&lt;/p&gt;

&lt;p&gt;A green automated score is a good start. A real user completing a real task without barriers is the goal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;CSS-Tricks, “HTTP Archive 2025 Web Almanac,” published January 16, 2026: &lt;a href="https://css-tricks.com/http-archive-2025-web-almanac/" rel="noopener noreferrer"&gt;https://css-tricks.com/http-archive-2025-web-almanac/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Syncfusion, “Frontend Development Trends 2026,” published April 28, 2026: &lt;a href="https://www.syncfusion.com/blogs/post/frontend-development-trends" rel="noopener noreferrer"&gt;https://www.syncfusion.com/blogs/post/frontend-development-trends&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MDN Web Docs, “Baseline compatibility”: &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading.&lt;/p&gt;

&lt;p&gt;You can find me here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/johnnylemonny" rel="noopener noreferrer"&gt;https://github.com/johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DEV: &lt;a href="https://dev.to/johnnylemonny"&gt;https://dev.to/johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>a11y</category>
      <category>webdev</category>
      <category>html</category>
      <category>frontend</category>
    </item>
    <item>
      <title>CSS Got Good While You Were Installing More Libraries</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Thu, 28 May 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/css-got-good-while-you-were-installing-more-libraries-dm1</link>
      <guid>https://dev.to/johnnylemonny/css-got-good-while-you-were-installing-more-libraries-dm1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Modern Web Development in 2026&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A practical series about building faster, cleaner, more maintainable web applications without chasing every shiny thing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A lot of frontend habits were formed when CSS was weaker.&lt;/p&gt;

&lt;p&gt;We reached for libraries because layout was painful, responsive components were awkward, browser support was uncertain, and the cascade felt like a haunted basement.&lt;/p&gt;

&lt;p&gt;That history is real.&lt;/p&gt;

&lt;p&gt;But modern CSS is not the same tool many developers learned five or ten years ago.&lt;/p&gt;

&lt;p&gt;CSS got good. Quietly. While everyone was arguing about JavaScript frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The new default: check the platform first
&lt;/h2&gt;

&lt;p&gt;Before installing a styling library, ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can the platform solve this now?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Often, the answer is yes.&lt;/p&gt;

&lt;p&gt;Modern CSS gives us better primitives for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;responsive components,&lt;/li&gt;
&lt;li&gt;layout composition,&lt;/li&gt;
&lt;li&gt;cascade control,&lt;/li&gt;
&lt;li&gt;design tokens,&lt;/li&gt;
&lt;li&gt;theme switching,&lt;/li&gt;
&lt;li&gt;typography,&lt;/li&gt;
&lt;li&gt;state-based styling,&lt;/li&gt;
&lt;li&gt;fluid spacing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Libraries can still be useful. They should not be reflexes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Container queries change component design
&lt;/h2&gt;

&lt;p&gt;Viewport media queries answer this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How wide is the screen?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Container queries answer the more useful question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How much space does this component have?&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.card-grid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;container-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@container&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;42rem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.article-card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14rem&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes components more portable. A card can adapt inside a sidebar, dashboard, modal, or full-width layout without pretending the viewport is the only context.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;:has()&lt;/code&gt; makes parent-aware styling practical
&lt;/h2&gt;

&lt;p&gt;For years, developers wanted a parent selector. Now we have a powerful version of it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.field&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="nd"&gt;:invalid&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-danger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.card&lt;/span&gt;&lt;span class="nd"&gt;:has&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.card__media&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;grid-template-rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Used carefully, &lt;code&gt;:has()&lt;/code&gt; can remove JavaScript that only exists to toggle classes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cascade layers make CSS less fragile
&lt;/h2&gt;

&lt;p&gt;The cascade is not bad. Unmanaged cascade is bad.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--space-4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="py"&gt;--radius-lg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--radius-lg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--space-4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Layers let you define priority intentionally instead of relying on selector combat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subgrid fixes a real layout problem
&lt;/h2&gt;

&lt;p&gt;Nested layouts often need to align with parent tracks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.article-list&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt; &lt;span class="n"&gt;minmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;42rem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.article-card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;subgrid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;grid-column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="m"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helps create layouts that feel designed, not merely stacked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fluid type without a framework
&lt;/h2&gt;

&lt;p&gt;You can create responsive typography with plain CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--step-0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.96rem&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;0.2vw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1.125rem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--step-3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1.75rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1.25rem&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="m"&gt;2vw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3rem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--step-3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.05&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--step-0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No runtime. No package. No hydration. Just CSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Baseline thinking
&lt;/h2&gt;

&lt;p&gt;The modern question is not “Can I use this feature on my machine?”&lt;/p&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is this feature supported well enough for my audience and fallback strategy?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Baseline helps make that decision more systematic, but it does not replace testing. You still need to check accessibility, usability, performance, and your actual user base.&lt;/p&gt;

&lt;h2&gt;
  
  
  The library decision checklist
&lt;/h2&gt;

&lt;p&gt;Before adding a styling dependency, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Is the problem solved by modern CSS?&lt;/li&gt;
&lt;li&gt;[ ] Does the library add runtime JavaScript?&lt;/li&gt;
&lt;li&gt;[ ] Does it make accessibility easier or harder?&lt;/li&gt;
&lt;li&gt;[ ] Can the team debug the generated output?&lt;/li&gt;
&lt;li&gt;[ ] Does it work with our design tokens?&lt;/li&gt;
&lt;li&gt;[ ] Can we remove it later?&lt;/li&gt;
&lt;li&gt;[ ] Is the dependency worth the long-term surface area?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;CSS is not just the thing you write after the “real” engineering is done.&lt;/p&gt;

&lt;p&gt;It is a powerful layout and interaction language that now solves problems we used to outsource by default.&lt;/p&gt;

&lt;p&gt;Install libraries when they help. But give the platform a chance first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;web.dev, “Baseline”: &lt;a href="https://web.dev/baseline/" rel="noopener noreferrer"&gt;https://web.dev/baseline/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MDN Web Docs, “Baseline compatibility”: &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CSS-Tricks, “HTTP Archive 2025 Web Almanac,” published January 16, 2026: &lt;a href="https://css-tricks.com/http-archive-2025-web-almanac/" rel="noopener noreferrer"&gt;https://css-tricks.com/http-archive-2025-web-almanac/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading.&lt;/p&gt;

&lt;p&gt;You can find me here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/johnnylemonny" rel="noopener noreferrer"&gt;https://github.com/johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DEV: &lt;a href="https://dev.to/johnnylemonny"&gt;https://dev.to/johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>design</category>
    </item>
    <item>
      <title>TypeScript Won — Now Stop Using It Like Fancy JavaScript</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Tue, 26 May 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/typescript-won-now-stop-using-it-like-fancy-javascript-3hi0</link>
      <guid>https://dev.to/johnnylemonny/typescript-won-now-stop-using-it-like-fancy-javascript-3hi0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Modern Web Development in 2026&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A practical series about building faster, cleaner, more maintainable web applications without chasing every shiny thing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;TypeScript won the argument.&lt;/p&gt;

&lt;p&gt;Most serious JavaScript codebases either use it already or are planning around it. The more interesting question is what we do after adoption.&lt;/p&gt;

&lt;p&gt;Because a lot of TypeScript code is still just JavaScript wearing a blazer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technically TypeScript. Spiritually not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types should protect boundaries
&lt;/h2&gt;

&lt;p&gt;The best places to spend type effort are boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API input,&lt;/li&gt;
&lt;li&gt;API output,&lt;/li&gt;
&lt;li&gt;database records,&lt;/li&gt;
&lt;li&gt;form state,&lt;/li&gt;
&lt;li&gt;configuration,&lt;/li&gt;
&lt;li&gt;feature flags,&lt;/li&gt;
&lt;li&gt;permissions,&lt;/li&gt;
&lt;li&gt;domain events.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Internal implementation types are useful. Boundary types are where bugs go to die.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replace vague shapes with domain language
&lt;/h2&gt;

&lt;p&gt;This is common:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is better than nothing, but it leaves too much room.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;UserRole&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;editor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;viewer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;UserStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;invited&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;suspended&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserRole&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserStatus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now invalid states have fewer places to hide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use branded types for dangerous strings
&lt;/h2&gt;

&lt;p&gt;Not every string is the same kind of string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Brand&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="na"&gt;__brand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;UserId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Brand&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UserId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;OrgId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Brand&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OrgId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now this mistake becomes harder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orgId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrgId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A plain string can come from anywhere. A branded string says, “This value passed through a specific gate.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Prefer discriminated unions over boolean soup
&lt;/h2&gt;

&lt;p&gt;Boolean-heavy state is where UI bugs breed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;RequestState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows nonsense:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use a union:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;RequestState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;idle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the impossible states are actually impossible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate runtime data
&lt;/h2&gt;

&lt;p&gt;TypeScript does not validate JSON at runtime.&lt;/p&gt;

&lt;p&gt;This is one of the most expensive misunderstandings in web development.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That line does not make the response a &lt;code&gt;User&lt;/code&gt;. It makes the compiler quiet.&lt;/p&gt;

&lt;p&gt;Use runtime validation at external boundaries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;editor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;viewer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;invited&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;suspended&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UserSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now TypeScript and runtime reality are connected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use &lt;code&gt;satisfies&lt;/code&gt; for configuration
&lt;/h2&gt;

&lt;p&gt;Configuration should be checked without losing literal types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;home&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dashboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dashboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/settings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;satisfies&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This catches invalid values while preserving useful inference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid type theater
&lt;/h2&gt;

&lt;p&gt;Some types add noise but not safety.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;StringOrNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ObjectMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These can be valid in rare cases, but often they are placeholders for decisions nobody made.&lt;/p&gt;

&lt;p&gt;Good TypeScript is not more TypeScript. Good TypeScript is better constraints.&lt;/p&gt;

&lt;h2&gt;
  
  
  The practical checklist
&lt;/h2&gt;

&lt;p&gt;Use this before merging TypeScript-heavy changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Are external inputs validated at runtime?&lt;/li&gt;
&lt;li&gt;[ ] Are domain concepts named explicitly?&lt;/li&gt;
&lt;li&gt;[ ] Are impossible UI states impossible?&lt;/li&gt;
&lt;li&gt;[ ] Are IDs and tokens distinguishable?&lt;/li&gt;
&lt;li&gt;[ ] Are &lt;code&gt;any&lt;/code&gt; and unsafe assertions isolated?&lt;/li&gt;
&lt;li&gt;[ ] Can a new developer understand the model from the types?&lt;/li&gt;
&lt;li&gt;[ ] Do the types reduce tests you would otherwise need?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;TypeScript is not valuable because it makes JavaScript look professional.&lt;/p&gt;

&lt;p&gt;It is valuable because it lets you encode decisions before those decisions become bugs.&lt;/p&gt;

&lt;p&gt;If your types do not protect boundaries, model the domain, or remove impossible states, you are leaving most of the value on the table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;InfoQ, “State of JavaScript 2025: Survey Reveals a Maturing Ecosystem with TypeScript Cementing Dominance,” published March 20, 2026: &lt;a href="https://www.infoq.com/news/2026/03/state-of-js-survey-2025/" rel="noopener noreferrer"&gt;https://www.infoq.com/news/2026/03/state-of-js-survey-2025/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Syncfusion, “Frontend Development Trends 2026,” published April 28, 2026: &lt;a href="https://www.syncfusion.com/blogs/post/frontend-development-trends" rel="noopener noreferrer"&gt;https://www.syncfusion.com/blogs/post/frontend-development-trends&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading.&lt;/p&gt;

&lt;p&gt;You can find me here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/johnnylemonny" rel="noopener noreferrer"&gt;https://github.com/johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DEV: &lt;a href="https://dev.to/johnnylemonny"&gt;https://dev.to/johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Cabal for Android: My First Native Kotlin P2P Chat App Reaches Its First Early Version</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Tue, 26 May 2026 06:30:00 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/cabal-for-android-my-first-native-kotlin-p2p-chat-app-reaches-its-first-early-version-1n64</link>
      <guid>https://dev.to/johnnylemonny/cabal-for-android-my-first-native-kotlin-p2p-chat-app-reaches-its-first-early-version-1n64</guid>
      <description>&lt;p&gt;A few weeks ago, I wrote about working on &lt;strong&gt;Cabal v3&lt;/strong&gt;, a modern peer-to-peer chat app for Android, and how I started by exploring a React Native direction.&lt;/p&gt;

&lt;p&gt;Later, I also wrote about how I started learning &lt;strong&gt;Kotlin&lt;/strong&gt; by building a real Android app instead of only reading tutorials.&lt;/p&gt;

&lt;p&gt;This post is the continuation of that series.&lt;/p&gt;

&lt;p&gt;Today, I’m happy to share that the first early version of &lt;strong&gt;Cabal for Android&lt;/strong&gt; is now available.&lt;/p&gt;

&lt;p&gt;GitHub repository:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/johnnylemonny/cabal-android" rel="noopener noreferrer"&gt;https://github.com/johnnylemonny/cabal-android&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An early version is also available to download from the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Cabal for Android?
&lt;/h2&gt;

&lt;p&gt;Cabal for Android is my attempt to build a modern Android client for Cabal as a native Kotlin application.&lt;/p&gt;

&lt;p&gt;The idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;a peer-to-peer chat app for Android, built with modern native Android development in mind.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This project started as an experiment, but it quickly became something more serious for me. It became a way to learn Kotlin, understand Android development better, and explore how decentralized communication can feel on mobile.&lt;/p&gt;

&lt;h2&gt;
  
  
  From React Native exploration to native Kotlin
&lt;/h2&gt;

&lt;p&gt;In the first post of this series, I wrote about reviving a P2P mobile chat app idea and experimenting with Cabal on Android.&lt;/p&gt;

&lt;p&gt;At that point, I was thinking about the project from a React Native perspective.&lt;/p&gt;

&lt;p&gt;That was useful because it helped me understand the shape of the app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what the UI could look like,&lt;/li&gt;
&lt;li&gt;how the chat flow should feel,&lt;/li&gt;
&lt;li&gt;what kind of mobile experience I wanted,&lt;/li&gt;
&lt;li&gt;and how much work would be involved in bringing a P2P chat app back to life on Android.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But after spending more time with the project, I decided to move deeper into native Android development.&lt;/p&gt;

&lt;p&gt;That decision led me to Kotlin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Kotlin?
&lt;/h2&gt;

&lt;p&gt;Kotlin had been on my list for a long time.&lt;/p&gt;

&lt;p&gt;I had read about it before. I had seen examples. I understood the basic syntax. But I never really learned it properly because I was missing one important thing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a real project.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cabal became that project.&lt;/p&gt;

&lt;p&gt;Instead of learning Kotlin in isolation, I learned it by building something that had real screens, real state, real problems, and real trade-offs.&lt;/p&gt;

&lt;p&gt;That changed everything.&lt;/p&gt;

&lt;p&gt;Kotlin stopped being just a list of language features and became a tool I used every day to move the app forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is included in this first version?
&lt;/h2&gt;

&lt;p&gt;This is still an early version, but it is an important milestone.&lt;/p&gt;

&lt;p&gt;The app is now at the point where it feels like a real native Android project instead of just an idea or experiment.&lt;/p&gt;

&lt;p&gt;The first version focuses on the foundation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;native Android structure,&lt;/li&gt;
&lt;li&gt;Kotlin-based implementation,&lt;/li&gt;
&lt;li&gt;a modern mobile app direction,&lt;/li&gt;
&lt;li&gt;Cabal-related chat experience,&lt;/li&gt;
&lt;li&gt;early downloadable build,&lt;/li&gt;
&lt;li&gt;and a codebase that can now evolve further.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is not perfect yet.&lt;/p&gt;

&lt;p&gt;It is not “finished” in the final-product sense.&lt;/p&gt;

&lt;p&gt;But it is finished in the most important early-stage sense:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the project exists, runs, can be tested, and has a real direction.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That feels like a big step.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned while building it
&lt;/h2&gt;

&lt;p&gt;Building this first version taught me much more than I expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Real apps teach faster than tutorials
&lt;/h3&gt;

&lt;p&gt;Tutorials are useful, but they usually have clean examples and predictable problems.&lt;/p&gt;

&lt;p&gt;Real apps are different.&lt;/p&gt;

&lt;p&gt;When building Cabal for Android, I had to make decisions about structure, state, screens, naming, data flow, and how to keep the app understandable as it grew.&lt;/p&gt;

&lt;p&gt;That kind of learning is harder, but also much more valuable.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Kotlin makes Android development feel focused
&lt;/h3&gt;

&lt;p&gt;One thing I started to appreciate is how Kotlin encourages more intentional code.&lt;/p&gt;

&lt;p&gt;Null safety, data classes, immutability, and concise syntax all helped me think more clearly about the app.&lt;/p&gt;

&lt;p&gt;At first, some parts slowed me down. But over time, those same features made the project feel safer and easier to reason about.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Native Android development has its own rhythm
&lt;/h3&gt;

&lt;p&gt;Coming from other environments, native Android development requires a different mindset.&lt;/p&gt;

&lt;p&gt;You are not only learning Kotlin.&lt;/p&gt;

&lt;p&gt;You are also learning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Android project structure,&lt;/li&gt;
&lt;li&gt;lifecycle concepts,&lt;/li&gt;
&lt;li&gt;UI patterns,&lt;/li&gt;
&lt;li&gt;state handling,&lt;/li&gt;
&lt;li&gt;Gradle,&lt;/li&gt;
&lt;li&gt;app packaging,&lt;/li&gt;
&lt;li&gt;and how all of these pieces fit together.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was challenging, but also one of the best parts of the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Small milestones matter
&lt;/h3&gt;

&lt;p&gt;This version is early, but shipping it matters.&lt;/p&gt;

&lt;p&gt;It is easy to keep waiting until everything is perfect.&lt;/p&gt;

&lt;p&gt;But open-source projects grow better when they are visible early.&lt;/p&gt;

&lt;p&gt;Putting this first version out there gives the project a real starting point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this project matters to me
&lt;/h2&gt;

&lt;p&gt;Cabal for Android sits at the intersection of a few things I care about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mobile development,&lt;/li&gt;
&lt;li&gt;Kotlin,&lt;/li&gt;
&lt;li&gt;Android,&lt;/li&gt;
&lt;li&gt;open source,&lt;/li&gt;
&lt;li&gt;peer-to-peer communication,&lt;/li&gt;
&lt;li&gt;and learning by building.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I like projects that are practical but also a little experimental.&lt;/p&gt;

&lt;p&gt;A P2P chat app is exactly that kind of project.&lt;/p&gt;

&lt;p&gt;It is not just another CRUD app. It forces you to think about networking, identity, local-first experiences, mobile constraints, and how users communicate without depending entirely on centralized infrastructure.&lt;/p&gt;

&lt;p&gt;That makes it a fun and meaningful project to work on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repository and download
&lt;/h2&gt;

&lt;p&gt;The project is available here:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/johnnylemonny/cabal-android" rel="noopener noreferrer"&gt;https://github.com/johnnylemonny/cabal-android&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is also an early downloadable version available from the repository.&lt;/p&gt;

&lt;p&gt;If you are interested in Kotlin, Android, peer-to-peer apps, or open-source mobile development, feel free to check it out.&lt;/p&gt;

&lt;p&gt;Feedback, ideas, and contributions are welcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes next?
&lt;/h2&gt;

&lt;p&gt;This first version is only the beginning.&lt;/p&gt;

&lt;p&gt;Next, I want to continue improving the app step by step:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;polish the UI,&lt;/li&gt;
&lt;li&gt;improve the chat experience,&lt;/li&gt;
&lt;li&gt;clean up the architecture,&lt;/li&gt;
&lt;li&gt;test more edge cases,&lt;/li&gt;
&lt;li&gt;improve stability,&lt;/li&gt;
&lt;li&gt;and continue learning Kotlin through real development.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also want to keep writing about the process because documenting the journey helps me understand what I’m learning.&lt;/p&gt;

&lt;p&gt;And maybe it helps someone else too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;This project started as an experiment.&lt;/p&gt;

&lt;p&gt;Then it became a Kotlin learning project.&lt;/p&gt;

&lt;p&gt;Now it is becoming a real native Android app.&lt;/p&gt;

&lt;p&gt;That progression has been very motivating.&lt;/p&gt;

&lt;p&gt;The biggest lesson so far is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to learn a technology deeply, build something real with it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cabal for Android gave me a reason to learn Kotlin properly.&lt;/p&gt;

&lt;p&gt;And now the first early version is here.&lt;/p&gt;

&lt;p&gt;Thanks for reading — and if you try the app or look through the code, I’d love to hear what you think.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>android</category>
      <category>opensource</category>
      <category>p2p</category>
    </item>
    <item>
      <title>Next.js Caching Is Hard Because You’re Thinking About It Too Late</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Thu, 21 May 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/nextjs-caching-is-hard-because-youre-thinking-about-it-too-late-586m</link>
      <guid>https://dev.to/johnnylemonny/nextjs-caching-is-hard-because-youre-thinking-about-it-too-late-586m</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Modern Web Development in 2026&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A practical series about building faster, cleaner, more maintainable web applications without chasing every shiny thing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next.js caching is not hard because caching is mysterious.&lt;/p&gt;

&lt;p&gt;It is hard because teams often decide what the user should see, build the feature, ship the route, and only then ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Wait, should this be cached?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By that point, caching feels like a trap. Static or dynamic? Revalidate or no-store? Tag invalidation? Server Actions? Partial rendering? Why did this page update locally but not in production?&lt;/p&gt;

&lt;p&gt;The problem started earlier.&lt;/p&gt;

&lt;p&gt;Caching is not a setting. It is a product decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with freshness
&lt;/h2&gt;

&lt;p&gt;Before writing code, classify the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Always fresh        account balance, auth state, checkout price
Fresh enough        inventory count, dashboard cards, notifications
Rarely changes      marketing copy, docs navigation, pricing page layout
Versioned           blog posts, changelog entries, release notes
User-triggered      form result, optimistic mutation, saved settings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each category deserves a different strategy.&lt;/p&gt;

&lt;p&gt;If you do not define freshness, the framework cannot guess your intent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The user does not care about your cache
&lt;/h2&gt;

&lt;p&gt;Users care about expectations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“I changed my profile; I should see it now.”&lt;/li&gt;
&lt;li&gt;“I published a post; the public page should update soon.”&lt;/li&gt;
&lt;li&gt;“I added an item to cart; the total should be correct.”&lt;/li&gt;
&lt;li&gt;“I opened docs; they should load instantly.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those expectations map directly to caching behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cache by boundary, not by hope
&lt;/h2&gt;

&lt;p&gt;A page usually contains mixed data.&lt;/p&gt;

&lt;p&gt;Example product page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;product title: rarely changes,&lt;/li&gt;
&lt;li&gt;price: may change,&lt;/li&gt;
&lt;li&gt;inventory: fresh enough,&lt;/li&gt;
&lt;li&gt;recommendations: personalized,&lt;/li&gt;
&lt;li&gt;reviews: revalidated periodically,&lt;/li&gt;
&lt;li&gt;cart state: user-specific.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you cache the entire page as one unit, one dynamic section can make the whole route harder to reason about.&lt;/p&gt;

&lt;p&gt;Instead, design boundaries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductShell&lt;/span&gt; &lt;span class="na"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LivePurchaseBox&lt;/span&gt; &lt;span class="na"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Recommendations&lt;/span&gt; &lt;span class="na"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then each section can have its own freshness model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make invalidation boring
&lt;/h2&gt;

&lt;p&gt;Invalidation should be obvious from the mutation.&lt;/p&gt;

&lt;p&gt;Bad mutation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Better mutation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;invalidateProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;invalidateProductList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even better: centralize the policy so developers do not remember tags by hand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;invalidateProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;revalidateTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`product:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;revalidateTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;products:list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A cache strategy nobody can remember is not a strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid accidental personalization leaks
&lt;/h2&gt;

&lt;p&gt;Caching becomes dangerous when user-specific data slips into shared output.&lt;/p&gt;

&lt;p&gt;Watch for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;user names in cached navigation,&lt;/li&gt;
&lt;li&gt;role-specific actions in shared HTML,&lt;/li&gt;
&lt;li&gt;personalized recommendations in static shells,&lt;/li&gt;
&lt;li&gt;auth-only pricing mixed with public product data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple rule helps:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Public data can be shared. User data needs a user boundary.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If a component depends on cookies, headers, session, or permissions, treat it differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use loading states intentionally
&lt;/h2&gt;

&lt;p&gt;Partial rendering and streaming are powerful because they let you show stable UI while dynamic sections load.&lt;/p&gt;

&lt;p&gt;But a loading skeleton is not a substitute for product thinking.&lt;/p&gt;

&lt;p&gt;Good skeleton:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;preserves layout,&lt;/li&gt;
&lt;li&gt;communicates progress,&lt;/li&gt;
&lt;li&gt;appears only where delay is expected,&lt;/li&gt;
&lt;li&gt;does not cause content to jump.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;flashes for 80ms,&lt;/li&gt;
&lt;li&gt;shifts the layout,&lt;/li&gt;
&lt;li&gt;appears everywhere,&lt;/li&gt;
&lt;li&gt;hides the fact that the data model is too slow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Debugging questions
&lt;/h2&gt;

&lt;p&gt;When a Next.js route behaves strangely, I use this checklist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Caching debug checklist&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Is this route expected to be static, dynamic, or mixed?
&lt;span class="p"&gt;-&lt;/span&gt; Which data must be fresh for every request?
&lt;span class="p"&gt;-&lt;/span&gt; Which data can be cached safely?
&lt;span class="p"&gt;-&lt;/span&gt; Is any user-specific data crossing a shared cache boundary?
&lt;span class="p"&gt;-&lt;/span&gt; What event invalidates this data?
&lt;span class="p"&gt;-&lt;/span&gt; Does the mutation revalidate the exact thing the user expects?
&lt;span class="p"&gt;-&lt;/span&gt; Is the loading state masking an architecture problem?
&lt;span class="p"&gt;-&lt;/span&gt; Can the behavior be explained in one paragraph?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last question matters. If nobody on the team can explain the caching behavior simply, production will explain it for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mental model
&lt;/h2&gt;

&lt;p&gt;Think of caching as a conversation between three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Data truth&lt;/strong&gt; — where the correct data lives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User expectation&lt;/strong&gt; — when the user expects to see changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rendering boundary&lt;/strong&gt; — where the UI can safely reuse work.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When those three agree, caching feels boring.&lt;/p&gt;

&lt;p&gt;When they disagree, caching feels haunted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;Next.js caching is not something to sprinkle on a finished route.&lt;/p&gt;

&lt;p&gt;It belongs in the first design conversation.&lt;/p&gt;

&lt;p&gt;Decide freshness early. Draw boundaries early. Name invalidation early.&lt;/p&gt;

&lt;p&gt;Your future self will spend less time asking why the page is stale and more time building things users actually notice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Next.js, “Next.js 16,” published October 21, 2025: &lt;a href="https://nextjs.org/blog/next-16" rel="noopener noreferrer"&gt;https://nextjs.org/blog/next-16&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;InfoWorld, “Next.js 16 features explicit caching, AI-powered debugging,” published October 24, 2025: &lt;a href="https://www.infoworld.com/article/4078213/next-js-16-features-explicit-caching-ai-powered-debugging.html" rel="noopener noreferrer"&gt;https://www.infoworld.com/article/4078213/next-js-16-features-explicit-caching-ai-powered-debugging.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading.&lt;/p&gt;

&lt;p&gt;You can find me here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/johnnylemonny" rel="noopener noreferrer"&gt;https://github.com/johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DEV: &lt;a href="https://dev.to/johnnylemonny"&gt;https://dev.to/johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>webdev</category>
      <category>performance</category>
    </item>
    <item>
      <title>React Is Not the Problem — Your Client-Side Mental Model Is</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Tue, 19 May 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/react-is-not-the-problem-your-client-side-mental-model-is-gcg</link>
      <guid>https://dev.to/johnnylemonny/react-is-not-the-problem-your-client-side-mental-model-is-gcg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Modern Web Development in 2026&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A practical series about building faster, cleaner, more maintainable web applications without chasing every shiny thing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;React gets blamed for a lot of slow websites.&lt;/p&gt;

&lt;p&gt;Sometimes that is fair. A careless React app can ship too much JavaScript, hydrate too much UI, and re-render too often.&lt;/p&gt;

&lt;p&gt;But React is rarely the whole problem. The deeper problem is a mental model that says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If something appears on the screen, it must be a client-side component.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That assumption made sense for many single-page apps. It makes less sense for much of the web we are building now.&lt;/p&gt;

&lt;h2&gt;
  
  
  A better default
&lt;/h2&gt;

&lt;p&gt;Try this default instead:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Render as much as possible before the browser receives the page. Hydrate only what must be interactive.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This does not mean every product should be static. It means interactivity should be intentional.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three kinds of UI
&lt;/h2&gt;

&lt;p&gt;Most screens contain three different kinds of UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Content UI
&lt;/h3&gt;

&lt;p&gt;This is information the user reads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;product names,&lt;/li&gt;
&lt;li&gt;documentation,&lt;/li&gt;
&lt;li&gt;blog content,&lt;/li&gt;
&lt;li&gt;pricing details,&lt;/li&gt;
&lt;li&gt;profile summaries,&lt;/li&gt;
&lt;li&gt;search results.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This usually does not need client-side state.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Navigation UI
&lt;/h3&gt;

&lt;p&gt;This helps the user move:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;menus,&lt;/li&gt;
&lt;li&gt;tabs,&lt;/li&gt;
&lt;li&gt;breadcrumbs,&lt;/li&gt;
&lt;li&gt;pagination,&lt;/li&gt;
&lt;li&gt;links.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of it needs JavaScript. Much of it does not.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Interaction UI
&lt;/h3&gt;

&lt;p&gt;This changes based on user action:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;forms,&lt;/li&gt;
&lt;li&gt;filters,&lt;/li&gt;
&lt;li&gt;carts,&lt;/li&gt;
&lt;li&gt;editors,&lt;/li&gt;
&lt;li&gt;dashboards,&lt;/li&gt;
&lt;li&gt;command palettes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where client-side React shines.&lt;/p&gt;

&lt;p&gt;The mistake is treating all three categories the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  The client component tax
&lt;/h2&gt;

&lt;p&gt;A client component costs more than its source file.&lt;/p&gt;

&lt;p&gt;It can pull in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dependencies,&lt;/li&gt;
&lt;li&gt;state management,&lt;/li&gt;
&lt;li&gt;event handlers,&lt;/li&gt;
&lt;li&gt;hydration work,&lt;/li&gt;
&lt;li&gt;serialization boundaries,&lt;/li&gt;
&lt;li&gt;re-render behavior,&lt;/li&gt;
&lt;li&gt;test complexity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So do not ask, “Can this be a client component?”&lt;/p&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What user interaction requires this to be a client component?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you cannot name the interaction, the component is probably in the wrong place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep islands small
&lt;/h2&gt;

&lt;p&gt;A common pattern is to make the whole layout interactive because one child needs state.&lt;/p&gt;

&lt;p&gt;Avoid this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setQuantity&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductHero&lt;/span&gt; &lt;span class="na"&gt;product&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductDescription&lt;/span&gt; &lt;span class="na"&gt;product&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;QuantityPicker&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setQuantity&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prefer this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductHero&lt;/span&gt; &lt;span class="na"&gt;product&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductDescription&lt;/span&gt; &lt;span class="na"&gt;product&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;QuantityPicker&lt;/span&gt; &lt;span class="na"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the interactive island is the picker, not the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop fetching twice
&lt;/h2&gt;

&lt;p&gt;Another slow pattern is server rendering a page and then immediately fetching the same data again in the browser.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;extra network requests,&lt;/li&gt;
&lt;li&gt;loading flicker,&lt;/li&gt;
&lt;li&gt;duplicated caching logic,&lt;/li&gt;
&lt;li&gt;inconsistent error states.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the server already has the data required for first render, use it. Fetch in the browser when the user’s interaction creates new information needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memoization is not architecture
&lt;/h2&gt;

&lt;p&gt;React developers sometimes reach for &lt;code&gt;useMemo&lt;/code&gt;, &lt;code&gt;useCallback&lt;/code&gt;, and memoized components when the real issue is that too much state lives too high in the tree.&lt;/p&gt;

&lt;p&gt;Before memoizing, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can this state move closer to the component that uses it?&lt;/li&gt;
&lt;li&gt;Can this work happen on the server?&lt;/li&gt;
&lt;li&gt;Can this derived value be computed once before render?&lt;/li&gt;
&lt;li&gt;Can this component stop receiving unstable props?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Memoization can help. It cannot fix a confused ownership model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The decision checklist
&lt;/h2&gt;

&lt;p&gt;For every component, ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Component placement review&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Does it need browser-only APIs?
&lt;span class="p"&gt;-&lt;/span&gt; Does it respond to user input before navigation?
&lt;span class="p"&gt;-&lt;/span&gt; Does it manage local interactive state?
&lt;span class="p"&gt;-&lt;/span&gt; Does it need real-time updates?
&lt;span class="p"&gt;-&lt;/span&gt; Does it depend on viewport measurements?
&lt;span class="p"&gt;-&lt;/span&gt; Could it render on the server and pass data to a smaller client child?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If most answers are “no,” keep it server-rendered.&lt;/p&gt;

&lt;h2&gt;
  
  
  React is still useful
&lt;/h2&gt;

&lt;p&gt;The point is not to write less React because React is bad.&lt;/p&gt;

&lt;p&gt;The point is to use React where it creates value:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rich forms,&lt;/li&gt;
&lt;li&gt;optimistic updates,&lt;/li&gt;
&lt;li&gt;complex stateful widgets,&lt;/li&gt;
&lt;li&gt;collaborative UI,&lt;/li&gt;
&lt;li&gt;dashboards,&lt;/li&gt;
&lt;li&gt;editors,&lt;/li&gt;
&lt;li&gt;design systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But rendering a heading, a paragraph, and a price card does not require a client-side mental model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;React did not make your app slow by itself.&lt;/p&gt;

&lt;p&gt;Your architecture made the browser responsible for work the browser did not need to do.&lt;/p&gt;

&lt;p&gt;Once you start treating client-side JavaScript as a budget instead of a default, React becomes easier to use well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;InfoQ, “State of JavaScript 2025: Survey Reveals a Maturing Ecosystem with TypeScript Cementing Dominance,” published March 20, 2026: &lt;a href="https://www.infoq.com/news/2026/03/state-of-js-survey-2025/" rel="noopener noreferrer"&gt;https://www.infoq.com/news/2026/03/state-of-js-survey-2025/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Next.js, “Next.js 16,” published October 21, 2025: &lt;a href="https://nextjs.org/blog/next-16" rel="noopener noreferrer"&gt;https://nextjs.org/blog/next-16&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading.&lt;/p&gt;

&lt;p&gt;You can find me here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/johnnylemonny" rel="noopener noreferrer"&gt;https://github.com/johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DEV: &lt;a href="https://dev.to/johnnylemonny"&gt;https://dev.to/johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>performance</category>
    </item>
    <item>
      <title>Stop Shipping 700KB of JavaScript for a Button</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Thu, 14 May 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/stop-shipping-700kb-of-javascript-for-a-button-3cnc</link>
      <guid>https://dev.to/johnnylemonny/stop-shipping-700kb-of-javascript-for-a-button-3cnc</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Modern Web Development in 2026&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A practical series about building faster, cleaner, more maintainable web applications without chasing every shiny thing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is a strange kind of web performance problem that does not look like a bug.&lt;/p&gt;

&lt;p&gt;The page loads. The button appears. The design looks fine. The framework did its job. Lighthouse is not screaming loudly enough to block the release.&lt;/p&gt;

&lt;p&gt;Then a real user taps the button on a mid-range phone while the main thread is busy doing work that should never have been shipped to the browser in the first place.&lt;/p&gt;

&lt;p&gt;That is when the interface stops feeling like software and starts feeling like a negotiation.&lt;/p&gt;

&lt;p&gt;This article is not an anti-JavaScript rant. JavaScript is one of the reasons the web is as capable as it is. The problem is simpler: &lt;strong&gt;we keep shipping client-side work that does not need to be client-side work&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable question
&lt;/h2&gt;

&lt;p&gt;Before optimizing a component, ask this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does the browser need this code to make the first interaction useful?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the answer is no, you have three options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;move it to the server,&lt;/li&gt;
&lt;li&gt;delay it,&lt;/li&gt;
&lt;li&gt;delete it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most performance wins come from that boring list.&lt;/p&gt;

&lt;h2&gt;
  
  
  The common failure mode
&lt;/h2&gt;

&lt;p&gt;A button starts as a button.&lt;/p&gt;

&lt;p&gt;Then it becomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a component library import,&lt;/li&gt;
&lt;li&gt;a theme provider dependency,&lt;/li&gt;
&lt;li&gt;an analytics wrapper,&lt;/li&gt;
&lt;li&gt;an icon package import,&lt;/li&gt;
&lt;li&gt;a tooltip,&lt;/li&gt;
&lt;li&gt;an animation primitive,&lt;/li&gt;
&lt;li&gt;a client component boundary,&lt;/li&gt;
&lt;li&gt;a hydration root,&lt;/li&gt;
&lt;li&gt;and a tiny state machine nobody asked for.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The user sees one button. The browser receives a small town.&lt;/p&gt;

&lt;h2&gt;
  
  
  Budget the page before you polish it
&lt;/h2&gt;

&lt;p&gt;A performance budget is not a punishment. It is a design constraint.&lt;/p&gt;

&lt;p&gt;Use a simple budget before arguing about micro-optimizations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Initial JavaScript:      under 170 KB compressed
Route-level JS:          under 80 KB compressed
Third-party scripts:     justified one by one
Critical CSS:            small enough to inline when useful
Images above the fold:   explicit dimensions and optimized format
Hydration islands:       only where interaction is required
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your exact numbers can differ. The important part is that the budget exists before the page is already slow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the bundle, not the vibes
&lt;/h2&gt;

&lt;p&gt;Run a bundle analyzer and look for the boring surprises:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
npx source-map-explorer &lt;span class="s2"&gt;"dist/**/*.js"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use the analyzer that fits your stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Next.js example&lt;/span&gt;
&lt;span class="nv"&gt;ANALYZE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why is this package in the initial route?&lt;/li&gt;
&lt;li&gt;Is this library used for one function?&lt;/li&gt;
&lt;li&gt;Did an icon import pull in more than one icon?&lt;/li&gt;
&lt;li&gt;Is a date library doing work that &lt;code&gt;Intl&lt;/code&gt; can do?&lt;/li&gt;
&lt;li&gt;Is a chart library needed before the user scrolls to the chart?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bundle analysis is humbling because it replaces opinions with weight.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be suspicious of client boundaries
&lt;/h2&gt;

&lt;p&gt;In server-first frameworks, the most expensive line in a file may be this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That line is not bad. It is a contract.&lt;/p&gt;

&lt;p&gt;It says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This component and the code it pulls in must be available to the browser.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So keep client components narrow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Good boundary: only the interactive part is client-side.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductSummary&lt;/span&gt; &lt;span class="na"&gt;product&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AddToCartButton&lt;/span&gt; &lt;span class="na"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductDetails&lt;/span&gt; &lt;span class="na"&gt;product&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The button can be interactive. The whole page does not need to hydrate because one button exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delay what is not needed yet
&lt;/h2&gt;

&lt;p&gt;Some code is useful, just not immediately.&lt;/p&gt;

&lt;p&gt;Good candidates for lazy loading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;charts below the fold,&lt;/li&gt;
&lt;li&gt;markdown editors,&lt;/li&gt;
&lt;li&gt;maps,&lt;/li&gt;
&lt;li&gt;video players,&lt;/li&gt;
&lt;li&gt;complex filters,&lt;/li&gt;
&lt;li&gt;admin-only panels,&lt;/li&gt;
&lt;li&gt;onboarding tours,&lt;/li&gt;
&lt;li&gt;non-critical animations.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HeavyChart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./HeavyChart&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ChartSkeleton&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;,&lt;/span&gt;
  &lt;span class="na"&gt;ssr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use this carefully. Lazy loading is not a magic spell. It moves cost. That is useful only when the moved cost no longer blocks the first useful interaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Third-party scripts are product decisions
&lt;/h2&gt;

&lt;p&gt;Performance often dies by a thousand approved vendors.&lt;/p&gt;

&lt;p&gt;Create a small script review:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;### Third-party script review&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; What user or business outcome does this script support?
&lt;span class="p"&gt;-&lt;/span&gt; Does it run on every route?
&lt;span class="p"&gt;-&lt;/span&gt; Can it load after interaction or consent?
&lt;span class="p"&gt;-&lt;/span&gt; Who owns it?
&lt;span class="p"&gt;-&lt;/span&gt; How do we remove it?
&lt;span class="p"&gt;-&lt;/span&gt; What is the fallback if it fails?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If nobody owns a third-party script, the browser owns the consequences.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimize for interaction, not screenshots
&lt;/h2&gt;

&lt;p&gt;A page that appears quickly but cannot respond is not fast.&lt;/p&gt;

&lt;p&gt;Look for long tasks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PerformanceObserver&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntries&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Long task:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;longtask&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;buffered&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then trace what blocks the main thread:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hydration,&lt;/li&gt;
&lt;li&gt;large JSON parsing,&lt;/li&gt;
&lt;li&gt;expensive rendering,&lt;/li&gt;
&lt;li&gt;synchronous storage access,&lt;/li&gt;
&lt;li&gt;client-side sorting/filtering of large lists,&lt;/li&gt;
&lt;li&gt;heavy analytics startup.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The browser is not a server. Stop treating it like one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The practical checklist
&lt;/h2&gt;

&lt;p&gt;Before shipping a new route, check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Is the initial JavaScript budget visible in CI?&lt;/li&gt;
&lt;li&gt;[ ] Are client components as small as possible?&lt;/li&gt;
&lt;li&gt;[ ] Is non-critical UI lazy loaded?&lt;/li&gt;
&lt;li&gt;[ ] Are third-party scripts justified?&lt;/li&gt;
&lt;li&gt;[ ] Are images sized and optimized?&lt;/li&gt;
&lt;li&gt;[ ] Is the main thread free before the first important interaction?&lt;/li&gt;
&lt;li&gt;[ ] Did you test on a slower device or throttled profile?&lt;/li&gt;
&lt;li&gt;[ ] Can the page still be useful if enhancements arrive late?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The boring truth
&lt;/h2&gt;

&lt;p&gt;Most users do not care which framework you used.&lt;/p&gt;

&lt;p&gt;They care that the page loads, responds, and gets out of their way.&lt;/p&gt;

&lt;p&gt;The fastest JavaScript is the JavaScript you never send. The second fastest is the JavaScript you send later. The third fastest is the JavaScript you actually needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;HTTP Archive, “Performance | 2025 Web Almanac,” published January 15, 2026: &lt;a href="https://almanac.httparchive.org/en/2025/performance" rel="noopener noreferrer"&gt;https://almanac.httparchive.org/en/2025/performance&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CSS-Tricks, “HTTP Archive 2025 Web Almanac,” published January 16, 2026: &lt;a href="https://css-tricks.com/http-archive-2025-web-almanac/" rel="noopener noreferrer"&gt;https://css-tricks.com/http-archive-2025-web-almanac/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading.&lt;/p&gt;

&lt;p&gt;You can find me here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/johnnylemonny" rel="noopener noreferrer"&gt;https://github.com/johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DEV: &lt;a href="https://dev.to/johnnylemonny"&gt;https://dev.to/johnnylemonny&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>performance</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>React devs: rethink useEffect! Treat it as sync with external systems, not lifecycle replacement. Avoid hidden state-machine traps &amp; keep your code clean. #webdev #react</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Fri, 08 May 2026 17:06:29 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/react-devs-rethink-useeffect-treat-it-as-sync-with-external-systems-not-lifecycle-replacement-1f0j</link>
      <guid>https://dev.to/johnnylemonny/react-devs-rethink-useeffect-treat-it-as-sync-with-external-systems-not-lifecycle-replacement-1f0j</guid>
      <description></description>
      <category>frontend</category>
      <category>javascript</category>
      <category>react</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
