<?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>Forem: Rad Code</title>
    <description>The latest articles on Forem by Rad Code (@heyradcode).</description>
    <link>https://forem.com/heyradcode</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%2F3511924%2F3176b77c-3787-4ba4-8a88-84b9b61e9002.png</url>
      <title>Forem: Rad Code</title>
      <link>https://forem.com/heyradcode</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/heyradcode"/>
    <language>en</language>
    <item>
      <title>The Case of the Missing Proxy: When Multiple Valtio Instances Broke My App</title>
      <dc:creator>Rad Code</dc:creator>
      <pubDate>Sun, 09 Nov 2025 14:24:55 +0000</pubDate>
      <link>https://forem.com/heyradcode/the-case-of-the-missing-proxy-when-multiple-valtio-instances-broke-my-app-1ke9</link>
      <guid>https://forem.com/heyradcode/the-case-of-the-missing-proxy-when-multiple-valtio-instances-broke-my-app-1ke9</guid>
      <description>&lt;p&gt;&lt;em&gt;This debugging story is from the &lt;a href="https://github.com/heyradcode/do-not-stop" rel="noopener noreferrer"&gt;do-not-stop&lt;/a&gt; project - a full-stack Web3 app with React frontend, React Native mobile frontend, Node.js backend, and multi-blockchain smart contracts.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mystery Begins
&lt;/h2&gt;

&lt;p&gt;It all started with a crash in &lt;code&gt;@reown/appkit-react-native&lt;/code&gt;. While debugging that crash, I noticed something weird happening with valtio. The &lt;code&gt;subscribe()&lt;/code&gt; function seemed to work fine, but &lt;code&gt;proxy()&lt;/code&gt; wasn't behaving as expected. I decided to dig into the library code itself and added some debugging directly in &lt;code&gt;node_modules/valtio/vanilla.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I printed &lt;code&gt;proxyStateMap.get(proxyObject)&lt;/code&gt; in both functions, and that's when things got interesting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;proxy()&lt;/code&gt;: Got the correct value ✓&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;subscribe()&lt;/code&gt;: Got &lt;code&gt;undefined&lt;/code&gt; ✗&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wait, what? They were &lt;strong&gt;completely different WeakMap instances&lt;/strong&gt; from different valtio module instances! The proxy object was being stored in one &lt;code&gt;proxyStateMap&lt;/code&gt;, but when &lt;code&gt;subscribe()&lt;/code&gt; tried to find it, it was looking in a completely different &lt;code&gt;proxyStateMap&lt;/code&gt; from a different valtio instance. That's like putting your keys in one drawer and looking for them in another.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Quick Note on Debugging Library Code
&lt;/h2&gt;

&lt;p&gt;Before I continue, here's something I had to remember: when you're adding &lt;code&gt;console.log&lt;/code&gt; statements to library code in &lt;code&gt;node_modules&lt;/code&gt;, your logs might not show up because modules get cached. I spent way too long wondering why my logs weren't appearing before I remembered to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm start &lt;span class="nt"&gt;--reset-cache&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding logs directly to library code is actually super helpful when you're dealing with weird behavior - you can see exactly what's happening inside the library, trace the execution flow, and peek at internal state that's not exposed through the public API. Just don't forget to clear that cache!&lt;/p&gt;

&lt;h2&gt;
  
  
  Going Back in Time
&lt;/h2&gt;

&lt;p&gt;At this point, I was stuck. The code was working before, so what changed? I did what I should have done from the start: I checked git history.&lt;/p&gt;

&lt;p&gt;I went through past commits one by one until I found a commit where everything worked. Then I compared the diff between the working state and the broken state. Bingo - there it was, a single line change in &lt;code&gt;.npmrc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+ node-linker=hoisted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one line was the culprit.&lt;/p&gt;

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

&lt;p&gt;After adding &lt;code&gt;node-linker=hoisted&lt;/code&gt;, pnpm switched to a flat structure similar to npm, and that broke the sharing mechanism by creating separate valtio instances:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules/valtio (version 1.13.2)
node_modules/@reown/appkit-core-react-native/node_modules/valtio (version 2.1.8)
node_modules/@reown/appkit-react-native/node_modules/valtio (version 2.1.8)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why did this happen? A few reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Version mismatch&lt;/strong&gt;: The root had &lt;code&gt;1.13.2&lt;/code&gt; (some transitive dependency), while the nested packages needed &lt;code&gt;2.1.8&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No virtual store&lt;/strong&gt;: With &lt;code&gt;node-linker=hoisted&lt;/code&gt;, pnpm doesn't use the &lt;code&gt;.pnpm&lt;/code&gt; virtual store structure that ensures proper symlinking&lt;/li&gt;
&lt;li&gt;pnpm kept them separate to avoid conflicts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since they were using different valtio instances, and each instance had its own &lt;code&gt;proxyStateMap&lt;/code&gt; WeakMap, the proxy object stored in one &lt;code&gt;proxyStateMap&lt;/code&gt; wasn't found when &lt;code&gt;subscribe()&lt;/code&gt; looked in a different &lt;code&gt;proxyStateMap&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;The solution was to force all valtio dependencies to use the same version. I added this to my root &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pnpm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"overrides"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"valtio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.1.8"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then ran &lt;code&gt;pnpm install&lt;/code&gt;, and that was it. This:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unified all valtio to &lt;code&gt;2.1.8&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Allowed pnpm to hoist it to the root&lt;/li&gt;
&lt;li&gt;Removed the nested instances&lt;/li&gt;
&lt;li&gt;Made all packages share the same &lt;code&gt;proxyStateMap&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Alternatively, I could have just removed &lt;code&gt;node-linker=hoisted&lt;/code&gt; to go back to pnpm's default virtual store, which handles this automatically. But I needed the hoisted structure for &lt;a href="https://dev.to/heyradcode/react-native-pnpm-and-monorepos-a-dependency-hoisting-journey-5809"&gt;other reasons&lt;/a&gt;, so the override approach worked better for me.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check git history first&lt;/strong&gt; - When something breaks, see what changed. The diff often reveals the root cause immediately.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Adding console.log to library code is a good debugging habit&lt;/strong&gt; - When you encounter weird library behavior, adding logs directly in &lt;code&gt;node_modules&lt;/code&gt; can reveal exactly what's happening inside the library. Just remember to use &lt;code&gt;--reset-cache&lt;/code&gt; when debugging library code in &lt;code&gt;node_modules&lt;/code&gt; to see your changes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multiple package instances can break singleton-like behavior&lt;/strong&gt; - Libraries that rely on WeakMaps, module-level state, or singletons can fail when multiple instances exist. This isn't just a valtio thing - it can happen with any library that maintains internal state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Version mismatches prevent hoisting&lt;/strong&gt; - Even with &lt;code&gt;node-linker=hoisted&lt;/code&gt;, pnpm will keep separate instances if versions differ. Use &lt;code&gt;pnpm.overrides&lt;/code&gt; to force consistent versions across your workspace.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Happy debugging!&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>pnpm</category>
      <category>debugging</category>
      <category>valtio</category>
    </item>
    <item>
      <title>React Native, pnpm, and Monorepos: A Dependency Hoisting Journey</title>
      <dc:creator>Rad Code</dc:creator>
      <pubDate>Wed, 05 Nov 2025 05:16:50 +0000</pubDate>
      <link>https://forem.com/heyradcode/react-native-pnpm-and-monorepos-a-dependency-hoisting-journey-5809</link>
      <guid>https://forem.com/heyradcode/react-native-pnpm-and-monorepos-a-dependency-hoisting-journey-5809</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;When working with React Native and pnpm, &lt;strong&gt;just use &lt;code&gt;node-linker=hoisted&lt;/code&gt;&lt;/strong&gt; instead of trying to selectively hoist individual packages. It will save you hours of debugging mysterious codegen errors and Kotlin compilation issues. (If you're in a monorepo, you'll also need to update &lt;code&gt;build.gradle&lt;/code&gt; paths to point to the root &lt;code&gt;node_modules&lt;/code&gt;.)&lt;/p&gt;

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

