<?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.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>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>
    <item>
      <title>The AI Code Review Checklist I Use Before Merging Any TypeScript PR</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Thu, 30 Apr 2026 10:40:00 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/the-ai-code-review-checklist-i-use-before-merging-any-typescript-pr-fc0</link>
      <guid>https://dev.to/johnnylemonny/the-ai-code-review-checklist-i-use-before-merging-any-typescript-pr-fc0</guid>
      <description>&lt;p&gt;AI can write code quickly.&lt;/p&gt;

&lt;p&gt;That is no longer the interesting part.&lt;/p&gt;

&lt;p&gt;The interesting part is what happens &lt;strong&gt;after&lt;/strong&gt; the code is generated.&lt;/p&gt;

&lt;p&gt;Because in real projects, the bottleneck is rarely “how do we get code faster?”&lt;br&gt;
It is usually:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this correct?&lt;/li&gt;
&lt;li&gt;Does it match the architecture?&lt;/li&gt;
&lt;li&gt;Does it handle edge cases?&lt;/li&gt;
&lt;li&gt;Is it safe to merge?&lt;/li&gt;
&lt;li&gt;Can someone else maintain it next month?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why I stopped treating AI output like finished code.&lt;/p&gt;

&lt;p&gt;Now I treat it like a draft that needs a reliable review system.&lt;/p&gt;

&lt;p&gt;This is the checklist I use before I merge any AI-generated TypeScript PR.&lt;/p&gt;

&lt;p&gt;Not a theoretical checklist.&lt;br&gt;
A practical one.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Can I explain the change in one sentence?
&lt;/h2&gt;

&lt;p&gt;Before I look at the code, I force myself to summarize the PR in one sentence.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;“This change validates uploaded image metadata before saving records to the database.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If I cannot describe the change clearly, one of two things is probably true:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the PR is doing too much&lt;/li&gt;
&lt;li&gt;the code is hiding the real intent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI often produces code that &lt;em&gt;looks&lt;/em&gt; organized while actually mixing multiple concerns.&lt;/p&gt;

&lt;p&gt;If the purpose is fuzzy, I stop there and split the change.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Does the code match the requested scope?
&lt;/h2&gt;

&lt;p&gt;AI loves to be helpful.&lt;/p&gt;

&lt;p&gt;Sometimes too helpful.&lt;/p&gt;

&lt;p&gt;It will often:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rename unrelated variables&lt;/li&gt;
&lt;li&gt;refactor nearby code&lt;/li&gt;
&lt;li&gt;introduce “small improvements”&lt;/li&gt;
&lt;li&gt;create utility functions nobody asked for&lt;/li&gt;
&lt;li&gt;solve adjacent problems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That makes review harder.&lt;/p&gt;

&lt;p&gt;So one of my first checks is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Did the model change only what needed to change?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If a PR was supposed to fix one validation bug but now touches eight files, I get suspicious immediately.&lt;/p&gt;

&lt;p&gt;A good AI-generated PR is usually narrower than you think.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Are the boundaries explicit?
&lt;/h2&gt;

&lt;p&gt;For TypeScript projects, this is one of the biggest signals of quality.&lt;/p&gt;

&lt;p&gt;I look for clear boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;input types&lt;/li&gt;
&lt;li&gt;output types&lt;/li&gt;
&lt;li&gt;domain models&lt;/li&gt;
&lt;li&gt;DTOs&lt;/li&gt;
&lt;li&gt;API contracts&lt;/li&gt;
&lt;li&gt;validation layers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI-generated code is much easier to trust when the edges are visible.&lt;/p&gt;

&lt;p&gt;Bad sign:&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;saveUser&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="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&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 typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CreateUserInput&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;email&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;displayName&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CreateUserResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&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;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;saveUser&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;CreateUserInput&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CreateUserResult&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the AI patch adds logic without tightening the contract, I usually improve the boundary before I approve the implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Did the AI generate code, or did it generate a new abstraction?
&lt;/h2&gt;

&lt;p&gt;This matters a lot.&lt;/p&gt;

&lt;p&gt;Sometimes AI gives you useful implementation.&lt;br&gt;
Sometimes it gives you a brand new architecture you did not ask for.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;extra layers&lt;/li&gt;
&lt;li&gt;generic helpers&lt;/li&gt;
&lt;li&gt;“reusable” wrappers&lt;/li&gt;
&lt;li&gt;configuration systems&lt;/li&gt;
&lt;li&gt;class hierarchies for simple logic&lt;/li&gt;
&lt;li&gt;abstraction before repetition actually exists&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A useful question here is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Would I still create this abstraction if a human teammate had not suggested it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the answer is no, I remove it.&lt;/p&gt;

&lt;p&gt;AI-generated code often becomes bloated not because it is broken, but because it is too eager to generalize.&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Is there a test for the thing that actually changed?
&lt;/h2&gt;

&lt;p&gt;I do not just ask, “Are there tests?”&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Is there a test for the exact behavior this PR claims to fix or add?&lt;/strong&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;one test for the happy path&lt;/li&gt;
&lt;li&gt;one test for the expected failure mode&lt;/li&gt;
&lt;li&gt;one test for the edge case that is easiest to miss&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the PR fixes a parsing bug, I want a parsing test.&lt;br&gt;
If it changes authorization logic, I want an authorization test.&lt;br&gt;
If it adds fallback behavior, I want a test that proves the fallback works.&lt;/p&gt;

&lt;p&gt;AI often writes tests that mirror the implementation too closely.&lt;/p&gt;

&lt;p&gt;So I look for tests that verify behavior, not just structure.&lt;/p&gt;
&lt;h2&gt;
  
  
  6. Does the code fail safely?
&lt;/h2&gt;

&lt;p&gt;A surprising amount of AI-generated code handles success better than failure.&lt;/p&gt;

&lt;p&gt;So I explicitly review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;missing values&lt;/li&gt;
&lt;li&gt;invalid input&lt;/li&gt;
&lt;li&gt;timeouts&lt;/li&gt;
&lt;li&gt;third-party failures&lt;/li&gt;
&lt;li&gt;null and undefined cases&lt;/li&gt;
&lt;li&gt;partial success scenarios&lt;/li&gt;
&lt;li&gt;retries&lt;/li&gt;
&lt;li&gt;logging&lt;/li&gt;
&lt;li&gt;user-facing error messages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I ask myself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What happens if this dependency is down?&lt;/li&gt;
&lt;li&gt;What happens if the payload shape changes?&lt;/li&gt;
&lt;li&gt;What happens if this field is missing?&lt;/li&gt;
&lt;li&gt;What happens if the operation succeeds halfway?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the answer is “the app probably throws something weird,” the PR is not ready.&lt;/p&gt;
&lt;h2&gt;
  
  
  7. Are runtime checks present at the system edges?
&lt;/h2&gt;

&lt;p&gt;TypeScript is great, but it does not validate runtime data by itself.&lt;/p&gt;

&lt;p&gt;So any AI-generated code that touches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;request bodies&lt;/li&gt;
&lt;li&gt;query parameters&lt;/li&gt;
&lt;li&gt;local storage&lt;/li&gt;
&lt;li&gt;database results&lt;/li&gt;
&lt;li&gt;webhooks&lt;/li&gt;
&lt;li&gt;environment variables&lt;/li&gt;
&lt;li&gt;third-party APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;should make me ask:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where is the runtime validation?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Types help inside the codebase.&lt;br&gt;
Validation protects the boundary.&lt;/p&gt;

&lt;p&gt;For example:&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;z&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;zod&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;CreatePostSchema&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;title&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="nf"&gt;min&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="na"&gt;body&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="nf"&gt;min&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="na"&gt;tags&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;array&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="k"&gt;default&lt;/span&gt;&lt;span class="p"&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;CreatePostInput&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="nx"&gt;infer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;CreatePostSchema&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;function&lt;/span&gt; &lt;span class="nf"&gt;parseCreatePostInput&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="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;CreatePostInput&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;CreatePostSchema&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="nx"&gt;input&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;If AI writes strongly typed code without validating incoming data, it creates a false sense of safety.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Are names better after the change — or worse?
&lt;/h2&gt;

&lt;p&gt;AI can produce valid code with terrible naming.&lt;/p&gt;

&lt;p&gt;And bad names are expensive because they survive code review surprisingly often.&lt;/p&gt;

&lt;p&gt;So I check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;function names&lt;/li&gt;
&lt;li&gt;variable names&lt;/li&gt;
&lt;li&gt;type names&lt;/li&gt;
&lt;li&gt;file names&lt;/li&gt;
&lt;li&gt;booleans&lt;/li&gt;
&lt;li&gt;enum values&lt;/li&gt;
&lt;li&gt;error messages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I want names that reflect the domain, not the implementation trick.&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 typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dataProcessorManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createHandler&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&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 typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;invoiceRetryScheduler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRetryScheduler&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When naming gets vague, maintainability drops fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Does the PR introduce duplicate logic?
&lt;/h2&gt;

&lt;p&gt;AI often rewrites something that already exists somewhere else in the codebase.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;near-duplicate validators&lt;/li&gt;
&lt;li&gt;inconsistent helpers&lt;/li&gt;
&lt;li&gt;slightly different parsing functions&lt;/li&gt;
&lt;li&gt;multiple ways to do the same thing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I always scan for duplication before approving.&lt;/p&gt;

&lt;p&gt;My rule is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if the logic already exists, reuse it&lt;/li&gt;
&lt;li&gt;if the existing abstraction is bad, improve it&lt;/li&gt;
&lt;li&gt;do not allow AI to create parallel versions of the same idea&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Duplicate code is especially dangerous when it looks clean.&lt;br&gt;
It feels harmless at first and becomes expensive later.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Would I be comfortable debugging this at 2 AM?
&lt;/h2&gt;

&lt;p&gt;This is one of my favorite review questions.&lt;/p&gt;

&lt;p&gt;Because code can be technically correct and still be operationally terrible.&lt;/p&gt;

&lt;p&gt;I look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;meaningful logs&lt;/li&gt;
&lt;li&gt;useful error messages&lt;/li&gt;
&lt;li&gt;predictable branching&lt;/li&gt;
&lt;li&gt;obvious control flow&lt;/li&gt;
&lt;li&gt;easy-to-trace data transformations&lt;/li&gt;
&lt;li&gt;no “magic” hidden in helpers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If production breaks, will this code help the next person understand what happened?&lt;/p&gt;