&lt;p&gt;I'm working on a React Native 0.82 project in a pnpm monorepo. The structure looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;do-not-stop/
├── mobile/          # React Native app
├── frontend/        # Web app
├── backend/         # API server
├── packages/        # Shared packages
└── package.json     # Root workspace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;During an Android build, I encountered this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Cannot find module 'E:\git\do-not-stop\mobile\node_modules\@react-native\codegen\lib\cli\combine\combine-js-to-schema-cli.js'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The build was failing at the &lt;code&gt;generateCodegenSchemaFromJavaScript&lt;/code&gt; task for &lt;code&gt;@react-native-async-storage/async-storage&lt;/code&gt;. Classic React Native codegen issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Initial "Solution" (That Didn't Work)
&lt;/h2&gt;

&lt;p&gt;Following the conventional wisdom, I added the missing packages to &lt;code&gt;mobile/package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@react-native/codegen"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.82.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@react-native/gradle-plugin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.82.1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This seemed logical - the build needs these packages, so let's add them as dependencies. Right?&lt;/p&gt;

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

&lt;h2&gt;
  
  
  The Rabbit Hole Deepens
&lt;/h2&gt;

&lt;p&gt;After adding these dependencies, I started getting Kotlin compilation errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unresolved reference 'NativeRNWalletConnectModuleSpec'
'getName' overrides nothing
'getTypedExportedConstants' overrides nothing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The errors were coming from &lt;code&gt;@walletconnect/react-native-compat&lt;/code&gt;, which was trying to use codegen-generated classes that weren't being found.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempting Selective Hoisting
&lt;/h2&gt;

&lt;p&gt;I tried selective hoisting - first with a simple &lt;code&gt;hoistPattern&lt;/code&gt; in &lt;code&gt;package.json&lt;/code&gt; (which created even more issues with inconsistent paths and module resolution), then with a more sophisticated &lt;code&gt;public-hoist-pattern&lt;/code&gt; configuration in &lt;code&gt;.npmrc&lt;/code&gt; that others claim works. Here's the more comprehensive example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# .npmrc
&lt;/span&gt;
&lt;span class="c"&gt;# so gradle / metro can see my local package
&lt;/span&gt;&lt;span class="py"&gt;hoist-workspace-packages&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="err"&gt;public-hoist-pattern&lt;/span&gt;&lt;span class="nn"&gt;[]&lt;/span&gt;&lt;span class="err"&gt;=@myOrg/sharedPackage&lt;/span&gt;