&lt;p&gt;Or will it force them to reverse-engineer AI-generated cleverness under pressure?&lt;/p&gt;

&lt;p&gt;If debugging would be painful, I simplify the code before merge.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Is the security model still intact?
&lt;/h2&gt;

&lt;p&gt;Any AI-generated PR that touches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;auth&lt;/li&gt;
&lt;li&gt;permissions&lt;/li&gt;
&lt;li&gt;tokens&lt;/li&gt;
&lt;li&gt;cookies&lt;/li&gt;
&lt;li&gt;headers&lt;/li&gt;
&lt;li&gt;uploads&lt;/li&gt;
&lt;li&gt;database access&lt;/li&gt;
&lt;li&gt;redirects&lt;/li&gt;
&lt;li&gt;HTML rendering&lt;/li&gt;
&lt;li&gt;shell commands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;gets a slower review.&lt;/p&gt;

&lt;p&gt;I specifically check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;authorization, not just authentication&lt;/li&gt;
&lt;li&gt;input handling&lt;/li&gt;
&lt;li&gt;secret leakage&lt;/li&gt;
&lt;li&gt;unsafe defaults&lt;/li&gt;
&lt;li&gt;overly broad permissions&lt;/li&gt;
&lt;li&gt;accidental exposure of internal fields&lt;/li&gt;
&lt;li&gt;client/server boundary mistakes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I do not trust “looks secure.”&lt;/p&gt;

&lt;p&gt;I want the security assumptions to be obvious in the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Can I roll this back easily?
&lt;/h2&gt;

&lt;p&gt;This final check is underrated.&lt;/p&gt;

&lt;p&gt;Even a good change can fail in production.&lt;/p&gt;

&lt;p&gt;So before merging I ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is the change isolated?&lt;/li&gt;
&lt;li&gt;is it behind a flag?&lt;/li&gt;
&lt;li&gt;can it be reverted cleanly?&lt;/li&gt;
&lt;li&gt;does it change data shape or persistence behavior?&lt;/li&gt;
&lt;li&gt;does it create migration risk?&lt;/li&gt;
&lt;li&gt;does it depend on coordinated deployment?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI makes it easy to create larger patches than necessary.&lt;br&gt;
Rollback thinking forces the patch back into a safer shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  My quick merge rubric
&lt;/h2&gt;

&lt;p&gt;If I need a fast decision, I use this simple rubric.&lt;/p&gt;

&lt;h3&gt;
  
  
  I merge when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;the scope is narrow&lt;/li&gt;
&lt;li&gt;the intent is obvious&lt;/li&gt;
&lt;li&gt;the boundaries are typed&lt;/li&gt;
&lt;li&gt;runtime input is validated&lt;/li&gt;
&lt;li&gt;tests prove the claimed behavior&lt;/li&gt;
&lt;li&gt;failure paths are handled&lt;/li&gt;
&lt;li&gt;naming is clear&lt;/li&gt;
&lt;li&gt;security assumptions are visible&lt;/li&gt;
&lt;li&gt;rollback is simple&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  I request changes when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;the PR does more than requested&lt;/li&gt;
&lt;li&gt;the code adds unnecessary abstractions&lt;/li&gt;
&lt;li&gt;tests are shallow&lt;/li&gt;
&lt;li&gt;boundary validation is missing&lt;/li&gt;
&lt;li&gt;names are vague&lt;/li&gt;
&lt;li&gt;duplicate logic appears&lt;/li&gt;
&lt;li&gt;debugging would be painful&lt;/li&gt;
&lt;li&gt;the operational or security story is unclear&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;AI can absolutely make teams faster.&lt;/p&gt;

&lt;p&gt;But speed only matters if the output is reviewable, understandable, and safe to ship.&lt;/p&gt;

&lt;p&gt;That is why I do not ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Did AI write this?”&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;blockquote&gt;
&lt;p&gt;“Would I still approve this if I had to own it in production?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That single question has improved my reviews more than any tool setting.&lt;/p&gt;

&lt;p&gt;If you are using AI heavily in your TypeScript workflow, build a checklist like this one.&lt;/p&gt;

&lt;p&gt;It does not have to be identical.&lt;/p&gt;

&lt;p&gt;It just has to be consistent.&lt;/p&gt;

&lt;p&gt;Because the real productivity gain is not generated code.&lt;/p&gt;

&lt;p&gt;It is generated code that survives review without creating future pain.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I recommend this!</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Tue, 28 Apr 2026 16:37:34 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/i-recommend-this-1bo7</link>
      <guid>https://dev.to/johnnylemonny/i-recommend-this-1bo7</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/georgekobaidze/15-essential-sections-every-readme-needs-give-your-project-what-it-deserves-fie" class="crayons-story__hidden-navigation-link"&gt;15 Essential Sections Every README Needs: Give Your Project What It Deserves&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/georgekobaidze/15-essential-sections-every-readme-needs-give-your-project-what-it-deserves-fie" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Ready-to-use markdown template&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/georgekobaidze" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F55651%2F29e2a161-9d78-410b-a6e5-9aca17092fa3.jpeg" alt="georgekobaidze profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/georgekobaidze" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Giorgi Kobaidze
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Giorgi Kobaidze
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
              
              &lt;div id="story-author-preview-content-3553477" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/georgekobaidze" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F55651%2F29e2a161-9d78-410b-a6e5-9aca17092fa3.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Giorgi Kobaidze&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/georgekobaidze/15-essential-sections-every-readme-needs-give-your-project-what-it-deserves-fie" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 26&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/georgekobaidze/15-essential-sections-every-readme-needs-give-your-project-what-it-deserves-fie" id="article-link-3553477"&gt;
          15 Essential Sections Every README Needs: Give Your Project What It Deserves
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/documentation"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;documentation&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/development"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;development&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/georgekobaidze/15-essential-sections-every-readme-needs-give-your-project-what-it-deserves-fie" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;157&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/georgekobaidze/15-essential-sections-every-readme-needs-give-your-project-what-it-deserves-fie#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              80&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            11 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

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

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

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>How I Started Learning Kotlin by Building a Real Android App</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Wed, 22 Apr 2026 09:20:00 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/how-i-started-learning-kotlin-by-building-a-real-android-app-32ic</link>
      <guid>https://dev.to/johnnylemonny/how-i-started-learning-kotlin-by-building-a-real-android-app-32ic</guid>
      <description>&lt;p&gt;For a long time, Kotlin was one of those technologies I kept meaning to learn “properly.”&lt;/p&gt;

&lt;p&gt;You probably know the feeling.&lt;/p&gt;

&lt;p&gt;You read a few examples.&lt;br&gt;&lt;br&gt;
You understand the syntax.&lt;br&gt;&lt;br&gt;
You save a few tutorials.&lt;br&gt;&lt;br&gt;
You tell yourself you’ll build something with it “soon.”&lt;/p&gt;

&lt;p&gt;And then nothing happens.&lt;/p&gt;

&lt;p&gt;That was me for a while.&lt;/p&gt;

&lt;p&gt;The problem wasn’t that Kotlin looked hard.&lt;br&gt;&lt;br&gt;
The problem was that learning it in isolation felt abstract.&lt;/p&gt;

&lt;p&gt;I could read about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;null safety,&lt;/li&gt;
&lt;li&gt;data classes,&lt;/li&gt;
&lt;li&gt;coroutines,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;when&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;extension functions,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;but none of it really &lt;em&gt;stuck&lt;/em&gt; until I had a reason to use it inside a real Android app.&lt;/p&gt;

&lt;p&gt;That’s when things changed.&lt;/p&gt;

&lt;p&gt;Instead of trying to “finish a Kotlin course,” I decided to learn Kotlin by building something real — small enough to complete, but real enough to force me to understand how Android development actually works.&lt;/p&gt;

&lt;p&gt;This post is about what that approach taught me, what slowed me down, and what I’d focus on first if I started learning Kotlin again today.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I chose to learn Kotlin by building instead of studying longer
&lt;/h2&gt;

&lt;p&gt;At some point I realized I was consuming too much “learning material” and not enough actual friction.&lt;/p&gt;

&lt;p&gt;And friction is where real learning starts.&lt;/p&gt;

&lt;p&gt;It is easy to feel productive when you are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reading docs,&lt;/li&gt;
&lt;li&gt;watching tutorials,&lt;/li&gt;
&lt;li&gt;highlighting syntax,&lt;/li&gt;
&lt;li&gt;copying code examples.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But you don’t really understand a language until you hit questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How should I structure this screen?&lt;/li&gt;
&lt;li&gt;Where should this logic live?&lt;/li&gt;
&lt;li&gt;Why is this nullable?&lt;/li&gt;
&lt;li&gt;Why does this state not update the way I expected?&lt;/li&gt;
&lt;li&gt;What is the most Kotlin-like way to write this instead of just translating Java or JavaScript habits?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building an app forces those questions to happen naturally.&lt;/p&gt;

&lt;p&gt;It also gives you a much better feedback loop:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;write code,&lt;/li&gt;
&lt;li&gt;break something,&lt;/li&gt;
&lt;li&gt;fix it,&lt;/li&gt;
&lt;li&gt;understand one concept more deeply,&lt;/li&gt;
&lt;li&gt;repeat.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That loop taught me more than another week of passive learning would have.&lt;/p&gt;




&lt;h2&gt;
  
  
  The kind of app I built
&lt;/h2&gt;

&lt;p&gt;I did &lt;strong&gt;not&lt;/strong&gt; start with a big idea.&lt;/p&gt;

&lt;p&gt;I didn’t try to build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a full social app,&lt;/li&gt;
&lt;li&gt;an e-commerce platform,&lt;/li&gt;
&lt;li&gt;a chat system,&lt;/li&gt;
&lt;li&gt;or a feature-packed productivity tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That would have been a mistake.&lt;/p&gt;

&lt;p&gt;Instead, I picked something small and realistic:&lt;br&gt;
a simple Android app with a few clear screens, local state, user input, and enough structure to feel like a real product.&lt;/p&gt;

&lt;p&gt;That mattered a lot.&lt;/p&gt;

&lt;p&gt;A good beginner project should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;small enough to finish,&lt;/li&gt;
&lt;li&gt;useful enough to stay motivating,&lt;/li&gt;
&lt;li&gt;and complex enough to teach real patterns.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to impress people with scope.&lt;/p&gt;