&lt;span class="c"&gt;# so gradle and metro can see various cli tools and settings
&lt;/span&gt;&lt;span class="err"&gt;public-hoist-pattern&lt;/span&gt;&lt;span class="nn"&gt;[]&lt;/span&gt;&lt;span class="err"&gt;=@react-native-community/*&lt;/span&gt;
&lt;span class="err"&gt;public-hoist-pattern&lt;/span&gt;&lt;span class="nn"&gt;[]&lt;/span&gt;&lt;span class="err"&gt;=react-native&lt;/span&gt;
&lt;span class="err"&gt;public-hoist-pattern&lt;/span&gt;&lt;span class="nn"&gt;[]&lt;/span&gt;&lt;span class="err"&gt;=@react-native/codegen&lt;/span&gt;
&lt;span class="err"&gt;public-hoist-pattern&lt;/span&gt;&lt;span class="nn"&gt;[]&lt;/span&gt;&lt;span class="err"&gt;=@react-native/gradle-plugin&lt;/span&gt;
&lt;span class="err"&gt;public-hoist-pattern&lt;/span&gt;&lt;span class="nn"&gt;[]&lt;/span&gt;&lt;span class="err"&gt;=@babel/runtime&lt;/span&gt;

&lt;span class="c"&gt;# i happen to be using this, and it has a complex native build step that didn't work without this hoist.
# might be true for other packages that have native build steps
&lt;/span&gt;&lt;span class="err"&gt;public-hoist-pattern&lt;/span&gt;&lt;span class="nn"&gt;[]&lt;/span&gt;&lt;span class="err"&gt;=react-native-gesture-handler&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration uses &lt;code&gt;public-hoist-pattern&lt;/code&gt; (instead of &lt;code&gt;hoistPattern&lt;/code&gt; in &lt;code&gt;package.json&lt;/code&gt;) and includes workspace packages, a broader set of React Native packages, and packages with native build steps. &lt;strong&gt;But it still didn't work for me.&lt;/strong&gt; This is exactly the problem with selective hoisting - even configurations that work for others can fail in your specific setup because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React Native versions differ&lt;/li&gt;
&lt;li&gt;Dependency trees vary based on your packages&lt;/li&gt;
&lt;li&gt;Build tools evolve and change their expectations&lt;/li&gt;
&lt;li&gt;Native modules have different requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a configuration this comprehensive still fails, it's a clear sign that selective hoisting is fragile and outdated. The fact that it works for some but not others is precisely why full hoisting is the safer choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Solution
&lt;/h2&gt;

&lt;p&gt;After hours of debugging, I finally gave up on selective hoisting and went nuclear:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Added &lt;code&gt;.npmrc&lt;/code&gt; at the root:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node-linker=hoisted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hoisted linker ensures all packages are in a single &lt;code&gt;node_modules&lt;/code&gt; directory, which React Native's build system expects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For pnpm monorepos specifically, updated &lt;code&gt;mobile/android/app/build.gradle&lt;/code&gt; to point to the root &lt;code&gt;node_modules&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="n"&gt;react&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Point to root node_modules since we're using hoisted linking in a monorepo&lt;/span&gt;
    &lt;span class="n"&gt;reactNativeDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"../../../node_modules/react-native"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;codegenDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"../../../node_modules/@react-native/codegen"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cliFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"../../../node_modules/react-native/cli.js"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;autolinkLibrariesWithApp&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The paths go up three levels because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mobile/android/app/build.gradle&lt;/code&gt; → &lt;code&gt;mobile/android/&lt;/code&gt; → &lt;code&gt;mobile/&lt;/code&gt; → root&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you're using pnpm with a standalone React Native project (not a monorepo), you only need the &lt;code&gt;.npmrc&lt;/code&gt; change. The &lt;code&gt;build.gradle&lt;/code&gt; modifications are only necessary when your React Native app is nested in a monorepo structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lesson
&lt;/h2&gt;

&lt;p&gt;Selective hoisting in React Native projects using pnpm is a &lt;strong&gt;false optimization&lt;/strong&gt;. You might think: "Couldn't I just track down all the React Native-related packages that need hoisting?" Technically, yes - you could spend hours finding &lt;code&gt;@react-native/codegen&lt;/code&gt;, &lt;code&gt;@react-native/gradle-plugin&lt;/code&gt;, &lt;code&gt;@react-native/metro-config&lt;/code&gt;, &lt;code&gt;@react-native/babel-preset&lt;/code&gt;, and all the other RN packages. But React Native's dependency structure changes frequently between versions. What works today might break tomorrow when you upgrade React Native or one of its tooling packages. You'd be playing whack-a-mole with your hoist patterns every time you update.&lt;/p&gt;

&lt;p&gt;React Native's build system expects a certain structure. When you have Gradle looking for codegen, Metro bundler resolving modules, native code trying to import generated classes, and multiple tools working together, you need &lt;strong&gt;consistency&lt;/strong&gt;, not clever path resolution. Hoisting everything gives you that consistency, and it's future-proof.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That said&lt;/strong&gt;, if you really want to save disk space and are willing to maintain a selective hoisting configuration with pnpm's &lt;code&gt;hoistPattern&lt;/code&gt; or &lt;code&gt;public-hoist-pattern&lt;/code&gt; and symlinking strategies, it's technically possible. But this requires deep knowledge of React Native's dependency graph, ongoing maintenance as packages update, and potential debugging time when things break.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you've successfully set up selective hoisting for React Native with pnpm and want to help others, please share your configuration in the comments!&lt;/strong&gt; Include your &lt;code&gt;.npmrc&lt;/code&gt; settings, &lt;code&gt;package.json&lt;/code&gt; hoist patterns, and any &lt;code&gt;build.gradle&lt;/code&gt; modifications you made.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Next time you're setting up a React Native project with pnpm, just hoist everything from the start. Your builds will be reliable, and your debugging time will be spent on actual features, not dependency resolution.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article is part of the &lt;a href="https://github.com/heyradcode/do-not-stop" rel="noopener noreferrer"&gt;do-not-stop&lt;/a&gt; project - a full-stack Web3 playground with React Native mobile support.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have you had similar experiences with React Native and pnpm? Share your horror stories (and solutions) in the comments!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>pnpm</category>
      <category>monorepo</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Why AI Needs Human Oversight for Architecture: A Real Refactoring Story</title>
      <dc:creator>Rad Code</dc:creator>
      <pubDate>Sun, 02 Nov 2025 10:37:21 +0000</pubDate>
      <link>https://forem.com/heyradcode/why-ai-needs-human-oversight-for-architecture-a-real-refactoring-story-4l3g</link>
      <guid>https://forem.com/heyradcode/why-ai-needs-human-oversight-for-architecture-a-real-refactoring-story-4l3g</guid>
      <description>&lt;p&gt;&lt;em&gt;How a simple authentication refactor taught me that AI assistants are great at code, but need human guidance for architectural decisions&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article is based on my experience refactoring the authentication system in the &lt;a href="https://github.com/heyradcode/do-not-stop" rel="noopener noreferrer"&gt;heyradcode/do-not-stop&lt;/a&gt; project.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Task
&lt;/h2&gt;

&lt;p&gt;I had a shared authentication package (&lt;code&gt;@do-not-stop/shared-auth&lt;/code&gt;) that was being used by both my frontend (React web) and mobile (React Native) apps. Interestingly, this package was originally created by my AI assistant during a "vibe coding" session - I was just going with the flow and letting it build the structure. The code had some duplication - both projects were manually wiring up the same hooks and API clients. Simple task: consolidate the duplicated code.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the AI Suggested First
&lt;/h2&gt;

&lt;p&gt;When I asked my AI assistant to consolidate, it immediately jumped to a &lt;strong&gt;factory pattern&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AI's first suggestion&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createEthereumAuth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;apiUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;storageAdapter&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createAuthApiClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiUrl&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;useNonce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createUseNonce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiClient&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;useVerifySignature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createUseVerifySignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onTokenSuccess&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;AuthProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createAuthProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;useAccountHook&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;useAccount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;useSignMessageHook&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;useSignMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;useNonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;useVerifySignature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;storageAdapter&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="nx"&gt;AuthProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useNonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useVerifySignature&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;At first glance, this seems reasonable. It removes duplication, right? But it's still passing everything around. The AI assistant kept adding layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Factory functions that return other factories&lt;/li&gt;
&lt;li&gt;Parameters that get passed through multiple levels&lt;/li&gt;
&lt;li&gt;"Backward compatibility" exports "just in case"&lt;/li&gt;
&lt;li&gt;Bridge files that re-export things&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Human Intervention
&lt;/h2&gt;

&lt;p&gt;When I looked at the code, I kept asking simpler questions:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why do we need &lt;code&gt;createAuthProvider&lt;/code&gt;? Can't we just use &lt;code&gt;AuthProvider&lt;/code&gt; directly?"&lt;/p&gt;

&lt;p&gt;"Why pass &lt;code&gt;apiClient&lt;/code&gt; when we can set it globally and reuse it?"&lt;/p&gt;

&lt;p&gt;"Why re-export hooks through bridge files when we can import directly?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each question stripped away another unnecessary layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Final Solution
&lt;/h2&gt;

&lt;p&gt;Instead of factories and parameters, we used &lt;strong&gt;global configuration&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// config.ts - configure once&lt;/span&gt;
&lt;span class="nf"&gt;setApiBaseUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;setTokenSuccessCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;setStorageAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// AuthContext.tsx - just use directly&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useAccount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useSignMessage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wagmi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useNonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useVerifySignature&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../hooks&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;const&lt;/span&gt; &lt;span class="nx"&gt;AuthProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAccount&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// No parameters!&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;signMessage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSignMessage&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="c1"&gt;// App.tsx - simple import&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AuthProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@do-not-stop/shared-auth&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;&lt;strong&gt;The difference:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Before: Factory pattern, 6+ parameters, bridge files, re-exports&lt;/li&gt;
&lt;li&gt;✅ After: Global setters, direct imports, zero parameters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Final Architecture I ended up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;packages/shared-auth/
  ├── api.ts              # Singleton API client
  ├── hooks/
  │   ├── useNonce.ts     # Direct hook (uses shared client)
  │   └── useVerifySignature.ts
  └── contexts/
      └── AuthContext.tsx # Direct component (uses hooks directly)

frontend/src/config.ts    # setApiBaseUrl(), setStorageAdapter()
mobile/src/config.ts      # Same, different adapter

App.tsx                   # import { AuthProvider } from 'shared-auth'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Zero factories. Zero parameters. Zero bridge files.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;The breakthrough questions I kept asking were all about &lt;strong&gt;simplification&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;"Can this be removed?"&lt;/strong&gt; - I asked about every layer, every file, every export&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Why pass this as a parameter?"&lt;/strong&gt; - When the AI suggested passing hooks, I asked why not import directly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Where is this actually used?"&lt;/strong&gt; - I found many files that were just re-exporting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"What's the minimum I need?"&lt;/strong&gt; - I stripped it down to just configuration + direct usage&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;AI is excellent at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing code&lt;/li&gt;
&lt;li&gt;Implementing patterns it's seen before&lt;/li&gt;
&lt;li&gt;Fixing syntax errors&lt;/li&gt;
&lt;li&gt;Refactoring within existing patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI struggles with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Questioning whether patterns are needed&lt;/li&gt;
&lt;li&gt;Simplifying beyond existing patterns&lt;/li&gt;
&lt;li&gt;Understanding when "less is more"&lt;/li&gt;
&lt;li&gt;Architectural judgment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The solution?&lt;/strong&gt; Use AI for implementation, but keep a human in the loop for architectural decisions. When AI suggests adding complexity, ask: "Can I do this more simply?"&lt;/p&gt;

&lt;p&gt;The best code is often the code you don't write. AI doesn't always know that.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article is based on my real refactoring session with an AI coding assistant while working on the &lt;a href="https://github.com/heyradcode/do-not-stop" rel="noopener noreferrer"&gt;heyradcode/do-not-stop&lt;/a&gt; project. The authentication system works great now, and the codebase is simpler than when I started.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cursor</category>
      <category>ai</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Surviving pnpm + React Native: How I Finally Stopped Metro from Screaming About `@babel/runtime`</title>
      <dc:creator>Rad Code</dc:creator>
      <pubDate>Thu, 30 Oct 2025 12:53:21 +0000</pubDate>
      <link>https://forem.com/heyradcode/surviving-pnpm-react-native-how-i-finally-stopped-metro-from-screaming-about-babelruntime-493i</link>
      <guid>https://forem.com/heyradcode/surviving-pnpm-react-native-how-i-finally-stopped-metro-from-screaming-about-babelruntime-493i</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/heyradcode/do-not-stop" rel="noopener noreferrer"&gt;&lt;strong&gt;Do-Not-Stop&lt;/strong&gt;&lt;/a&gt; is a continuously evolving &lt;strong&gt;Web3 frontend playground&lt;/strong&gt; — built with &lt;strong&gt;Vite&lt;/strong&gt;, &lt;strong&gt;React&lt;/strong&gt;, &lt;strong&gt;TypeScript&lt;/strong&gt;, &lt;strong&gt;viem&lt;/strong&gt;, and &lt;strong&gt;wagmi&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
It’s designed to grow, adapt, and experiment with the latest in &lt;strong&gt;Ethereum&lt;/strong&gt;, &lt;strong&gt;Solana&lt;/strong&gt;, and &lt;strong&gt;React Native&lt;/strong&gt; development — a living project that explores how a single modern codebase can span web and mobile in the Web3 space.&lt;/p&gt;

&lt;p&gt;Naturally, I used &lt;strong&gt;pnpm workspaces&lt;/strong&gt;. They’re fast, clean, and perfect for sharing code between multiple projects.&lt;br&gt;&lt;br&gt;
Until Metro — React Native’s bundler — decided it hated my setup.&lt;/p&gt;


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


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;do-not-stop/
├─ frontend/              ← web (Vite + React + wagmi)
├─ mobile/                ← React Native
├─ packages/shared-auth/  ← shared logic between web &amp;amp; mobile
└─ pnpm-workspace.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The goal was simple: import &lt;code&gt;packages/shared-auth&lt;/code&gt; into both web and mobile.&lt;br&gt;&lt;br&gt;
Everything looked good — &lt;code&gt;pnpm install&lt;/code&gt; was blazing fast, dependencies deduped — until Metro threw this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Unable to resolve module @babel/runtime/helpers/interopRequireDefault
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even though &lt;code&gt;mobile/node_modules/@babel/runtime&lt;/code&gt; clearly existed.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Where Everything Fell Apart
&lt;/h2&gt;

&lt;p&gt;Here’s the thing about pnpm: it doesn’t install packages “flat” like npm or yarn.&lt;br&gt;&lt;br&gt;
Instead, it builds a &lt;strong&gt;virtual store&lt;/strong&gt; under &lt;code&gt;.pnpm/&lt;/code&gt; and links everything through symlinks.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mobile/node_modules/@babel/runtime
  → ../../node_modules/.pnpm/@babel+runtime@7.x/node_modules/@babel/runtime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Node can follow that fine, but &lt;strong&gt;Metro’s resolver&lt;/strong&gt; often doesn’t.&lt;br&gt;&lt;br&gt;
It just climbs directories until it finds a &lt;code&gt;node_modules&lt;/code&gt;, so it ends up loading stuff from the &lt;strong&gt;workspace root&lt;/strong&gt;, not from the mobile app’s local copy.&lt;/p&gt;


&lt;h2&gt;
  
  
  🧠 I Tried Everything First
&lt;/h2&gt;

&lt;p&gt;When you start googling “pnpm react native &lt;a class="mentioned-user" href="https://dev.to/babel"&gt;@babel&lt;/a&gt;/runtime,” you end up in GitHub issues and Stack Overflow threads full of half-solutions.&lt;/p&gt;

&lt;p&gt;I tried disabling hoisting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;hoistPattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then isolating linkers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node-linker=isolated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the nuclear option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;shamefully-hoist=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each time, something else broke — either the mobile build, or the shared packages stopped linking, or Metro just refused to cooperate.&lt;/p&gt;

&lt;p&gt;Nothing really fixed it.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚙️ The Real Fix — Teach Metro to Understand pnpm
&lt;/h2&gt;

&lt;p&gt;The problem wasn’t pnpm.&lt;br&gt;&lt;br&gt;
The problem was &lt;strong&gt;Metro’s assumptions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It expects a flat &lt;code&gt;node_modules&lt;/code&gt; world, but pnpm builds a smart, symlinked tree.&lt;br&gt;&lt;br&gt;
So instead of forcing pnpm to behave like npm, I made &lt;strong&gt;Metro understand pnpm.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here’s the final working &lt;code&gt;metro.config.js&lt;/code&gt; inside my &lt;code&gt;mobile/&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getDefaultConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mergeConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@react-native/metro-config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Metro configuration for pnpm monorepos
 * (do-not-stop project)
 *
 * - Lets Metro follow pnpm's symlinks
 * - Always prefers the mobile app's own node_modules
 * - Watches the monorepo root so shared packages rebuild correctly
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getDefaultConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&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;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;watchFolders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
  &lt;span class="na"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaultConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;unstable_enableSymlinks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;unstable_enablePackageExports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;extraNodeModules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&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="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node_modules&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mergeConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defaultConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧪 Bonus: Using Shared Packages
&lt;/h2&gt;

&lt;p&gt;Because &lt;code&gt;watchFolders&lt;/code&gt; includes the repo root, Metro can detect changes in &lt;code&gt;packages/shared-auth/&lt;/code&gt; (or any shared package).&lt;/p&gt;

&lt;p&gt;If you want finer control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;watchFolders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;packages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shared-auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;packages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utils&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;h2&gt;
  
  
  🧠 Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;pnpm’s layout isn’t broken — it’s just &lt;strong&gt;smarter&lt;/strong&gt; than Metro expects.
&lt;/li&gt;
&lt;li&gt;Disabling hoisting or flattening installs only hides the issue.
&lt;/li&gt;
&lt;li&gt;The real solution lives in &lt;strong&gt;Metro’s resolver config&lt;/strong&gt;, not your package manager.
&lt;/li&gt;
&lt;li&gt;When mixing web + mobile in a single repo, configure your tools to coexist — not compete.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎉 Conclusion
&lt;/h2&gt;

&lt;p&gt;This setup now powers &lt;a href="https://github.com/heyradcode/do-not-stop" rel="noopener noreferrer"&gt;&lt;strong&gt;do-not-stop&lt;/strong&gt;&lt;/a&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A React web app (&lt;code&gt;frontend/&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;A React Native app (&lt;code&gt;mobile/&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;Shared code in &lt;code&gt;packages/shared-auth/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;All managed by pnpm&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Do-Not-Stop&lt;/strong&gt; isn’t just a playground anymore — it’s an evolving multi-platform Web3 stack that grows alongside the latest in Ethereum, Solana, and modern React development.&lt;/p&gt;

&lt;p&gt;After a week of battling hoisting, symlinks, and Babel errors, I stopped fighting pnpm and just made Metro smarter.&lt;/p&gt;

&lt;p&gt;If you’re running React Native inside a pnpm monorepo, drop this config in your app and get back to building 🚀&lt;/p&gt;




</description>
      <category>reactnative</category>
      <category>npm</category>
      <category>monorepo</category>
      <category>web3</category>
    </item>
    <item>
      <title>How to Build a Persistent Solana Docker Dev Setup (No WSL!)</title>
      <dc:creator>Rad Code</dc:creator>
      <pubDate>Sat, 11 Oct 2025 03:10:10 +0000</pubDate>
      <link>https://forem.com/heyradcode/building-a-persistent-solana-docker-environment-on-windows-without-wsl-388l</link>
      <guid>https://forem.com/heyradcode/building-a-persistent-solana-docker-environment-on-windows-without-wsl-388l</guid>
      <description>&lt;h3&gt;
  
  
  Chapter 1 — The Goal
&lt;/h3&gt;

&lt;p&gt;The objective was to create a &lt;strong&gt;reliable, persistent Solana development environment&lt;/strong&gt; that runs natively on &lt;strong&gt;Windows&lt;/strong&gt;, without depending on the Windows Subsystem for Linux (WSL).&lt;/p&gt;

&lt;p&gt;While WSL can be useful, it often introduces file system latency, permission inconsistencies, and integration issues with Docker Desktop. The goal was to achieve a seamless workflow using Docker alone — fully isolated, reproducible, and stable.&lt;/p&gt;

&lt;p&gt;This setup is now part of the &lt;a href="https://github.com/heyradcode/do-not-stop" rel="noopener noreferrer"&gt;&lt;strong&gt;do-not-stop&lt;/strong&gt;&lt;/a&gt; project — a long-term initiative to define a consistent, modern development standard for Web3.&lt;br&gt;&lt;br&gt;
The project aims to demonstrate how ecosystems such as &lt;strong&gt;Ethereum&lt;/strong&gt;, &lt;strong&gt;Solana&lt;/strong&gt;, and eventually &lt;strong&gt;Cardano&lt;/strong&gt; can coexist within a unified, practical environment that supports real-world development workflows.&lt;/p&gt;


&lt;h3&gt;
  
  
  Chapter 2 — The Starting Point
&lt;/h3&gt;

&lt;p&gt;The foundation came from the &lt;a href="https://hub.docker.com/r/tchambard/solana-test-validator" rel="noopener noreferrer"&gt;&lt;strong&gt;tchambard/solana-test-validator&lt;/strong&gt;&lt;/a&gt; image — a prebuilt Solana development image that includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solana CLI
&lt;/li&gt;
&lt;li&gt;Rust toolchain
&lt;/li&gt;
&lt;li&gt;A fully functional &lt;code&gt;solana-test-validator&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Credit goes to &lt;a href="https://github.com/tchambard" rel="noopener noreferrer"&gt;&lt;strong&gt;@tchambard&lt;/strong&gt;&lt;/a&gt; for providing this image. It’s lightweight, well-prepared, and an excellent baseline for any Solana development workflow.&lt;/p&gt;

&lt;p&gt;Initially, the idea was to isolate responsibilities across multiple Docker services — one for the validator, another for Solana CLI, and perhaps a third for project logic. However, Solana’s toolchain and CLI are tightly coupled. Separating them would introduce unnecessary complexity, requiring multiple shared volumes and synchronized builds.&lt;/p&gt;

&lt;p&gt;Consolidating everything into a &lt;strong&gt;single service&lt;/strong&gt; proved more effective. It ensured that the validator, CLI, and supporting dependencies operated in a consistent and predictable environment.&lt;/p&gt;


&lt;h3&gt;
  
  
  Chapter 3 — The Permission Model
&lt;/h3&gt;

&lt;p&gt;The first issue encountered involved &lt;strong&gt;filesystem permissions&lt;/strong&gt; when working with bind mounts from Windows.&lt;/p&gt;

&lt;p&gt;The container was configured to mount the project source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./cryptozombies:/home/ubuntu/cryptozombies&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, any write operation — such as building or generating artifacts — resulted in permission errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EACCES: permission denied, mkdir '/home/ubuntu/cryptozombies/target'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This occurs because NTFS-based volumes mounted through Docker Desktop are &lt;strong&gt;owned by the root user&lt;/strong&gt; inside the Linux container. The default &lt;code&gt;ubuntu&lt;/code&gt; user inside the image lacks write privileges for these mounts.&lt;/p&gt;

&lt;p&gt;The solution was straightforward and justified:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running as root allowed the container to write to bind-mounted directories, resolving the issue without introducing unsafe behavior.&lt;br&gt;&lt;br&gt;
This is not a workaround — it is the correct approach when dealing with Docker bind mounts on Windows, where file ownership cannot be translated directly from the host.&lt;/p&gt;

&lt;p&gt;After this change, the environment functioned as expected. Builds, validator logs, and temporary files all wrote correctly to the mounted project directory.&lt;/p&gt;


&lt;h3&gt;
  
  
  Chapter 4 — The Platform Tools Discovery
&lt;/h3&gt;

&lt;p&gt;The next challenge appeared when repeatedly running Solana commands inside the container.&lt;br&gt;&lt;br&gt;
Each time &lt;code&gt;solana-test-validator&lt;/code&gt; or a build was executed, Solana re-downloaded large SDK archives such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tmp-platform-tools-linux-x86_64.tar.bz2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The downloads were redundant. Even after adding a named volume to persist Solana’s installation directory, the issue persisted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;solana-install:/home/ubuntu/.local/share/solana/install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This volume behaved as expected: Docker seeded it with the Solana CLI that already existed inside the base image. The CLI itself worked correctly after each restart — a clear sign that the seeding process was functioning as intended.&lt;/p&gt;

&lt;p&gt;However, the platform toolchain and SDK assets continued to be re-downloaded on every startup.&lt;br&gt;&lt;br&gt;
To understand why, the next step was to trace the actual file paths used by Solana during installation.&lt;/p&gt;

&lt;p&gt;Inside the install directory, the following path appeared:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/ubuntu/.local/share/solana/install/releases/2.3.7/solana-release/bin/platform-tools-sdk/sbf/dependencies/platform-tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On inspection, this path was not a directory but a &lt;strong&gt;symbolic link&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;platform-tools -&amp;gt; /root/.cache/solana/v1.48/platform-tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This detail was critical.&lt;br&gt;&lt;br&gt;
While the main Solana binaries lived under the persisted install directory, the SDK and toolchains were stored separately under &lt;code&gt;/root/.cache/solana&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;/root/.cache&lt;/code&gt; was not mounted as a volume, it was cleared each time the container restarted. As a result, the symbolic link pointed to a non-existent path, prompting Solana to re-download all dependencies.&lt;/p&gt;

&lt;p&gt;The resolution was simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;solana-cache:/root/.cache/solana&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensured that Solana’s cached SDKs and platform-tools persisted across restarts, eliminating unnecessary downloads and improving startup performance dramatically.&lt;/p&gt;




&lt;h3&gt;
  
  
  Chapter 5 — The Final Configuration
&lt;/h3&gt;

&lt;p&gt;Below is the finalized Docker Compose configuration that encapsulates the complete solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;solana-dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tchambard/solana-test-validator:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;solana-dev&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8899:8899"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8900:8900"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9900:9900"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./cryptozombies:/home/ubuntu/cryptozombies&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;solana-cache:/root/.cache/solana&lt;/span&gt;
    &lt;span class="na"&gt;working_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/home/ubuntu&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="s"&gt;sh -c "&lt;/span&gt;
        &lt;span class="s"&gt;solana-test-validator --reset --rpc-port 8899 --bind-address 0.0.0.0 &amp;amp;&lt;/span&gt;
        &lt;span class="s"&gt;sleep 10 &amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="s"&gt;tail -f /dev/null&lt;/span&gt;
      &lt;span class="s"&gt;"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;RUST_LOG=warn&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;TMPDIR=/tmp&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;solana-network&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;curl"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-f"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8899/health"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;solana-network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;solana-cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration delivers a self-contained Solana development environment with persistent volumes for both the Solana installation and its cached toolchain. It’s efficient, reproducible, and compatible with Docker Desktop on Windows without WSL.&lt;/p&gt;




&lt;h3&gt;
  
  
  Chapter 6 — The Broader Vision
&lt;/h3&gt;

&lt;p&gt;This setup now serves as part of the &lt;a href="https://github.com/heyradcode/do-not-stop" rel="noopener noreferrer"&gt;&lt;strong&gt;do-not-stop&lt;/strong&gt;&lt;/a&gt; project — a practical foundation for &lt;strong&gt;cross-chain Web3 development&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal is to provide developers with a consistent environment that supports multiple blockchain ecosystems simultaneously, ensuring that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Toolchains remain stable and cached between runs.
&lt;/li&gt;
&lt;li&gt;Source code is editable directly from the host machine.
&lt;/li&gt;
&lt;li&gt;Builds and validators execute identically across operating systems.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach promotes a unified development experience that reduces friction and eliminates environment drift.&lt;/p&gt;




&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WSL is not required.&lt;/strong&gt; Docker Desktop provides sufficient isolation for Windows-based Solana development.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Running as root&lt;/strong&gt; is the correct approach for handling NTFS-mounted volumes.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persist both&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/home/ubuntu/.local/share/solana/install&lt;/code&gt; (Solana CLI)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/root/.cache/solana&lt;/code&gt; (SDKs and platform-tools)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Docker’s named volume seeding&lt;/strong&gt; ensures that existing image content is copied into new volumes on first run.&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This setup represents a methodical exploration of how Solana’s toolchain interacts with Docker under Windows.&lt;br&gt;&lt;br&gt;
By identifying the symbolic link to &lt;code&gt;/root/.cache/solana&lt;/code&gt; and ensuring that both the installation and cache directories are persisted, the environment achieves complete reproducibility and fast startup times.&lt;/p&gt;

&lt;p&gt;The resulting configuration is stable, efficient, and ready to serve as a reference for modern Web3 developers.&lt;br&gt;&lt;br&gt;
It’s one step toward the broader goal of &lt;strong&gt;do-not-stop&lt;/strong&gt; — establishing clear, reliable examples of how Web3 infrastructure should be built.&lt;/p&gt;

</description>
      <category>solana</category>
      <category>docker</category>
      <category>windows</category>
      <category>web3</category>
    </item>
    <item>
      <title>The Reality Check: Why AI Coding Assistants Still Need Human Oversight</title>
      <dc:creator>Rad Code</dc:creator>
      <pubDate>Tue, 23 Sep 2025 09:48:43 +0000</pubDate>
      <link>https://forem.com/heyradcode/the-reality-check-why-ai-coding-assistants-still-need-human-oversight-32l5</link>
      <guid>https://forem.com/heyradcode/the-reality-check-why-ai-coding-assistants-still-need-human-oversight-32l5</guid>
      <description>&lt;p&gt;&lt;em&gt;A cautionary tale about trusting Cursor's AI too much and the importance of understanding your tools&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Setup
&lt;/h3&gt;

&lt;p&gt;Recently, I was working on a Web3 authentication flow using React, wagmi, and a Node.js backend. The goal was simple: connect wallet → sign message → verify signature → get JWT token. Sounds straightforward, right?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;The authentication flow wasn't working correctly. When users clicked "Sign Message &amp;amp; Login", the backend verification API was being called immediately, before the user had actually signed anything in MetaMask. This was clearly wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  The AI's "Solution"
&lt;/h3&gt;

&lt;p&gt;I asked Cursor's AI assistant to fix this issue. Here's what happened:&lt;/p&gt;

&lt;h4&gt;
  
  
  First Attempt: The Async/Await Mistake
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AI's initial "fix"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;signMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI assumed &lt;code&gt;signMessage&lt;/code&gt; was an async function and tried to await it. This was completely wrong.&lt;/p&gt;

&lt;h4&gt;
  
  
  Second Attempt: Still Not Getting It
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AI's second attempt - still wrong&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;signMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isPending&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSignMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// ... later&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;signMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// Still trying to await!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI was still treating &lt;code&gt;signMessage&lt;/code&gt; as if it returned a Promise, even after I pointed out it wasn't async.&lt;/p&gt;

&lt;h4&gt;
  
  
  Third Attempt: Finally Understanding the Hook Pattern
&lt;/h4&gt;

&lt;p&gt;Only after I explicitly explained that &lt;code&gt;signMessage&lt;/code&gt; is a function from a React hook (not an async function) did the AI implement the correct pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Correct implementation&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;signMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isPending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;signError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSignMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Use useEffect to listen for signature completion&lt;/span&gt;
&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;pendingNonce&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;handleSignatureComplete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pendingNonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pendingNonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// Trigger signing (non-blocking)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleSignAndLogin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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="c1"&gt;// ... get nonce&lt;/span&gt;
  &lt;span class="nf"&gt;signMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// This triggers MetaMask popup&lt;/span&gt;
  &lt;span class="c1"&gt;// Don't await this - it's not async!&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why This Happened
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. &lt;strong&gt;Pattern Recognition vs. Understanding&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The AI recognized common patterns (async/await for API calls) but didn't understand the specific React hook pattern for &lt;code&gt;useSignMessage&lt;/code&gt;. It applied the wrong mental model.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. &lt;strong&gt;Lack of Context Awareness&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Even when I mentioned "wagmi hook", the AI didn't connect this to the specific behavior of React hooks that trigger side effects rather than return promises.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. &lt;strong&gt;Overconfidence in Initial Solutions&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The AI presented its first solution with confidence, making it seem like the correct approach. This can lead developers to trust the solution without questioning it.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Correct Solution
&lt;/h3&gt;

&lt;p&gt;Here's how the authentication flow should actually work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;signMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isPending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;signError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSignMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Listen for signature completion&lt;/span&gt;
&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;pendingNonce&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;handleSignatureComplete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pendingNonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pendingNonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&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;handleSignAndLogin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get nonce from backend&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;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/auth/nonce&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;nonce&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Store nonce for later use&lt;/span&gt;
    &lt;span class="nf"&gt;setPendingNonce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Create message to sign&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Sign this message to authenticate: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nonce&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="c1"&gt;// Trigger signing (shows MetaMask popup)&lt;/span&gt;
    &lt;span class="nf"&gt;signMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Handle error&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;handleSignatureComplete&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&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;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Verify signature with backend&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/auth/verify&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="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;nonce&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Store JWT and update UI&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="s1"&gt;authToken&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setIsAuthenticated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Handle verification error&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setPendingNonce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Cursor's AI assistant is a powerful tool, but it's not a senior developer. It can help with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Code generation&lt;/li&gt;
&lt;li&gt;✅ Pattern suggestions&lt;/li&gt;
&lt;li&gt;✅ Boilerplate reduction&lt;/li&gt;
&lt;li&gt;✅ Documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it struggles with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Complex architectural decisions&lt;/li&gt;
&lt;li&gt;❌ Domain-specific patterns&lt;/li&gt;
&lt;li&gt;❌ Understanding context deeply&lt;/li&gt;
&lt;li&gt;❌ Making critical business logic decisions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The key takeaway&lt;/strong&gt;: Use Cursor's AI as a powerful junior developer that needs constant oversight, not as a replacement for understanding your code and your tools.&lt;/p&gt;

&lt;p&gt;Always question, always test, and always understand what you're building. The AI might write the code, but you're responsible for making sure it works correctly.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you had similar experiences with Cursor or other AI coding assistants? Share your stories in the comments below!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>web3</category>
      <category>react</category>
      <category>development</category>
    </item>
    <item>
      <title>Comparing Web3 Wallet Onboarding: Dynamic.xyz, Web3Auth.io, and Privy.io</title>
      <dc:creator>Rad Code</dc:creator>
      <pubDate>Tue, 23 Sep 2025 07:20:36 +0000</pubDate>
      <link>https://forem.com/heyradcode/comparing-web3-wallet-onboarding-dynamicxyz-web3authio-and-privyio-1018</link>
      <guid>https://forem.com/heyradcode/comparing-web3-wallet-onboarding-dynamicxyz-web3authio-and-privyio-1018</guid>
      <description>&lt;p&gt;Wallet onboarding solutions have improved significantly over the last couple of years. While Dynamic.xyz has been a popular option, there are some differences in behavior across platforms that are worth noting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Observed Behavior on Disconnect
&lt;/h2&gt;

&lt;p&gt;One example is how wallet disconnections are handled in MetaMask. In &lt;code&gt;@dynamic-labs/sdk-react-core@4.32.0&lt;/code&gt;, the &lt;code&gt;useIsLoggedIn()&lt;/code&gt; hook &lt;strong&gt;does not update to &lt;code&gt;false&lt;/code&gt;&lt;/strong&gt; when disconnecting a wallet. This behavior is noticeable not just in code but also on the &lt;a href="https://demo.dynamic.xyz/" rel="noopener noreferrer"&gt;Dynamic.xyz demo&lt;/a&gt; website, where disconnecting the wallet does not log out the user automatically.&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%2F5g8hk35rygvhboirf4y6.png" 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%2F5g8hk35rygvhboirf4y6.png" alt=" " width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In contrast, both &lt;a href="https://demo.privy.io/" rel="noopener noreferrer"&gt;Privy.io&lt;/a&gt; and &lt;a href="https://demo.web3auth.io/" rel="noopener noreferrer"&gt;Web3Auth&lt;/a&gt; demos handle disconnects as expected, updating the state and logging the user out when the wallet is disconnected.&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%2F2bo0merqj2nor1el2ip9.png" 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%2F2bo0merqj2nor1el2ip9.png" alt=" " width="800" height="428"&gt;&lt;/a&gt;&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%2Fzbscgt51sk3ieb7vgb1t.png" 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%2Fzbscgt51sk3ieb7vgb1t.png" alt=" " width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Web3Auth and Privy.io Are Favorable Choices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Web3Auth
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Developed by the Metamask team.&lt;/li&gt;
&lt;li&gt;Offers reliable wallet disconnection handling.&lt;/li&gt;
&lt;li&gt;Supports social login flows alongside decentralized wallets.&lt;/li&gt;
&lt;li&gt;Provides cross-platform support including browser, mobile, and Unity.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Privy.io
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Developed and backed by Stripe.&lt;/li&gt;
&lt;li&gt;Handles wallet disconnections reliably.&lt;/li&gt;
&lt;li&gt;Supports account abstraction and embedded wallets.&lt;/li&gt;
&lt;li&gt;Focuses on privacy-first authentication and encrypted data management.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From a personal perspective, backing by well-known teams adds confidence in terms of long-term support and reliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Dynamic.xyz&lt;/th&gt;
&lt;th&gt;Web3Auth&lt;/th&gt;
&lt;th&gt;Privy.io&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wallet disconnect sync&lt;/td&gt;
&lt;td&gt;Does not update&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Social login&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Account abstraction&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;Default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reliability&lt;/td&gt;
&lt;td&gt;Generally good&lt;/td&gt;
&lt;td&gt;Strong&lt;/td&gt;
&lt;td&gt;Strong&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backed by popular teams&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Metamask team&lt;/td&gt;
&lt;td&gt;Stripe&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;All three platforms offer wallet onboarding capabilities, but Web3Auth and Privy.io provide additional confidence with reliable disconnect handling and backing from prominent teams. They are favorable choices if you want consistent user experience and support for both social and decentralized login flows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://demo.dynamic.xyz/" rel="noopener noreferrer"&gt;Dynamic.xyz Demo&lt;/a&gt; | &lt;a href="https://demo.web3auth.io/" rel="noopener noreferrer"&gt;Web3Auth Demo&lt;/a&gt; | &lt;a href="https://demo.privy.io/" rel="noopener noreferrer"&gt;Privy.io Demo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ux</category>
      <category>javascript</category>
      <category>web3</category>
      <category>react</category>
    </item>
    <item>
      <title>Treat ChatGPT Like a Junior Dev: Helpful, But Needs Review</title>
      <dc:creator>Rad Code</dc:creator>
      <pubDate>Thu, 18 Sep 2025 16:56:17 +0000</pubDate>
      <link>https://forem.com/heyradcode/treat-chatgpt-like-a-junior-dev-helpful-but-needs-review-88j</link>
      <guid>https://forem.com/heyradcode/treat-chatgpt-like-a-junior-dev-helpful-but-needs-review-88j</guid>
      <description>&lt;p&gt;AI coding assistants like ChatGPT are everywhere now. They can scaffold components, generate test cases, and even debug code. But here’s the catch: they’re not senior engineers. They don’t have context of your project history, and they don’t automatically spot when the &lt;strong&gt;tests themselves are wrong&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;In other words: &lt;strong&gt;treat ChatGPT like a junior dev on your team&lt;/strong&gt; — helpful, but always needing review.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Experience: Fixing Legacy Code Against Broken Tests
&lt;/h2&gt;

&lt;p&gt;I was recently working on a legacy React form validation feature. The requirements were simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate name, email, employee ID, and joining date.
&lt;/li&gt;
&lt;li&gt;Show error messages until inputs are valid.
&lt;/li&gt;
&lt;li&gt;Enable submit only when everything passes.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tricky part? I didn’t just have to implement the form — I had to make it pass an &lt;strong&gt;existing test suite&lt;/strong&gt; that had been written years ago.  &lt;/p&gt;

&lt;p&gt;I turned to ChatGPT for help, thinking it could quickly draft a working component. It generated a solution — but when I ran the tests, they &lt;strong&gt;kept failing&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;At first, I thought maybe I had misunderstood the requirements, so I asked ChatGPT to debug. We went back and forth &lt;strong&gt;multiple times&lt;/strong&gt;. I provided more context, clarified each input validation rule, and even explained what the error messages should be. ChatGPT suggested fixes each time, but &lt;strong&gt;none of them worked&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;It wasn’t until I dug into the test suite myself that I realized the real problem: the tests were &lt;strong&gt;wrong&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Test That Broke Everything
&lt;/h2&gt;

&lt;p&gt;One test hard-coded &lt;code&gt;"2025-04-12"&lt;/code&gt; as a “future date”:&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="nf"&gt;changeInputFields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UserA&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user@email.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;123456&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2025-04-12&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputJoiningDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toHaveTextContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Joining Date cannot be in the future&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;The problem? We’re &lt;strong&gt;already past April 2025&lt;/strong&gt;. That date is no longer in the future, so the expected error message would never appear. The component was fine — the &lt;strong&gt;tests were broken&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;I had to dig through the logic, analyze the assumptions, and rewrite the test with &lt;strong&gt;relative dates&lt;/strong&gt;, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Corrected test using relative dates&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;futureDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;futureDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;futureDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// always 30 days ahead&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;futureDateStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;futureDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;changeInputFields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UserA&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user@email.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;123456&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;futureDateStr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Joining Date cannot be in the future&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="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This small change makes your test time-proof, so it will work regardless of the current year.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AI will follow broken requirements blindly&lt;/strong&gt;
ChatGPT can’t tell that a test is logically invalid. It will try to satisfy the failing test, even if the test itself makes no sense.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat output like a junior PR&lt;/strong&gt;
ChatGPT’s suggestions were helpful as scaffolding, but it struggled to see the root cause. I had to step in, dig through the legacy code, and analyze the tests myself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tests can rot too&lt;/strong&gt;
Hard-coded dates, magic numbers, or outdated assumptions make test suites brittle. If the tests are wrong, no amount of component fixes will help.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relative values keep tests reliable&lt;/strong&gt;
Replace absolute dates or values with calculations relative to today. This ensures your tests work across time.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  How to Work Effectively With AI Tools
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Give context&lt;/strong&gt;, but don’t rely on it to reason like a senior dev.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask “why”&lt;/strong&gt;, and inspect its explanations carefully.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate everything yourself&lt;/strong&gt; — especially when working with legacy code.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iteratively refine&lt;/strong&gt; — use AI as scaffolding, but you own the fix.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;My experience taught me a simple truth: AI can accelerate coding, but it &lt;strong&gt;cannot replace human judgment&lt;/strong&gt;, especially when dealing with messy, legacy code and outdated tests.  &lt;/p&gt;

&lt;p&gt;Treat ChatGPT like a junior teammate:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Helpful, eager to please, fast.
&lt;/li&gt;
&lt;li&gt;Sometimes confidently wrong.
&lt;/li&gt;
&lt;li&gt;Needs review, oversight, and occasionally, a reality check.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you keep that mindset, you’ll get the productivity boost without blindly following bad guidance — and you’ll know when to dig in yourself.&lt;/p&gt;




&lt;p&gt;💡 &lt;strong&gt;Takeaway:&lt;/strong&gt; When working with code, the human developer is still the ultimate problem-solver. AI is there to assist, not to replace your reasoning.&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>react</category>
      <category>unittest</category>
    </item>
  </channel>
</rss>