&lt;p&gt;The goal is to create a project that forces you to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kotlin syntax in real conditions,&lt;/li&gt;
&lt;li&gt;Android UI structure,&lt;/li&gt;
&lt;li&gt;state handling,&lt;/li&gt;
&lt;li&gt;user interactions,&lt;/li&gt;
&lt;li&gt;navigation,&lt;/li&gt;
&lt;li&gt;and a bit of architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s where the language starts making sense.&lt;/p&gt;




&lt;h2&gt;
  
  
  What helped me most when learning Kotlin
&lt;/h2&gt;

&lt;p&gt;A few things made a huge difference early on.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Writing Kotlin every day, even in small amounts
&lt;/h3&gt;

&lt;p&gt;I learned very quickly that consistency mattered more than long study sessions.&lt;/p&gt;

&lt;p&gt;A focused 30–45 minutes of building something real helped me much more than occasionally spending 4 hours jumping between tutorials.&lt;/p&gt;

&lt;p&gt;Kotlin started to feel natural only after repeated exposure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;writing functions,&lt;/li&gt;
&lt;li&gt;handling nullable values,&lt;/li&gt;
&lt;li&gt;creating data models,&lt;/li&gt;
&lt;li&gt;wiring UI interactions,&lt;/li&gt;
&lt;li&gt;refactoring small pieces of code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first, even simple things felt slower than they should.&lt;/p&gt;

&lt;p&gt;But that’s normal.&lt;/p&gt;

&lt;p&gt;The important part was not speed.&lt;br&gt;&lt;br&gt;
It was repetition.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Accepting that Kotlin is not just “better Java”
&lt;/h3&gt;

&lt;p&gt;This was one of the biggest mental shifts.&lt;/p&gt;

&lt;p&gt;If you come from another language, especially something like Java, JavaScript, or TypeScript, it is tempting to treat Kotlin as “the same thing with nicer syntax.”&lt;/p&gt;

&lt;p&gt;That mindset slows you down.&lt;/p&gt;

&lt;p&gt;Kotlin becomes much more enjoyable when you stop translating your old habits directly and start asking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is the idiomatic Kotlin way to do this?&lt;/li&gt;
&lt;li&gt;Why does this language encourage immutability here?&lt;/li&gt;
&lt;li&gt;Why is null handling so explicit?&lt;/li&gt;
&lt;li&gt;Why do data classes feel so natural in app development?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The more I leaned into Kotlin as its own language, the easier it became to write cleaner code.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Building features instead of collecting concepts
&lt;/h3&gt;

&lt;p&gt;At the beginning, I thought I needed to “learn coroutines,” “learn data classes,” or “learn sealed classes” one by one.&lt;/p&gt;

&lt;p&gt;That was not the best approach for me.&lt;/p&gt;

&lt;p&gt;What worked better was learning those things inside real features.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;user input taught me about state,&lt;/li&gt;
&lt;li&gt;screen transitions taught me about navigation,&lt;/li&gt;
&lt;li&gt;modeling items taught me about data classes,&lt;/li&gt;
&lt;li&gt;error handling taught me about nullability,&lt;/li&gt;
&lt;li&gt;asynchronous work pushed me toward coroutines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That approach made the concepts feel useful immediately.&lt;/p&gt;

&lt;p&gt;And useful concepts are easier to remember than isolated definitions.&lt;/p&gt;




&lt;h2&gt;
  
  
  What confused me the most at first
&lt;/h2&gt;

&lt;p&gt;Learning Kotlin through a real Android project was helpful — but not frictionless.&lt;/p&gt;

&lt;p&gt;A few things definitely slowed me down.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Null safety felt great… until I had to actually design around it
&lt;/h3&gt;

&lt;p&gt;At first, null safety looked like one of Kotlin’s cleanest features.&lt;/p&gt;

&lt;p&gt;And it is.&lt;/p&gt;

&lt;p&gt;But it also forced me to become more deliberate.&lt;/p&gt;

&lt;p&gt;Instead of casually passing values around and “handling it later,” I had to think earlier about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what can actually be null,&lt;/li&gt;
&lt;li&gt;what should be required,&lt;/li&gt;
&lt;li&gt;what should have a default value,&lt;/li&gt;
&lt;li&gt;and where I was making assumptions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was frustrating at first because it exposed weak decisions immediately.&lt;/p&gt;

&lt;p&gt;But looking back, that was a good thing.&lt;/p&gt;

&lt;p&gt;Kotlin was not making development harder.&lt;br&gt;&lt;br&gt;
It was making sloppy assumptions harder.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Android structure is part of the learning curve, not just Kotlin
&lt;/h3&gt;

&lt;p&gt;This is important.&lt;/p&gt;

&lt;p&gt;When you learn Kotlin for Android, you are not learning just one thing.&lt;/p&gt;

&lt;p&gt;You are learning at least three layers at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kotlin itself,&lt;/li&gt;
&lt;li&gt;Android concepts,&lt;/li&gt;
&lt;li&gt;and your chosen UI / architecture approach.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means confusion is not always caused by the language.&lt;/p&gt;

&lt;p&gt;Sometimes the real question is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this a Kotlin issue?&lt;/li&gt;
&lt;li&gt;an Android lifecycle issue?&lt;/li&gt;
&lt;li&gt;a UI state issue?&lt;/li&gt;
&lt;li&gt;a project structure issue?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once I understood that, I stopped blaming Kotlin for every confusing moment.&lt;/p&gt;

&lt;p&gt;A lot of beginner frustration actually comes from trying to learn the whole Android stack at once.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. There is a difference between “it works” and “this is clean”
&lt;/h3&gt;

&lt;p&gt;One of the biggest lessons was realizing how easy it is to build something that works… and how much harder it is to build something that still feels clean after the third or fourth feature.&lt;/p&gt;

&lt;p&gt;Early in the project, I often wrote code like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;directly inside the screen,&lt;/li&gt;
&lt;li&gt;with too much logic in one place,&lt;/li&gt;
&lt;li&gt;and without thinking much about separation of concerns.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That got me moving quickly, which was good.&lt;/p&gt;

&lt;p&gt;But after a while, I could feel the cost:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;harder refactors,&lt;/li&gt;
&lt;li&gt;messier UI code,&lt;/li&gt;
&lt;li&gt;less confidence when adding features.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was actually one of the most valuable parts of the project.&lt;/p&gt;

&lt;p&gt;It taught me that learning Kotlin is not just about syntax.&lt;br&gt;&lt;br&gt;
It is also about learning where code should live.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Kotlin features that clicked fastest for me
&lt;/h2&gt;

&lt;p&gt;Some Kotlin features made sense almost immediately once I used them in a real app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data classes
&lt;/h3&gt;

&lt;p&gt;These were one of the quickest wins.&lt;/p&gt;

&lt;p&gt;As soon as I started modeling app data, data classes felt obvious and useful.&lt;/p&gt;

&lt;p&gt;They reduce boilerplate and make data easier to reason about.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;when&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This felt much cleaner than long conditional chains.&lt;/p&gt;

&lt;p&gt;It became especially useful for handling UI states and branching logic in a more readable way.&lt;/p&gt;




&lt;h3&gt;
  
  
  Null safety
&lt;/h3&gt;

&lt;p&gt;Even though it was frustrating at first, it made my code more intentional.&lt;/p&gt;

&lt;p&gt;Once I stopped fighting it, I started trusting my code more.&lt;/p&gt;




&lt;h3&gt;
  
  
  Immutability by default
&lt;/h3&gt;

&lt;p&gt;This changed how I thought about state.&lt;/p&gt;

&lt;p&gt;When building UI, predictable state matters a lot.&lt;br&gt;&lt;br&gt;
Kotlin nudged me toward better habits there.&lt;/p&gt;




&lt;h3&gt;
  
  
  Extension functions
&lt;/h3&gt;

&lt;p&gt;These made code feel more expressive once I understood where they were actually useful.&lt;/p&gt;

&lt;p&gt;Not “clever” useful — but genuinely cleaner in small repeated patterns.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I would focus on first if I started again
&lt;/h2&gt;

&lt;p&gt;If I had to restart from zero today, I would not try to learn everything.&lt;/p&gt;

&lt;p&gt;I would focus on this order:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Core Kotlin basics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;variables&lt;/li&gt;
&lt;li&gt;functions&lt;/li&gt;
&lt;li&gt;classes&lt;/li&gt;
&lt;li&gt;nullability&lt;/li&gt;
&lt;li&gt;collections&lt;/li&gt;
&lt;li&gt;control flow&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Kotlin features that matter quickly in apps
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;data classes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;when&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;immutable patterns&lt;/li&gt;
&lt;li&gt;simple extension functions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Basic Android app flow
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;screens&lt;/li&gt;
&lt;li&gt;user input&lt;/li&gt;
&lt;li&gt;state&lt;/li&gt;
&lt;li&gt;navigation&lt;/li&gt;
&lt;li&gt;local data&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Code organization
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;separating UI from logic&lt;/li&gt;
&lt;li&gt;keeping files readable&lt;/li&gt;
&lt;li&gt;avoiding giant screens or giant classes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Then only later: deeper architecture and advanced patterns
&lt;/h3&gt;

&lt;p&gt;I would avoid over-optimizing too early.&lt;/p&gt;

&lt;p&gt;The first milestone should not be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Build the perfect architecture.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It should be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Build something working, understandable, and easy enough to improve.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That makes everything else easier.&lt;/p&gt;




&lt;h2&gt;
  
  
  My biggest takeaway
&lt;/h2&gt;

&lt;p&gt;The biggest thing I learned is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Kotlin became much easier once I stopped trying to “master Kotlin” and started trying to build something with it.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;A real app gave every concept a reason to exist.&lt;/p&gt;

&lt;p&gt;Without a project, Kotlin was just a list of language features.&lt;/p&gt;

&lt;p&gt;Inside a project, it became:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a way to model data,&lt;/li&gt;
&lt;li&gt;a way to structure UI logic,&lt;/li&gt;
&lt;li&gt;a way to write safer code,&lt;/li&gt;
&lt;li&gt;and a way to think more clearly about app development.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s when learning started to feel real.&lt;/p&gt;




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

&lt;p&gt;If you are trying to learn Kotlin right now, my honest advice is:&lt;/p&gt;

&lt;p&gt;Don’t wait until you feel “ready.”&lt;/p&gt;

&lt;p&gt;Don’t try to complete every tutorial first.&lt;/p&gt;

&lt;p&gt;Build something small.&lt;br&gt;&lt;br&gt;
Make it real.&lt;br&gt;&lt;br&gt;
Let the project expose the gaps in your understanding.&lt;/p&gt;

&lt;p&gt;That is where the best learning happens.&lt;/p&gt;

&lt;p&gt;Not when everything is clear.&lt;/p&gt;

&lt;p&gt;But when the code forces you to ask better questions.&lt;/p&gt;




&lt;p&gt;If you’re learning Kotlin too, I’d love to know:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What has been harder for you so far — the language itself, Android concepts, or figuring out how to structure a real app?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>android</category>
      <category>beginners</category>
      <category>mobile</category>
    </item>
    <item>
      <title>NatureNode: An AI-Powered Biodiversity Research Tool for Earth Day</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Sat, 18 Apr 2026 09:30:00 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/naturenode-an-ai-powered-biodiversity-research-tool-for-earth-day-35ko</link>
      <guid>https://dev.to/johnnylemonny/naturenode-an-ai-powered-biodiversity-research-tool-for-earth-day-35ko</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/weekend-challenge-2026-04-16"&gt;Weekend Challenge: Earth Day Edition&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;NatureNode&lt;/strong&gt; is a professional biodiversity research tool and Progressive Web App that transforms a smartphone photo into a comprehensive ecological dossier — instantly, in the field, with no backend and full offline capability.&lt;/p&gt;

&lt;p&gt;Most identification apps give you a name and a pretty photo. NatureNode gives you what a field researcher actually needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔬 &lt;strong&gt;Full scientific taxonomy&lt;/strong&gt; — family, genus, species&lt;/li&gt;
&lt;li&gt;🌍 &lt;strong&gt;Conservation status&lt;/strong&gt; (IUCN scale) and native range&lt;/li&gt;
&lt;li&gt;♻️ &lt;strong&gt;Ecological role&lt;/strong&gt; — what this organism contributes to its ecosystem&lt;/li&gt;
&lt;li&gt;🛡️ &lt;strong&gt;Protection guidelines&lt;/strong&gt; — actionable tips to conserve what you found&lt;/li&gt;
&lt;li&gt;📓 &lt;strong&gt;Specimen Journal&lt;/strong&gt; — a persistent local database of all your discoveries&lt;/li&gt;
&lt;li&gt;🗺️ &lt;strong&gt;Location tagging&lt;/strong&gt; + one-tap Google Maps navigation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The design philosophy matched the ambition: dark botanical aesthetics, high information density, and interactions that feel deliberate — like a research dashboard, not a social app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1attjgova8v2yd1b9m59.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1attjgova8v2yd1b9m59.webp" alt="NatureNode — Desktop Analysis View" width="800" height="1154"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;🌿 &lt;strong&gt;Live PWA:&lt;/strong&gt; &lt;a href="https://johnnylemonny.github.io/naturenode/" rel="noopener noreferrer"&gt;https://johnnylemonny.github.io/naturenode/&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;To try it:&lt;/strong&gt; Get a free Gemini API key at &lt;a href="https://aistudio.google.com/app/apikey" rel="noopener noreferrer"&gt;Google AI Studio&lt;/a&gt;, paste it into the app, and upload any photo of a plant, insect, animal, or mushroom. Your key is stored securely in your browser — never on any server.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/johnnylemonny" rel="noopener noreferrer"&gt;
        johnnylemonny
      &lt;/a&gt; / &lt;a href="https://github.com/johnnylemonny/naturenode" rel="noopener noreferrer"&gt;
        naturenode
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      🌿 NatureNode: A premium Progressive Web App (PWA) for advanced biodiversity identification. Powered by Google Gemini AI, featuring professional field research tools, specimen journaling, and ecological conservation insights. Built as part of the DEV Weekend Challenge.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🌿 NatureNode&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://react.dev/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b46bfccb78b3b4cd9b05aaf834f04a661af853e66dafff4cb9bfe11d685ee5b8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f52656163742d31392d626c7565" alt="React"&gt;&lt;/a&gt;
&lt;a href="https://tailwindcss.com/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/53b4c18dfa6a79aecb0effa1cfa88ac1e6f792f3dfbdd336eb6a8df6f293bc43/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5461696c77696e642d342d333862646638" alt="Tailwind CSS"&gt;&lt;/a&gt;
&lt;a href="https://ai.google.dev/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/8383ade6b338d8653b967512384d9a4f83b5311e0901388a647f03b0ced79795/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f47656d696e692532304150492d33253230466c6173682d6f72616e6765" alt="Gemini API"&gt;&lt;/a&gt;
&lt;a href="https://vite-pwa-org.netlify.app/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/58d1f53b9fd14b05947f0432d3dc3cb4920b4e546a2439362dfa5cf423641179/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5057412d52656164792d677265656e" alt="PWA"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NatureNode&lt;/strong&gt; is a professional biodiversity research and identification tool. Designed for field researchers and nature enthusiasts, it leverages state-of-the-art AI to transform simple photos into comprehensive ecological dossiers.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/johnnylemonny/naturenode/./photos/desktop3.webp"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fjohnnylemonny%2Fnaturenode%2FHEAD%2F.%2Fphotos%2Fdesktop3.webp" alt="NatureNode Desktop Interface"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🌟 Key Features&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Botanical Precision UI&lt;/strong&gt;: A high-density, research-oriented design system using Tailwind CSS v4 and the OKLCH color model for superior visual clarity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-Driven Identification&lt;/strong&gt;: Powered by &lt;strong&gt;Google Gemini 3 Flash&lt;/strong&gt; for instant, high-accuracy recognition of plants, animals, insects, and fungi.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specimen Journal&lt;/strong&gt;: A persistent, local history of all your discoveries, allowing you to build your own personal biodiversity database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📱 PWA &amp;amp; Offline Support&lt;/strong&gt;: Fully installable as a Progressive Web App. Designed to work in the field with robust offline capabilities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🗺️ Manual Location Mapping&lt;/strong&gt;: Easily log observation points by town or area name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📍 Google Maps Integration&lt;/strong&gt;: One-click navigation and mapping of find locations directly in Google Maps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ecological Insights&lt;/strong&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/johnnylemonny/naturenode" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;





&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Architecture: Zero-Backend, Privacy-First PWA
&lt;/h3&gt;

&lt;p&gt;The core constraint was: &lt;strong&gt;no backend, no running costs, no privacy compromise&lt;/strong&gt;. Everything that isn't AI inference runs locally in the browser. The Gemini API call goes directly from the user's browser to Google's servers using their own key — a "Bring Your Own Key" model that is the most honest approach to privacy-first AI tooling possible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Browser
├── React 19 + TypeScript          # UI &amp;amp; state management
├── Vite 8 (Rolldown engine)       # Lightning-fast builds
├── Tailwind CSS v4 (OKLCH model)  # Design system with perceptual color
├── Vite PWA Plugin + Workbox      # Offline support &amp;amp; installability
└── @google/generative-ai SDK      # Direct browser → Gemini calls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Gemini Integration: Typed JSON Schema
&lt;/h3&gt;

&lt;p&gt;The intelligence layer is engineered around Gemini's &lt;code&gt;responseSchema&lt;/code&gt; API — instead of parsing or validating free-form AI text, the model is constrained to return exactly the data shape that TypeScript expects. Zero parsing errors, deterministic structure:&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;responseSchema&lt;/span&gt; &lt;span class="o"&gt;=&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="nx"&gt;SchemaType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OBJECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;commonName&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;SchemaType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRING&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;scientificName&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;SchemaType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRING&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;conservationStatus&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;SchemaType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRING&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;ecologicalRole&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;SchemaType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRING&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;protectionTips&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;SchemaType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&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="nx"&gt;SchemaType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRING&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&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;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;genAI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getGenerativeModel&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-3-flash-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;generationConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;responseMimeType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;responseSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;// ← model is constrained to this shape&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 approach is more reliable than prompt-engineering your way to JSON and faster than asking the model to explain its reasoning. The result: sub-3-second identifications with zero unparseable responses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secure API Key Storage
&lt;/h3&gt;

&lt;p&gt;GitHub's CodeQL scanner flagged clear-text storage of the user's key in &lt;code&gt;localStorage&lt;/code&gt;. The fix: Base64 obfuscation with a generic storage key name. Not encryption — but it prevents the key from sitting as a plain string visible in browser dev tools, and satisfies static analysis:&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;obfuscate&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;btoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;unescape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&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;deobfuscate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;decodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;atob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;

&lt;span class="c1"&gt;// Save&lt;/span&gt;
&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nn_secure_session&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;obfuscate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Load&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nn_secure_session&lt;/span&gt;&lt;span class="dl"&gt;"&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;raw&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;deobfuscate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  PWA &amp;amp; Offline
&lt;/h3&gt;

&lt;p&gt;Workbox precaches all static assets at build time — the shell, fonts, and full UI load instantly even with no connection. Only the Gemini API call requires internet, and the app communicates this clearly. The manifest enables full "Add to Home Screen" installability on both Android and iOS.&lt;/p&gt;

&lt;h3&gt;
  
  
  CI/CD Pipeline
&lt;/h3&gt;

&lt;p&gt;Three GitHub Actions workflows run on every push:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workflow&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deploy.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;actions/deploy-pages@v4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build → GitHub Pages (no branch needed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;linter.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Super-Linter slim v7&lt;/td&gt;
&lt;td&gt;JS / TS / HTML / JSON quality gates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;codeql.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CodeQL&lt;/td&gt;
&lt;td&gt;Security vulnerability scanning&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5u1467fb5oui9rwrq8ir.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5u1467fb5oui9rwrq8ir.webp" alt="NatureNode — Desktop Hero (Upload State)" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;🥇 Best Use of Google Gemini&lt;/strong&gt; — Gemini 3 Flash Preview is the entire intelligence layer, used with typed JSON schema enforcement for production-grade, zero-parse AI responses delivered directly from browser to API.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
    </item>
    <item>
      <title>5 Developer Trends That Actually Matter in 2026 (Not Just the Hype)</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Thu, 16 Apr 2026 12:45:32 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/5-developer-trends-that-actually-matter-in-2026-not-just-the-hype-4g86</link>
      <guid>https://dev.to/johnnylemonny/5-developer-trends-that-actually-matter-in-2026-not-just-the-hype-4g86</guid>
      <description>&lt;p&gt;Every year, developers get flooded with trend lists.&lt;/p&gt;

&lt;p&gt;Most of them are either too broad to be useful or too early to matter in real projects.&lt;/p&gt;

&lt;p&gt;So instead of listing every shiny new thing, I want to focus on the shifts that are already changing how modern apps are built, shipped, and maintained &lt;strong&gt;right now&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Because 2026 does feel different.&lt;/p&gt;

&lt;p&gt;GitHub’s latest Octoverse data says more than &lt;strong&gt;1.1 million public repositories now use an LLM SDK&lt;/strong&gt;, with &lt;strong&gt;693,867&lt;/strong&gt; of those created in the last 12 months alone. It also reports that &lt;strong&gt;TypeScript became the most-used language on GitHub in August 2025&lt;/strong&gt;, overtaking both Python and JavaScript. &lt;a href="https://github.blog/news-insights/octoverse/octoverse-a-new-developer-joins-github-every-second-as-ai-leads-typescript-to-1/" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is not just noise. That is a signal.&lt;/p&gt;

&lt;p&gt;Here are the five trends I think actually matter in 2026 — and what developers should do about them.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. AI coding agents are moving beyond autocomplete
&lt;/h2&gt;

&lt;p&gt;For a while, AI coding tools were mostly “fancy autocomplete.”&lt;/p&gt;

&lt;p&gt;That is no longer the right mental model.&lt;/p&gt;

&lt;p&gt;Anthropic’s 2026 Agentic Coding Trends Report argues that in 2025, coding agents moved from experimental tools to systems that can handle real implementation workflows, including writing tests, debugging, generating docs, and navigating large codebases. At the same time, the report notes an important nuance: developers may use AI in roughly &lt;strong&gt;60% of their work&lt;/strong&gt;, but they still report being able to &lt;strong&gt;fully delegate only 0–20% of tasks&lt;/strong&gt;. &lt;a href="https://resources.anthropic.com/hubfs/2026%20Agentic%20Coding%20Trends%20Report.pdf" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That means the real shift is not “AI replaces developers.”&lt;/p&gt;

&lt;p&gt;The real shift is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;developers are becoming managers of execution loops.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Can the tool write this function?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;we now ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Can the agent plan this task?”&lt;/li&gt;
&lt;li&gt;“Can it inspect the codebase safely?”&lt;/li&gt;
&lt;li&gt;“Can it run tests and recover from failure?”&lt;/li&gt;
&lt;li&gt;“Can it return something reviewable?”&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What this means in practice
&lt;/h3&gt;

&lt;p&gt;The best teams will stop treating AI as a tab in the IDE and start treating it as an &lt;strong&gt;async collaborator&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That changes what matters in your codebase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clean file structure,&lt;/li&gt;
&lt;li&gt;consistent naming,&lt;/li&gt;
&lt;li&gt;reliable tests,&lt;/li&gt;
&lt;li&gt;good docs,&lt;/li&gt;
&lt;li&gt;and explicit constraints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Messy code used to slow down humans.&lt;br&gt;&lt;br&gt;
Now it also slows down agents.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. MCP is becoming the integration layer developers should pay attention to
&lt;/h2&gt;

&lt;p&gt;One of the biggest shifts happening quietly is the rise of &lt;strong&gt;MCP&lt;/strong&gt; — the &lt;strong&gt;Model Context Protocol&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Cloudflare describes MCP as an &lt;strong&gt;open standard&lt;/strong&gt; that connects AI systems to external applications, comparing it to a “USB-C port for AI applications.” Vercel describes it similarly as a standard interface that lets LLMs communicate with external tools and data sources. &lt;a href="https://developers.cloudflare.com/agents/model-context-protocol/" rel="noopener noreferrer"&gt;3&lt;/a&gt;&lt;a href="https://vercel.com/docs/mcp" rel="noopener noreferrer"&gt;5&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why does that matter?&lt;/p&gt;

&lt;p&gt;Because once agents become useful, the next bottleneck is &lt;strong&gt;tool access&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;databases,&lt;/li&gt;
&lt;li&gt;issue trackers,&lt;/li&gt;
&lt;li&gt;deployment platforms,&lt;/li&gt;
&lt;li&gt;logs,&lt;/li&gt;
&lt;li&gt;docs,&lt;/li&gt;
&lt;li&gt;cloud resources,&lt;/li&gt;
&lt;li&gt;and internal APIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MCP is interesting because it reduces custom glue code and pushes the ecosystem toward a common contract.&lt;/p&gt;

&lt;p&gt;And this is no longer theoretical.&lt;/p&gt;

&lt;p&gt;Cloudflare explicitly supports building and deploying MCP servers. Vercel has its own official MCP server and documents support for clients including Claude, ChatGPT, Cursor, VS Code with Copilot, Codex CLI, Windsurf, and more. &lt;a href="https://developers.cloudflare.com/agents/model-context-protocol/" rel="noopener noreferrer"&gt;3&lt;/a&gt;&lt;a href="https://vercel.com/docs/agent-resources/vercel-mcp" rel="noopener noreferrer"&gt;4&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What this means in practice
&lt;/h3&gt;

&lt;p&gt;If you build developer tools, internal platforms, APIs, or anything that might be used by agents, you should already be asking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should this expose an MCP interface?&lt;/li&gt;
&lt;li&gt;What tools would actually be safe and useful for an agent?&lt;/li&gt;
&lt;li&gt;What permissions should be scoped tightly?&lt;/li&gt;
&lt;li&gt;What tasks should remain human-only?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In 2026, “AI-ready” increasingly means “toolable by standards.”&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Type safety and boring developer experience are becoming competitive advantages
&lt;/h2&gt;

&lt;p&gt;One of the most underrated trends right now is that &lt;strong&gt;typed and predictable systems are becoming more valuable&lt;/strong&gt;, not less.&lt;/p&gt;

&lt;p&gt;GitHub’s Octoverse says TypeScript overtook both Python and JavaScript in August 2025 to become the most-used language on GitHub, calling it the most significant language shift in more than a decade. GitHub also ties that rise to agent-assisted coding, arguing that typed languages make production work more reliable. &lt;a href="https://github.blog/news-insights/octoverse/octoverse-a-new-developer-joins-github-every-second-as-ai-leads-typescript-to-1/" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This matches what many teams are noticing already:&lt;/p&gt;

&lt;p&gt;When humans and agents work together, ambiguity becomes expensive.&lt;/p&gt;

&lt;p&gt;That is why “boring” qualities are suddenly strategic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;strong types,&lt;/li&gt;
&lt;li&gt;explicit contracts,&lt;/li&gt;
&lt;li&gt;stable interfaces,&lt;/li&gt;
&lt;li&gt;predictable scripts,&lt;/li&gt;
&lt;li&gt;clear schemas,&lt;/li&gt;
&lt;li&gt;readable errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your stack is easy to reason about, it is easier to automate, test, review, and evolve.&lt;/p&gt;

&lt;h3&gt;
  
  
  What this means in practice
&lt;/h3&gt;

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

&lt;blockquote&gt;
&lt;p&gt;“What is the trendiest stack?”&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;blockquote&gt;
&lt;p&gt;“What stack minimizes ambiguity for both humans and machines?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is one reason TypeScript-heavy workflows feel stronger right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;they reduce guessing,&lt;/li&gt;
&lt;li&gt;improve refactors,&lt;/li&gt;
&lt;li&gt;and make AI-assisted changes less fragile.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In 2026, good DX is no longer just a human experience.&lt;br&gt;&lt;br&gt;
It is also an &lt;strong&gt;agent experience&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Passkeys are moving from “nice idea” to default modern authentication
&lt;/h2&gt;

&lt;p&gt;Authentication is also going through a real shift.&lt;/p&gt;

&lt;p&gt;Passkeys are no longer just something security people talk about at conferences.&lt;/p&gt;

&lt;p&gt;The FIDO Alliance describes passkeys as phishing-resistant credentials based on FIDO standards that let users sign in with the same process they use to unlock their device. FIDO also says passkeys lead to &lt;strong&gt;20% more successful sign-ins over passwords&lt;/strong&gt;, and reports that &lt;strong&gt;53% of people have enabled passkeys on at least one account&lt;/strong&gt;, while &lt;strong&gt;22% enable them everywhere they can&lt;/strong&gt;. &lt;a href="https://fidoalliance.org/passkeys/" rel="noopener noreferrer"&gt;6&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the implementation side, Microsoft now documents passkeys in ASP.NET Core Identity, and MDN describes WebAuthn as a widely available browser capability that enables strong authentication with public key cryptography. &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/security/authentication/passkeys/?view=aspnetcore-10.0" rel="noopener noreferrer"&gt;7&lt;/a&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API" rel="noopener noreferrer"&gt;8&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That makes passkeys relevant far beyond enterprise identity teams.&lt;/p&gt;

&lt;p&gt;They now matter to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SaaS builders,&lt;/li&gt;
&lt;li&gt;indie hackers,&lt;/li&gt;
&lt;li&gt;frontend engineers,&lt;/li&gt;
&lt;li&gt;full-stack teams,&lt;/li&gt;
&lt;li&gt;and anyone trying to improve signup/login conversion without weakening security.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What this means in practice
&lt;/h3&gt;

&lt;p&gt;If you are building a modern app in 2026, authentication should probably be designed around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;passkeys,&lt;/li&gt;
&lt;li&gt;OAuth/OIDC where appropriate,&lt;/li&gt;
&lt;li&gt;and fewer custom password flows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The old “email + password + reset flow + 2FA patchwork” setup is no longer the most compelling default.&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;“How do I reduce friction and improve security at the same time?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Passkeys are one of the few trends that genuinely do both.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Open source maintainership is changing because AI lowers contribution friction
&lt;/h2&gt;

&lt;p&gt;One more trend that deserves attention: &lt;strong&gt;AI is increasing contribution volume, but not always contribution quality&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;GitHub’s Octoverse reports record growth in repositories, pull requests, and commits, with more than &lt;strong&gt;36 million new developers&lt;/strong&gt; joining GitHub in the past year, more than &lt;strong&gt;230 new repositories created every minute&lt;/strong&gt;, and &lt;strong&gt;43.2 million pull requests merged on average each month&lt;/strong&gt;. &lt;a href="https://github.blog/news-insights/octoverse/octoverse-a-new-developer-joins-github-every-second-as-ai-leads-typescript-to-1/" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That growth is exciting — but it also creates pressure.&lt;/p&gt;

&lt;p&gt;InfoQ, summarizing GitHub’s open source trend analysis, highlights a growing maintainer burden from low-quality AI-generated issues and pull requests, and argues that projects without clear written governance, contribution guidelines, and review expectations will struggle to scale sustainably. &lt;a href="https://www.infoq.com/news/2026/03/github-ai-2026/" rel="noopener noreferrer"&gt;9&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What this means in practice
&lt;/h3&gt;

&lt;p&gt;In 2026, if you maintain a public repository, your project needs more than code.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;contribution rules,&lt;/li&gt;
&lt;li&gt;issue templates,&lt;/li&gt;
&lt;li&gt;review expectations,&lt;/li&gt;
&lt;li&gt;a clear roadmap,&lt;/li&gt;
&lt;li&gt;and documentation that helps people help you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open source is becoming more accessible.&lt;br&gt;&lt;br&gt;
That is good.&lt;/p&gt;

&lt;p&gt;But it also means maintainers need better systems, not just better instincts.&lt;/p&gt;




&lt;h2&gt;
  
  
  So what should developers actually do now?
&lt;/h2&gt;

&lt;p&gt;If I had to turn these trends into a practical 30-day plan, it would look like this:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Upgrade how you use AI
&lt;/h3&gt;

&lt;p&gt;Stop using AI only for snippets.&lt;br&gt;&lt;br&gt;
Start testing it on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scoped feature implementation,&lt;/li&gt;
&lt;li&gt;test generation,&lt;/li&gt;
&lt;li&gt;documentation updates,&lt;/li&gt;
&lt;li&gt;and repetitive refactors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Audit your codebase for agent-friendliness
&lt;/h3&gt;

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

&lt;ul&gt;
&lt;li&gt;Is the structure obvious?&lt;/li&gt;
&lt;li&gt;Are scripts predictable?&lt;/li&gt;
&lt;li&gt;Are errors readable?&lt;/li&gt;
&lt;li&gt;Are tests reliable?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Learn the MCP mental model
&lt;/h3&gt;

&lt;p&gt;Even if you do not build an MCP server this month, understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hosts,&lt;/li&gt;
&lt;li&gt;clients,&lt;/li&gt;
&lt;li&gt;servers,&lt;/li&gt;
&lt;li&gt;permissions,&lt;/li&gt;
&lt;li&gt;and where this fits in your stack.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Revisit authentication
&lt;/h3&gt;

&lt;p&gt;If your product still treats passwords as the default forever-plan, it is time to reassess.&lt;br&gt;&lt;br&gt;
Passkeys are no longer “future tech.”&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Treat repositories like products
&lt;/h3&gt;

&lt;p&gt;If you maintain public code, improve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;README,&lt;/li&gt;
&lt;li&gt;contribution docs,&lt;/li&gt;
&lt;li&gt;labels,&lt;/li&gt;
&lt;li&gt;issue templates,&lt;/li&gt;
&lt;li&gt;and release notes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because in 2026, the quality of your repo operations matters almost as much as the quality of your code.&lt;/p&gt;




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

&lt;p&gt;A lot of trend posts focus on what is &lt;strong&gt;new&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The more useful question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;what is already reshaping real developer workflows?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Right now, the answer looks something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI agents are becoming real collaborators,&lt;/li&gt;
&lt;li&gt;MCP is becoming the connector layer,&lt;/li&gt;
&lt;li&gt;typed systems are gaining strategic value,&lt;/li&gt;
&lt;li&gt;passkeys are becoming practical default auth,&lt;/li&gt;
&lt;li&gt;and maintainership is becoming more operational.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is not just hype.&lt;br&gt;&lt;br&gt;
That is a workflow shift.&lt;/p&gt;

&lt;p&gt;The developers who adapt fastest will not be the ones who try every new tool.&lt;/p&gt;

&lt;p&gt;They will be the ones who build systems that are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clearer,&lt;/li&gt;
&lt;li&gt;safer,&lt;/li&gt;
&lt;li&gt;more automatable,&lt;/li&gt;
&lt;li&gt;and easier for both humans and machines to work with.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;What trend are you actually seeing in your day-to-day work right now?&lt;/p&gt;

&lt;p&gt;Is it AI agents, passkeys, TypeScript everywhere, better repo hygiene, or something else?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>github</category>
      <category>webdev</category>
      <category>security</category>
    </item>
    <item>
      <title>How to Build a Public Open-Source Project People Actually Want to Clone, Use, and Contribute To</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Mon, 13 Apr 2026 12:42:18 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/how-to-build-a-public-open-source-project-people-actually-want-to-clone-use-and-contribute-to-4lmg</link>
      <guid>https://dev.to/johnnylemonny/how-to-build-a-public-open-source-project-people-actually-want-to-clone-use-and-contribute-to-4lmg</guid>
      <description>&lt;p&gt;Most public repositories do &lt;strong&gt;not&lt;/strong&gt; fail because the code is bad.&lt;/p&gt;

&lt;p&gt;They fail because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nobody understands &lt;strong&gt;why the project exists&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;it is hard to &lt;strong&gt;run locally&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;the repository looks like an abandoned side experiment,&lt;/li&gt;
&lt;li&gt;and there is no clear reason for anyone to come back a second time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to build public open-source projects that attract attention on GitHub, earn stars, and gain real users, you have to think about your repository as more than a folder of code.&lt;/p&gt;

&lt;p&gt;You have to treat it like a &lt;strong&gt;public product&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this article, I’ll show you how to build modern open-source projects so they:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;look credible from the first visit,&lt;/li&gt;
&lt;li&gt;are easy to run,&lt;/li&gt;
&lt;li&gt;invite contributions,&lt;/li&gt;
&lt;li&gt;and have a much better chance of growing organically.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. Start with a problem, not with a stack
&lt;/h2&gt;

&lt;p&gt;A very common mistake looks like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I’m going to build a new project in Next.js / FastAPI / Rust / Bun to practice.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That may be a fine learning project, but it rarely turns into a strong &lt;strong&gt;public&lt;/strong&gt; project.&lt;/p&gt;

&lt;p&gt;Repositories that have the best chance of getting traction usually solve &lt;strong&gt;one clear problem&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;they save time,&lt;/li&gt;
&lt;li&gt;simplify something,&lt;/li&gt;
&lt;li&gt;automate something repetitive,&lt;/li&gt;
&lt;li&gt;or show a modern, opinionated way to build something useful.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Better ideas than “another starter”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a starter with a &lt;strong&gt;real-world use case&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;a boilerplate for a specific product type,&lt;/li&gt;
&lt;li&gt;a tool that solves a small but frequent developer pain point,&lt;/li&gt;
&lt;li&gt;a team-ready repository template,&lt;/li&gt;
&lt;li&gt;a dashboard, generator, CLI, integration, or mini-SaaS released as open source.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;A good rule of thumb:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
If you can’t describe the project in one sentence &lt;strong&gt;without naming technologies&lt;/strong&gt;, the idea is probably still too vague.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This is a project built with React, Node, Prisma, and Docker.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“This is an open-source starter for building admin panels with authentication, roles, billing, and deployment already set up.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;People click on problems.&lt;br&gt;&lt;br&gt;
Not on stacks.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Build a small but complete vertical slice
&lt;/h2&gt;

&lt;p&gt;A public project does not need to be huge.&lt;br&gt;&lt;br&gt;
It needs to feel &lt;strong&gt;complete&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is a big difference.&lt;/p&gt;

&lt;p&gt;It is far better to show:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;working authentication,&lt;/li&gt;
&lt;li&gt;one clean core workflow,&lt;/li&gt;
&lt;li&gt;solid UI,&lt;/li&gt;
&lt;li&gt;sensible folder structure,&lt;/li&gt;
&lt;li&gt;documentation,&lt;/li&gt;
&lt;li&gt;and a simple deployment path,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;than to show 27 half-finished features.&lt;/p&gt;

&lt;p&gt;The most shareable open-source projects are often the ones that create this feeling quickly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Okay, this works. I can see how I would use this.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s why modern public apps should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clear onboarding,&lt;/li&gt;
&lt;li&gt;a demo or screenshots,&lt;/li&gt;
&lt;li&gt;fast local setup,&lt;/li&gt;
&lt;li&gt;sample data,&lt;/li&gt;
&lt;li&gt;a useful &lt;code&gt;.env.example&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;and one obvious way to run the project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If someone needs 40 minutes just to reach the first useful screen, you lose most potential users.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Treat your README like a landing page
&lt;/h2&gt;

&lt;p&gt;In practice, your README is your project’s homepage.&lt;/p&gt;

&lt;p&gt;That is where visitors decide, often within seconds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whether to stay or leave,&lt;/li&gt;
&lt;li&gt;whether to star the repo,&lt;/li&gt;
&lt;li&gt;whether to clone it,&lt;/li&gt;
&lt;li&gt;or whether to close the tab.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good README does &lt;strong&gt;not&lt;/strong&gt; begin with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Project X is a modern application that aims to…”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It begins with a &lt;strong&gt;clear value proposition&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What your README should include near the top
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. A one-line pitch
&lt;/h4&gt;

&lt;p&gt;Make it obvious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what it is,&lt;/li&gt;
&lt;li&gt;who it is for,&lt;/li&gt;
&lt;li&gt;and why it matters.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;blockquote&gt;
&lt;p&gt;An open-source SaaS starter for building modern web apps with auth, billing, roles, and deployment built in.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  2. A screenshot, GIF, or live demo
&lt;/h4&gt;

&lt;p&gt;People understand software much faster when they can &lt;strong&gt;see&lt;/strong&gt; it.&lt;/p&gt;

&lt;p&gt;If it’s an application, show:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the dashboard,&lt;/li&gt;
&lt;li&gt;onboarding,&lt;/li&gt;
&lt;li&gt;settings,&lt;/li&gt;
&lt;li&gt;or the core workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. A “why this exists” section
&lt;/h4&gt;

&lt;p&gt;Briefly explain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what problem you are solving,&lt;/li&gt;
&lt;li&gt;how the project is different,&lt;/li&gt;
&lt;li&gt;when someone should use it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. A fast start section
&lt;/h4&gt;

&lt;p&gt;Keep it short and practical:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clone,&lt;/li&gt;
&lt;li&gt;install,&lt;/li&gt;
&lt;li&gt;env,&lt;/li&gt;
&lt;li&gt;run.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  5. Key features
&lt;/h4&gt;

&lt;p&gt;Not every feature.&lt;br&gt;&lt;br&gt;
Only the ones that actually communicate the value.&lt;/p&gt;

&lt;h4&gt;
  
  
  6. A roadmap
&lt;/h4&gt;

&lt;p&gt;People are more likely to follow a project when they can see where it is going.&lt;/p&gt;

&lt;h4&gt;
  
  
  7. A contributor section
&lt;/h4&gt;

&lt;p&gt;Even a short one helps.&lt;br&gt;&lt;br&gt;
It should be obvious how someone can get involved.&lt;/p&gt;

&lt;p&gt;A README does not need to be long.&lt;br&gt;&lt;br&gt;
It needs to be &lt;strong&gt;clear, concrete, and easy to scan&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. A public repository has to inspire trust
&lt;/h2&gt;

&lt;p&gt;A lot of repositories look promising at first glance.&lt;/p&gt;

&lt;p&gt;Then you open them and discover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no license,&lt;/li&gt;
&lt;li&gt;no idea whether the code can be used,&lt;/li&gt;
&lt;li&gt;no contribution rules,&lt;/li&gt;
&lt;li&gt;no issue templates,&lt;/li&gt;
&lt;li&gt;no signal that the project is actively maintained.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want people to take your project seriously, you need the &lt;strong&gt;trust layer&lt;/strong&gt; of open source.&lt;/p&gt;

&lt;p&gt;At minimum, have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LICENSE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CONTRIBUTING.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CODE_OF_CONDUCT.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SECURITY.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;issue and pull request templates&lt;/li&gt;
&lt;li&gt;a meaningful repository description&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the details that separate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“I uploaded some code”
from&lt;/li&gt;
&lt;li&gt;“I’m building a project people can join.”&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Optimize the first five minutes
&lt;/h2&gt;

&lt;p&gt;The most important moment is &lt;strong&gt;not&lt;/strong&gt; when someone stars your repo.&lt;/p&gt;

&lt;p&gt;The most important moment is when someone tries to &lt;strong&gt;run it locally&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If they hit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;missing environment variables,&lt;/li&gt;
&lt;li&gt;outdated setup instructions,&lt;/li&gt;
&lt;li&gt;confusing scripts,&lt;/li&gt;
&lt;li&gt;broken migrations,&lt;/li&gt;
&lt;li&gt;or secrets hidden in config files,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;they are usually gone.&lt;/p&gt;

&lt;p&gt;That’s why you should test your project as if you were a stranger discovering it for the first time.&lt;/p&gt;

&lt;h3&gt;
  
  
  My simple pre-release checklist
&lt;/h3&gt;

&lt;p&gt;Before making a repo public, I ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can a new person run this without asking me questions?&lt;/li&gt;
&lt;li&gt;Does the README answer the most obvious setup questions?&lt;/li&gt;
&lt;li&gt;Is the configuration predictable?&lt;/li&gt;
&lt;li&gt;Are the error messages understandable?&lt;/li&gt;
&lt;li&gt;Do sample data or seed scripts help show value quickly?&lt;/li&gt;
&lt;li&gt;Are the script names obvious?&lt;/li&gt;
&lt;li&gt;Can this project run on a clean machine without guesswork?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This usually matters more than one more feature.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Package the repository like a product
&lt;/h2&gt;

&lt;p&gt;Strong public projects are not only well-built.&lt;br&gt;&lt;br&gt;
They are also &lt;strong&gt;well-packaged&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you want more visibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;choose a clear repository name,&lt;/li&gt;
&lt;li&gt;write a sharp description,&lt;/li&gt;
&lt;li&gt;use relevant tags/topics,&lt;/li&gt;
&lt;li&gt;add a social preview image,&lt;/li&gt;
&lt;li&gt;publish real releases,&lt;/li&gt;
&lt;li&gt;show progress through changelogs and updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is especially important for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;modern web apps,&lt;/li&gt;
&lt;li&gt;starters,&lt;/li&gt;
&lt;li&gt;boilerplates,&lt;/li&gt;
&lt;li&gt;developer tools,&lt;/li&gt;
&lt;li&gt;templates,&lt;/li&gt;
&lt;li&gt;and infrastructure projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;People often judge project quality from the repository page itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is the name clear,&lt;/li&gt;
&lt;li&gt;is the description useful,&lt;/li&gt;
&lt;li&gt;is the value obvious,&lt;/li&gt;
&lt;li&gt;does the project look active?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your code can be excellent and still get ignored if the repository looks neglected.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Build community around the problem, not just the code
&lt;/h2&gt;

&lt;p&gt;A lot of people think open source is only about pull requests.&lt;/p&gt;

&lt;p&gt;That is too narrow.&lt;/p&gt;

&lt;p&gt;People can add value in many ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reporting bugs,&lt;/li&gt;
&lt;li&gt;suggesting features,&lt;/li&gt;
&lt;li&gt;improving documentation,&lt;/li&gt;
&lt;li&gt;testing edge cases,&lt;/li&gt;
&lt;li&gt;sharing feedback,&lt;/li&gt;
&lt;li&gt;explaining how they use the project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is why you should create a space not only for code changes, but for discussion and learning.&lt;/p&gt;

&lt;p&gt;Helpful tools include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;issue templates,&lt;/li&gt;
&lt;li&gt;labels like &lt;code&gt;good first issue&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;help wanted&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;a public roadmap,&lt;/li&gt;
&lt;li&gt;a list of contribution ideas,&lt;/li&gt;
&lt;li&gt;an FAQ,&lt;/li&gt;
&lt;li&gt;discussion threads,&lt;/li&gt;
&lt;li&gt;or a feedback board.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The less friction people feel, the more likely they are to participate.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Publish early, but do not publish chaos
&lt;/h2&gt;

&lt;p&gt;“Ship early” is good advice.&lt;/p&gt;

&lt;p&gt;But “make it public early” does &lt;strong&gt;not&lt;/strong&gt; mean:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Throw everything online before it makes sense.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before you promote a project, make sure it is at least:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;understandable,&lt;/li&gt;
&lt;li&gt;runnable,&lt;/li&gt;
&lt;li&gt;documented,&lt;/li&gt;
&lt;li&gt;and safe to share publicly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters even more if you are building in public on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;X,&lt;/li&gt;
&lt;li&gt;LinkedIn,&lt;/li&gt;
&lt;li&gt;DEV,&lt;/li&gt;
&lt;li&gt;Reddit,&lt;/li&gt;
&lt;li&gt;Hacker News,&lt;/li&gt;
&lt;li&gt;newsletters,&lt;/li&gt;
&lt;li&gt;or YouTube.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of the most common mistakes in open source is promoting a project too early:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;before the README is useful,&lt;/li&gt;
&lt;li&gt;before setup works,&lt;/li&gt;
&lt;li&gt;before the value proposition is clear.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result?&lt;/p&gt;

&lt;p&gt;Traffic arrives, but it does not turn into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stars,&lt;/li&gt;
&lt;li&gt;watchers,&lt;/li&gt;
&lt;li&gt;issues,&lt;/li&gt;
&lt;li&gt;contributors,&lt;/li&gt;
&lt;li&gt;or real users.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is better to publish a week later and make a strong first impression.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Think repo-first for modern apps
&lt;/h2&gt;

&lt;p&gt;If you are building a modern app in public, your repository is part of the product.&lt;/p&gt;

&lt;p&gt;It does not just store code. It:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sells the idea,&lt;/li&gt;
&lt;li&gt;documents decisions,&lt;/li&gt;
&lt;li&gt;builds trust,&lt;/li&gt;
&lt;li&gt;shows quality,&lt;/li&gt;
&lt;li&gt;and invites people in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A modern repository should answer these questions immediately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What does this do?&lt;/li&gt;
&lt;li&gt;Why does it exist?&lt;/li&gt;
&lt;li&gt;How do I run it?&lt;/li&gt;
&lt;li&gt;How do I use it?&lt;/li&gt;
&lt;li&gt;How do I report a problem?&lt;/li&gt;
&lt;li&gt;How do I contribute?&lt;/li&gt;
&lt;li&gt;What direction is the project going?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If those answers are visible from the beginning, your repository starts working for you even when you are offline.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. A simple checklist before publishing on GitHub
&lt;/h2&gt;

&lt;p&gt;Use this as a lightweight definition of done for any public project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Product
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] The project solves a specific problem&lt;/li&gt;
&lt;li&gt;[ ] There is a clear use case to show&lt;/li&gt;
&lt;li&gt;[ ] The value can be explained in one or two sentences&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Repository
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] The README is clear and concrete&lt;/li&gt;
&lt;li&gt;[ ] There is a screenshot, GIF, or demo&lt;/li&gt;
&lt;li&gt;[ ] There is a &lt;code&gt;LICENSE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] There is a &lt;code&gt;CONTRIBUTING.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] There is a &lt;code&gt;CODE_OF_CONDUCT.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] There is a &lt;code&gt;SECURITY.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Issue and PR templates are ready&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Developer Experience
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] There is an &lt;code&gt;.env.example&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Setup works from zero&lt;/li&gt;
&lt;li&gt;[ ] Commands are simple and predictable&lt;/li&gt;
&lt;li&gt;[ ] There is seed or sample data&lt;/li&gt;
&lt;li&gt;[ ] The project runs without guesswork&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Visibility
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] The repo has a good description&lt;/li&gt;
&lt;li&gt;[ ] The repository name is easy to understand&lt;/li&gt;
&lt;li&gt;[ ] Relevant tags/topics are added&lt;/li&gt;
&lt;li&gt;[ ] A first release is prepared&lt;/li&gt;
&lt;li&gt;[ ] The next steps are visible&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Open source does not win on code quality alone.&lt;/p&gt;

&lt;p&gt;It wins through a combination of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;usefulness&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;clarity&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;trust&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;easy onboarding&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;and &lt;strong&gt;consistent visible progress&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to build public projects that actually grow, do not just ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“How do I write better code?”&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;blockquote&gt;
&lt;p&gt;“Will a random developer on the internet understand this project, run it, and want to come back?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because that is where real public open source begins.&lt;/p&gt;




&lt;p&gt;If you are building public repositories yourself, I’d love to know:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What kills open-source projects fastest in your experience — scope creep, weak onboarding, poor README, inconsistent updates, or something else?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>github</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>Working on Cabal v3: reviving a P2P mobile chat app with React Native</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Mon, 13 Apr 2026 08:33:42 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/working-on-cabal-v3-reviving-a-p2p-mobile-chat-app-with-react-native-fd4</link>
      <guid>https://dev.to/johnnylemonny/working-on-cabal-v3-reviving-a-p2p-mobile-chat-app-with-react-native-fd4</guid>
      <description>&lt;p&gt;Right now I’m working on &lt;strong&gt;Cabal v3&lt;/strong&gt; — a modern &lt;strong&gt;peer-to-peer chat app for Android&lt;/strong&gt; built on top of the &lt;strong&gt;Cable protocol&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This project is both a modernization and a revival of the older &lt;strong&gt;cabal-mobile&lt;/strong&gt; effort:&lt;br&gt;
&lt;a href="https://github.com/cabal-club/cabal-mobile" rel="noopener noreferrer"&gt;https://github.com/cabal-club/cabal-mobile&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The original project was created to let people chat with the &lt;strong&gt;P2P swarm&lt;/strong&gt; on mobile devices, and its last tagged release (&lt;code&gt;1.0&lt;/code&gt;) goes back to &lt;strong&gt;June 17, 2018&lt;/strong&gt;. Later, the project README pointed development toward a &lt;code&gt;v2&lt;/code&gt; branch, so the idea of evolving the mobile client has already been part of its history. &lt;a href="https://github.com/cabal-club/cabal-mobile/releases" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;a href="https://github.com/cabal-club/cabal-mobile/diffs/0?commit=b49c4b76fbe1af236de5baaac5f3282681f18951&amp;amp;name=master&amp;amp;qualified_name=refs%2Fheads%2Fmaster&amp;amp;sha1=42a9c097684dfc6705c684ba0f6f2ccbdd7c4e00&amp;amp;sha2=b49c4b76fbe1af236de5baaac5f3282681f18951&amp;amp;short_path=5a831ea&amp;amp;unchanged=expanded&amp;amp;w=false" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;a href="https://github.com/cabal-club/cabal-mobile/commit/master" rel="noopener noreferrer"&gt;3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What makes this especially interesting to me is the protocol underneath it.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cable&lt;/strong&gt; is a lightweight protocol for &lt;strong&gt;peer-to-peer private group chats&lt;/strong&gt;, where peers communicate directly instead of depending on a centralized server. The protocol is designed around encrypted communication between equal peers and the exchange of cryptographically signed chat data. &lt;a href="https://github.com/cabal-club/cable" rel="noopener noreferrer"&gt;4&lt;/a&gt;&lt;a href="https://github.com/cabal-club/cable/blob/main/handshake.md" rel="noopener noreferrer"&gt;5&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For me, this is not just an interesting open-source revival.&lt;br&gt;&lt;br&gt;
It’s also a really good opportunity to learn more about building a &lt;strong&gt;mobile frontend with React Native&lt;/strong&gt; while working on something rooted in decentralized communication.&lt;/p&gt;

&lt;p&gt;So this project sits at a really interesting intersection for me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;mobile development&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;React Native&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Android&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;open source&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;and &lt;strong&gt;peer-to-peer systems&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still early, but I’m excited to keep pushing it forward.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>android</category>
      <category>opensource</category>
      <category>mobile</category>
    </item>
    <item>
      <title>From NASA Exoplanet Query to ExoVault: Building a Better Data Exploration Experience</title>
      <dc:creator>𝗝𝗼𝗵𝗻</dc:creator>
      <pubDate>Sat, 11 Apr 2026 13:25:13 +0000</pubDate>
      <link>https://dev.to/johnnylemonny/from-nasa-exoplanet-query-to-exovault-building-a-better-data-exploration-experience-4j06</link>
      <guid>https://dev.to/johnnylemonny/from-nasa-exoplanet-query-to-exovault-building-a-better-data-exploration-experience-4j06</guid>
      <description>&lt;p&gt;Other project idea I recently decided to build in public was inspired by the &lt;strong&gt;NASA Exoplanet Query&lt;/strong&gt; prompt from the App Ideas repository.&lt;/p&gt;

&lt;p&gt;That project eventually became &lt;strong&gt;ExoVault&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://github.com/johnnylemonny/ExoVault" rel="noopener noreferrer"&gt;https://github.com/johnnylemonny/ExoVault&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What started as a structured data query idea turned into something much more interesting for me:&lt;br&gt;
a more polished, cinematic interface for exploring NASA exoplanet archive data.&lt;/p&gt;




&lt;h2&gt;
  
  
  The original prompt
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;NASA Exoplanet Query&lt;/strong&gt; idea is already a really good challenge.&lt;/p&gt;

&lt;p&gt;It’s built around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;working with NASA archive data,&lt;/li&gt;
&lt;li&gt;loading CSV efficiently,&lt;/li&gt;
&lt;li&gt;minimizing delays at startup,&lt;/li&gt;
&lt;li&gt;and building a query interface that lets users filter exoplanet data by fields like discovery year, method, host name, and facility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That made it appealing right away.&lt;/p&gt;

&lt;p&gt;It wasn’t just about rendering data.&lt;br&gt;
It was about thinking carefully about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ingestion,&lt;/li&gt;
&lt;li&gt;structure,&lt;/li&gt;
&lt;li&gt;search,&lt;/li&gt;
&lt;li&gt;and presentation.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What pulled me into this idea
&lt;/h2&gt;

&lt;p&gt;What interested me most wasn’t only the dataset.&lt;/p&gt;

&lt;p&gt;It was the possibility of taking something that could have been “just a query tool” and pushing it toward a better exploration experience.&lt;/p&gt;

&lt;p&gt;I kept thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if this felt less like a utility screen and more like a discovery product?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question shaped the direction of the whole project.&lt;/p&gt;

&lt;p&gt;Instead of building only the minimum query UI, I wanted to create something that felt more deliberate, more visually refined, and more enjoyable to explore.&lt;/p&gt;

&lt;p&gt;That’s where &lt;strong&gt;ExoVault&lt;/strong&gt; came from.&lt;/p&gt;




&lt;h2&gt;
  
  
  How ExoVault evolved
&lt;/h2&gt;

&lt;p&gt;In my version, the project became a more premium archive explorer built around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a custom CSV-to-JSON data pipeline,&lt;/li&gt;
&lt;li&gt;a high-performance frontend,&lt;/li&gt;
&lt;li&gt;compare mode for exoplanetary systems,&lt;/li&gt;
&lt;li&gt;glassmorphic visuals,&lt;/li&gt;
&lt;li&gt;and a more cinematic browsing experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built ExoVault with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Astro&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;React&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tailwind CSS&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TypeScript&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and used a custom data pipeline to process NASA’s raw CSV into optimized JSON payloads.&lt;/p&gt;

&lt;p&gt;That part was especially satisfying because it made the project feel more complete end-to-end:&lt;br&gt;
not just UI, not just data handling, but both working together.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I wanted the project to feel like
&lt;/h2&gt;

&lt;p&gt;One of my goals with ExoVault was to make exploration feel intentional.&lt;/p&gt;

&lt;p&gt;Not just:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;filter,&lt;/li&gt;
&lt;li&gt;search,&lt;/li&gt;
&lt;li&gt;render rows,&lt;/li&gt;
&lt;li&gt;done.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted the archive to feel like something people would actually want to browse.&lt;/p&gt;

&lt;p&gt;That meant paying attention to things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;visual hierarchy,&lt;/li&gt;
&lt;li&gt;transitions,&lt;/li&gt;
&lt;li&gt;compare flow,&lt;/li&gt;
&lt;li&gt;and how the interface frames the data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think there’s a big difference between “showing information” and “creating a discovery experience.”&lt;/p&gt;

&lt;p&gt;This project made me think a lot about that difference.&lt;/p&gt;




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

&lt;p&gt;A few things stood out while working on ExoVault:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Data projects become more interesting when presentation is treated seriously
&lt;/h3&gt;

&lt;p&gt;A structured dataset can feel dry or compelling depending on how the experience is shaped.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Prompt-based ideas are often stronger than they first appear
&lt;/h3&gt;

&lt;p&gt;The original App Ideas brief gave me a very solid technical core.&lt;br&gt;
The interesting part came from deciding how to evolve it.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Frontend polish matters even in data-heavy apps
&lt;/h3&gt;

&lt;p&gt;If the interface feels flat, the whole experience feels flatter too.&lt;br&gt;
Good data presentation is also a UX problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Public repos push me toward stronger execution
&lt;/h3&gt;

&lt;p&gt;Knowing the project would live openly on GitHub made me care more about the quality of the pipeline, repo structure, documentation, and overall presentation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I like using structured prompts as a starting point
&lt;/h2&gt;

&lt;p&gt;I think structured prompt repositories are one of the best ways to build momentum — especially if you don’t treat them as the finish line.&lt;/p&gt;

&lt;p&gt;That’s how I’m trying to use them.&lt;/p&gt;

&lt;p&gt;Not as instructions to copy,&lt;br&gt;
but as a framework to build from.&lt;/p&gt;

&lt;p&gt;The prompt gives me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;direction,&lt;/li&gt;
&lt;li&gt;constraints,&lt;/li&gt;
&lt;li&gt;and technical focus.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then the real project begins when I start asking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how should this feel?&lt;/li&gt;
&lt;li&gt;what should be improved?&lt;/li&gt;
&lt;li&gt;what makes this worth sharing publicly?&lt;/li&gt;
&lt;li&gt;how can this become more than the base brief?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ExoVault is probably one of my clearest examples of that so far.&lt;/p&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ExoVault:&lt;/strong&gt; &lt;a href="https://github.com/johnnylemonny/ExoVault" rel="noopener noreferrer"&gt;https://github.com/johnnylemonny/ExoVault&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://johnnylemonny.github.io/ExoVault/" rel="noopener noreferrer"&gt;https://johnnylemonny.github.io/ExoVault/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App Ideas:&lt;/strong&gt; &lt;a href="https://github.com/florinpop17/app-ideas" rel="noopener noreferrer"&gt;https://github.com/florinpop17/app-ideas&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>astro</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
