<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aoda Zhang</title>
    <description>The latest articles on DEV Community by Aoda Zhang (@aoda-zhang).</description>
    <link>https://dev.to/aoda-zhang</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%2F912867%2F12b25170-07b0-4326-aebe-b2d3599ff490.jpg</url>
      <title>DEV Community: Aoda Zhang</title>
      <link>https://dev.to/aoda-zhang</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aoda-zhang"/>
    <language>en</language>
    <item>
      <title>How to sharing TypeScript codes Across Frontend and Backend in a Monorepo</title>
      <dc:creator>Aoda Zhang</dc:creator>
      <pubDate>Thu, 25 Dec 2025 15:02:25 +0000</pubDate>
      <link>https://dev.to/aoda-zhang/making-a-typescript-shared-package-work-across-frontend-and-backend-in-a-monorepo-1k23</link>
      <guid>https://dev.to/aoda-zhang/making-a-typescript-shared-package-work-across-frontend-and-backend-in-a-monorepo-1k23</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;In my monorepo project, pawHaven, the frontend and backend are not completely isolated systems.&lt;br&gt;&lt;br&gt;
They naturally share a portion of code, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Constants
&lt;/li&gt;
&lt;li&gt;Configuration schemas
&lt;/li&gt;
&lt;li&gt;Enums and dictionaries
&lt;/li&gt;
&lt;li&gt;Pure utility functions
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It seemed natural to extract these common pieces into a shared package and use it across both frontend and backend.&lt;/p&gt;

&lt;p&gt;Initially, this architecture appeared almost trivial to implement.  &lt;/p&gt;
&lt;h2&gt;
  
  
  With TypeScript, pnpm workspaces, and a monorepo already in place, it felt like everything was aligned.
&lt;/h2&gt;
&lt;h2&gt;
  
  
  When the Problems Started
&lt;/h2&gt;

&lt;p&gt;The real issues did not appear while writing the shared code, but when running the applications.&lt;/p&gt;

&lt;p&gt;After building the frontend and backend separately, I started encountering errors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node reported &lt;code&gt;Unexpected token 'export'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Frontend builds succeeded but failed at runtime
&lt;/li&gt;
&lt;li&gt;Some modules were reported as missing
&lt;/li&gt;
&lt;li&gt;CommonJS could not handle ESM syntax
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although these errors seemed random, the underlying issue was clear:&lt;br&gt;&lt;br&gt;
Frontend and backend expect completely different module systems.&lt;/p&gt;


&lt;h2&gt;
  
  
  My Initial Wrong Assumption
&lt;/h2&gt;

&lt;p&gt;I initially assumed it would be possible to produce a single build output compatible with both CommonJS and ESM.  &lt;/p&gt;

&lt;p&gt;I spent days experimenting with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;module: ESNext&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;module: CommonJS&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"type": "module"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Different &lt;code&gt;moduleResolution&lt;/code&gt; strategies
&lt;/li&gt;
&lt;li&gt;Various &lt;code&gt;tsconfig&lt;/code&gt; combinations
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After nearly three days of trial and error, it became clear:&lt;/p&gt;

&lt;p&gt;A single build output cannot satisfy both CommonJS and ESM.&lt;br&gt;&lt;br&gt;
These two targets are fundamentally incompatible.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Key Shift in Thinking
&lt;/h2&gt;

&lt;p&gt;The breakthrough came from a simple question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why must a shared package produce only one output?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Frontend and backend environments are inherently different:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Environment&lt;/th&gt;
&lt;th&gt;Module Expectation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Frontend (Vite/Webpack)&lt;/td&gt;
&lt;td&gt;ESM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node backend (Nest/require)&lt;/td&gt;
&lt;td&gt;CommonJS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Thus, the correct approach is to produce separate builds for each environment rather than compromise with a single artifact.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. One Source of Truth
&lt;/h2&gt;

&lt;p&gt;The shared package maintains a single source code base written in TypeScript using ESM syntax.&lt;br&gt;&lt;br&gt;
All code resides in a single &lt;code&gt;src&lt;/code&gt; directory and uses standard &lt;code&gt;export&lt;/code&gt; statements.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. Two TypeScript Configurations, Two Targets
&lt;/h2&gt;

&lt;p&gt;The package uses two separate TypeScript configurations:&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="err"&gt;packages/shared/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;├─&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tsconfig.esm.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;├─&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tsconfig.cjs.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;One configuration for ESM output targeting frontend and bundlers
&lt;/li&gt;
&lt;li&gt;One configuration for CommonJS output targeting Node.js backend
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This setup produces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An ESM build for frontend and bundlers
&lt;/li&gt;
&lt;li&gt;A CommonJS build for Node.js backend
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tsconfig.cjs.json&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;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@pawhaven/tsconfig/base"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&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;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/cjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CommonJS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&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;"exclude"&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="s2"&gt;"node_modules"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tsconfig.esm.json&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;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@pawhaven/tsconfig/base"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&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;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/esm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ESNext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bundler"&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;"exclude"&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="s2"&gt;"node_modules"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&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;h2&gt;
  
  
  3. Precise Entry Resolution via &lt;code&gt;package.json&lt;/code&gt;
&lt;/h2&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@pawhaven/shared"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/cjs/index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/esm/index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/index.d.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"exports"&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;"."&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;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/index.d.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"import"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/esm/index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"require"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/cjs/index.js"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&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="err"&gt;(https://dev-to-uploads.s&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;.amazonaws.com/uploads/articles/yi&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="err"&gt;mvpqhkigfvr&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="err"&gt;f&lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="err"&gt;ri.png)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;package.json&lt;/code&gt; defines which build is loaded for different consumers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;main&lt;/code&gt; points to the CommonJS build for Node.js (&lt;code&gt;require&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;module&lt;/code&gt; points to the ESM build for bundlers (&lt;code&gt;import&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;types&lt;/code&gt; points to the TypeScript declaration files for type checking
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;exports&lt;/code&gt; field ensures precise resolution:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Usage&lt;/th&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;import&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;exports.import&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ESM build&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;require&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;exports.require&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CommonJS build&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;&lt;code&gt;exports.types&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Declaration files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This guarantees both frontend and backend receive the correct implementation without runtime checks or environment variables.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;module&lt;/code&gt;, and &lt;code&gt;types&lt;/code&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;main&lt;/strong&gt;: Used by Node.js in CommonJS mode; loaded when calling &lt;code&gt;require()&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;module&lt;/strong&gt;: Used by bundlers to indicate an ESM entry point and enable tree-shaking; ignored by Node.js runtime.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;types&lt;/strong&gt;: Used by TypeScript at compile time; provides type declarations for both frontend and backend; independent of runtime.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why This Approach Is Stable
&lt;/h2&gt;

&lt;p&gt;Module selection happens at &lt;strong&gt;module resolution time&lt;/strong&gt;, not runtime.  &lt;/p&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No conditional logic in application code
&lt;/li&gt;
&lt;li&gt;No environment-dependent hacks
&lt;/li&gt;
&lt;li&gt;Fully deterministic builds across local and CI environments
&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;These three days of trial and error taught me a crucial lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The challenge of shared packages in a monorepo is not code reuse, but defining clear module boundaries.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A robust shared package should provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single source of truth
&lt;/li&gt;
&lt;li&gt;Multiple explicit build outputs
&lt;/li&gt;
&lt;li&gt;Consumption strictly controlled via &lt;code&gt;exports&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trying to fit all environments into a single artifact leads to conflicts.  &lt;/p&gt;

&lt;p&gt;The dual-build strategy is one of the most reliable and maintainable patterns for shared modules in large-scale monorepos.&lt;/p&gt;

&lt;p&gt;If you want to see this in practice, feel free to check out my real-world monorepo project for stray animal rescue. &lt;a href="https://github.com/aoda-zhang/PawHaven-Enterprise-FullStack-React-NestJS/tree/main/packages/shared" rel="noopener noreferrer"&gt;pawhaven-sahred&lt;/a&gt;&lt;/p&gt;

</description>
      <category>monorepo</category>
      <category>programming</category>
      <category>javascript</category>
      <category>architecture</category>
    </item>
    <item>
      <title>From Pain Points to Practice: Monorepo Architecture in PawHaven</title>
      <dc:creator>Aoda Zhang</dc:creator>
      <pubDate>Fri, 17 Oct 2025 14:27:24 +0000</pubDate>
      <link>https://dev.to/aoda-zhang/from-pain-points-to-practice-monorepo-architecture-in-pawhaven-70d</link>
      <guid>https://dev.to/aoda-zhang/from-pain-points-to-practice-monorepo-architecture-in-pawhaven-70d</guid>
      <description>&lt;p&gt;In modern full-stack projects, frontend, backend, shared libraries, and utility scripts are often tightly interwoven. As features grow and teams expand, using multiple independent repositories (polyrepo) can lead to several challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inconsistent versions of shared modules
&lt;/li&gt;
&lt;li&gt;Confusing imports and mismatched types
&lt;/li&gt;
&lt;li&gt;Difficulty keeping cross-project refactors consistent
&lt;/li&gt;
&lt;li&gt;Scattered and hard-to-maintain configuration
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Against this backdrop, &lt;strong&gt;Monorepo&lt;/strong&gt;—keeping multiple sub-projects in a single repository—becomes an attractive solution.&lt;br&gt;&lt;br&gt;
However, it’s a misconception that &lt;em&gt;Monorepo fits all projects&lt;/em&gt;. In this article, we will cover:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Suitable scenarios for Monorepo
&lt;/li&gt;
&lt;li&gt;Why PawHaven chose Monorepo
&lt;/li&gt;
&lt;li&gt;Architecture design and directory structure
&lt;/li&gt;
&lt;li&gt;Configuration reuse practices
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. When is Monorepo Suitable?
&lt;/h2&gt;

&lt;p&gt;Monorepo has clear boundaries for its effectiveness. The following scenarios benefit the most:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Multiple tightly-coupled sub-projects/modules&lt;/td&gt;
&lt;td&gt;Need to share types, DTOs, utilities, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frequent cross-module changes/refactoring&lt;/td&gt;
&lt;td&gt;One commit can update multiple packages simultaneously&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Team coordination is manageable&lt;/td&gt;
&lt;td&gt;Frequent inter-module collaboration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Desire for unified CI/CD pipelines&lt;/td&gt;
&lt;td&gt;Shared pipelines, caching, and dependency management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Want to reuse configuration and toolchains&lt;/td&gt;
&lt;td&gt;Centralized management for ESLint, tsconfig, styles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reduce version conflicts&lt;/td&gt;
&lt;td&gt;Shared dependencies are easier to align in one repo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Situations where Monorepo may not be suitable:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small projects with simple module relationships
&lt;/li&gt;
&lt;li&gt;Modules with almost no shared logic
&lt;/li&gt;
&lt;li&gt;Significantly different tech stacks making unified config difficult
&lt;/li&gt;
&lt;li&gt;Distributed teams requiring strict permission separation
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Challenges to be aware of:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
As the repository grows, build, clone, and CI times may increase.&lt;br&gt;&lt;br&gt;
Access control, dependency boundaries, and tooling also become more complex.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Conclusion&lt;/strong&gt;: Monorepo isn’t inherently “better” or “advanced”; it should be adopted after carefully weighing the benefits against the added complexity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  2. Why PawHaven Chose Monorepo
&lt;/h2&gt;

&lt;p&gt;PawHaven is a full-stack project consisting of &lt;strong&gt;React frontend + NestJS backend + shared libraries&lt;/strong&gt;. The decision to adopt Monorepo was driven by several factors:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Shared Types, DTOs, and Utilities
&lt;/h3&gt;

&lt;p&gt;Interfaces, constants, and types shared between frontend and backend can be maintained in a single repository. TypeScript path aliases or workspace references avoid duplicate maintenance.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Unified Configuration Management
&lt;/h3&gt;

&lt;p&gt;Centralized management of ESLint, tsconfig, Tailwind, etc., allows sub-packages to inherit configurations directly, maintaining consistency and reducing maintenance costs.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Cross-Package Changes in a Single Commit
&lt;/h3&gt;

&lt;p&gt;When shared components, DTOs, or backend interfaces need synchronized updates, a single PR can handle all changes, ensuring compatibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Centralized CI/CD Pipeline
&lt;/h3&gt;

&lt;p&gt;Build, test, and deployment processes are defined once at the repo level, with shared caching and toolchains, simplifying continuous integration.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Dependency Hoisting and Version Consistency
&lt;/h3&gt;

&lt;p&gt;Using pnpm's hoist mechanism, all sub-packages share the same dependency versions, minimizing conflicts.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Single Entry Point Reduces Communication Overhead
&lt;/h3&gt;

&lt;p&gt;For an open-source project, having one repository means developers, documentation, and promotional materials only need to focus on a single source, reducing coordination overhead across multiple repos.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Monorepo Structure Design &amp;amp; PawHaven Example
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Design Principles
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Separation of business and non-business code:&lt;/strong&gt; Clearly distinguish deployable apps from shared libraries
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralized configuration:&lt;/strong&gt; ESLint, tsconfig, Tailwind maintained in independent config packages
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent structure:&lt;/strong&gt; Each sub-package follows uniform directory conventions (&lt;code&gt;src/&lt;/code&gt;, &lt;code&gt;tsconfig.json&lt;/code&gt;, &lt;code&gt;package.json&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified imports:&lt;/strong&gt; Use alias/workspace references instead of deep relative paths
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Toolchain compatibility:&lt;/strong&gt; Build, type-check, and lint processes cover cross-package paths
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🧭 PawHaven Directory Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├─ pnpm-lock.yaml
├─ pnpm-workspace.yaml
├─ apps
│  ├─ backend
│  │  ├─ gateway
│  │  ├─ ms-auth
│  │  ├─ ms-document
│  │  └─ ms-pawhaven
│  └─ frontend
│     ├─ user
│     └─ admin
├─ packages
│  ├─ shared-frontend
│  ├─ i18n
│  ├─ shared-backend
│  ├─ theme
│  └─ ui
├─ libs
│  └─ configs
│     ├─ eslint-config
│     └─ tsconfig
├─ .vscode/
│  ├─ settings.json
│  └─ extensions.json

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Directory Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;apps/&lt;/code&gt;: Independent frontend and backend business modules
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;packages/&lt;/code&gt;: Shared modules related to business (UI, theme, i18n, shared types)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;libs/&lt;/code&gt;: Utility and configuration libraries, can be independently published
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Unified Configuration (ESLint / tsconfig / Tailwind)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1️⃣ ESLint
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Provide consistent rules for both Web and Node environments.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralized ESLint configs in &lt;code&gt;libs/configs/eslint/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Separate configs for Web (&lt;code&gt;eslint-config-web&lt;/code&gt;) and Node (&lt;code&gt;eslint-config-node&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;Sub-packages inherit and can override rules as needed
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📦 Reference: &lt;a href="https://github.com/aoda-zhang/PawHaven/tree/main/libs/configs/eslint-config" rel="noopener noreferrer"&gt;PawHaven/libs/eslint&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2️⃣ tsconfig
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Unified TypeScript compilation options with support for multiple targets (Web/Node).&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Base configuration in &lt;code&gt;libs/configs/tsconfig/base.json&lt;/code&gt; with environment-specific extensions
&lt;/li&gt;
&lt;li&gt;Sub-packages only override output paths, rootDir, or other necessary options
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📦 Reference: &lt;a href="https://github.com/aoda-zhang/PawHaven/tree/main/libs/configs/tsconfig" rel="noopener noreferrer"&gt;PawHaven/libs/tsconfig&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3️⃣ Tailwind Design System
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Ensure consistent styling across all frontend packages and UI components.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralized design tokens and global Tailwind config in &lt;code&gt;packages/theme/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Shared theme between UI library and frontend apps
&lt;/li&gt;
&lt;li&gt;Ensure content scanning paths include shared component files
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📦 Reference: &lt;a href="https://github.com/aoda-zhang/PawHaven/tree/main/packages/theme" rel="noopener noreferrer"&gt;PawHaven/packages/theme&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Conclusion
&lt;/h2&gt;

&lt;p&gt;PawHaven’s Monorepo architecture enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralized multi-module management and collaboration
&lt;/li&gt;
&lt;li&gt;Unified ESLint, TypeScript, and Tailwind configurations
&lt;/li&gt;
&lt;li&gt;Shared types and consistent dependencies between frontend and backend
&lt;/li&gt;
&lt;li&gt;Improved development efficiency and maintainability
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Key Takeaway:&lt;/strong&gt; Monorepo is not about being “advanced” but about achieving coordination.&lt;br&gt;&lt;br&gt;
Careful evaluation of project scale, team structure, and maintenance costs is essential before adoption.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;💡 &lt;strong&gt;Further Reading &amp;amp; Open Source Practice&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
For more enterprise-level full-stack, frontend, backend, and Monorepo practices, you can &lt;strong&gt;Star or Watch&lt;/strong&gt; the repository:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://github.com/aoda-zhang/PawHaven" rel="noopener noreferrer"&gt;https://github.com/aoda-zhang/PawHaven&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>monorepo</category>
      <category>programming</category>
    </item>
    <item>
      <title>💻 Doing Something Good with Code</title>
      <dc:creator>Aoda Zhang</dc:creator>
      <pubDate>Tue, 07 Oct 2025 06:32:48 +0000</pubDate>
      <link>https://dev.to/aoda-zhang/doing-something-good-with-code-13fj</link>
      <guid>https://dev.to/aoda-zhang/doing-something-good-with-code-13fj</guid>
      <description>&lt;p&gt;We write code every day to make products faster, features cooler, and systems more efficient.  &lt;/p&gt;

&lt;p&gt;But what if code could also make the world a little softer?&lt;/p&gt;

&lt;h2&gt;
  
  
  🐾 A Stray Cat Changed Everything
&lt;/h2&gt;

&lt;p&gt;One rainy night, my wife (a veterinarian) and I heard a faint “meow—meow—” in a dark alley.  &lt;/p&gt;

&lt;p&gt;We found a tiny, soaked kitten, shivering and exhausted. I wrapped it in my jacket and drove through the rain to an open animal hospital.  &lt;/p&gt;

&lt;p&gt;The vet said it was severely dehydrated. We stayed on the cold floor until morning.  &lt;/p&gt;

&lt;p&gt;My wife whispered:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Some people treat animals like family.&lt;br&gt;&lt;br&gt;
Others treat them like trash.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That night, I wondered — could I use code to make a difference?&lt;/p&gt;

&lt;h2&gt;
  
  
  🏠 Our Two Cats
&lt;/h2&gt;

&lt;p&gt;A few months later, our family grew by two:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One abandoned at a clinic
&lt;/li&gt;
&lt;li&gt;One found tangled in a plastic string next to a trash bin
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now they’re healthy and spoiled, sleeping next to my keyboard or on the balcony.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every time I watch them nap peacefully, I know — this is worth it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  💡 From Rescue to PawHaven
&lt;/h2&gt;

&lt;p&gt;Over the years, we rescued more animals, but always faced the same problems:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📍 Didn’t know who nearby could help
&lt;/li&gt;
&lt;li&gt;📷 No easy way to record or share rescue info
&lt;/li&gt;
&lt;li&gt;💬 Everything relied on private chats or social media
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I built &lt;strong&gt;PawHaven&lt;/strong&gt; 🐕 — “a safe haven for paws”.&lt;/p&gt;

&lt;p&gt;On PawHaven, you can:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📸 Upload photos of rescued animals
&lt;/li&gt;
&lt;li&gt;📍 Mark their location
&lt;/li&gt;
&lt;li&gt;📖 Record rescue stories
&lt;/li&gt;
&lt;li&gt;🤝 Connect with volunteers, vets, and rescuers nearby
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;A space where kindness travels faster.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  💻 The Technical Side
&lt;/h2&gt;

&lt;p&gt;PawHaven is a &lt;strong&gt;React + Node.js (NestJS) full-stack open-source project&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚙️ &lt;strong&gt;Frontend&lt;/strong&gt; – React + TypeScript + pnpm monorepo
&lt;/li&gt;
&lt;li&gt;🧩 &lt;strong&gt;Backend&lt;/strong&gt; – NestJS microservices
&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Deployment&lt;/strong&gt; – Azure Kubernetes + GitHub Actions CI/CD
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Developers can learn practical full-stack practices — from monorepo design and RPC communication to CI/CD and cloud deployment.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where code meets compassion.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🌈 In the End
&lt;/h2&gt;

&lt;p&gt;No funding, no ads — just a small dream from a developer who loves animals.  &lt;/p&gt;

&lt;p&gt;If you believe technology can make the world a little better:&lt;br&gt;&lt;br&gt;
🌟 &lt;a href="https://github.com/aoda-zhang/PawHaven" rel="noopener noreferrer"&gt;Check out PawHaven&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;Give it a &lt;strong&gt;Star ⭐️&lt;/strong&gt;, share it, or contribute a PR.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sometimes, a single line of code can help save a life 🐶🐱&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Thoughts and Practices on Enterprise-Level Project Architecture — A Case Study of PawHaven</title>
      <dc:creator>Aoda Zhang</dc:creator>
      <pubDate>Wed, 01 Oct 2025 05:48:42 +0000</pubDate>
      <link>https://dev.to/aoda-zhang/thoughts-and-practices-on-enterprise-level-project-architecture-a-case-study-of-pawhaven-1a4n</link>
      <guid>https://dev.to/aoda-zhang/thoughts-and-practices-on-enterprise-level-project-architecture-a-case-study-of-pawhaven-1a4n</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;PawHaven is an open-source full-stack project built with React and Node.js. With this project, you can learn how to build an enterprise-grade application from scratch — covering everything from frontend and backend development to CI/CD pipelines and cloud deployment, along with best practices at every stage.&lt;/p&gt;

&lt;p&gt;This article is based on the architecture and practical experience of PawHaven. If you’d like to explore more runnable code examples, feel free to check out the &lt;a href="https://github.com/aoda-zhang/PawHaven" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt; and give it a star ⭐️.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1. Project Background and Design Goals
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;PawHaven&lt;/strong&gt; at the design stage, I defined several key goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High scalability&lt;/strong&gt;: New feature modules such as a donation system, volunteer management, and mobile applications may be added in the future.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High maintainability&lt;/strong&gt;: Support long-term operation and multi-developer collaboration, reducing maintenance costs.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contributor-friendly for global developers&lt;/strong&gt;: The project is open source and may attract contributors from different regions. The code structure must be clear and standardized for quick onboarding.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These goals determined that PawHaven could not follow a temporary, scattered project structure and must adopt an enterprise-level architecture from the start.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Common Project Structures and Their Limitations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Traditional “Functional-Type” Structure
&lt;/h3&gt;

&lt;p&gt;Many tutorials recommend a structure like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;src&lt;/span&gt;
&lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;components&lt;/span&gt;
&lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;assets&lt;/span&gt;
&lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;pages&lt;/span&gt;
&lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;utils&lt;/span&gt;
&lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;apis&lt;/span&gt;
&lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Advantages: Simple and intuitive, suitable for small projects or rapid prototyping.&lt;/p&gt;

&lt;p&gt;Limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scattered logic: Code for a single business module may be spread across pages, store, components, and services.&lt;/li&gt;
&lt;li&gt;High modification cost: Every time a feature needs to be modified or debugged, files must be searched in multiple directories.&lt;/li&gt;
&lt;li&gt;Blurred business boundaries: Modules can become coupled, reducing team efficiency and making division of work difficult.&lt;/li&gt;
&lt;li&gt;Difficult to scale: Adding new features often requires creating files in multiple directories.&lt;/li&gt;
&lt;li&gt;Hard to maintain: In large legacy projects, this structure significantly increases maintenance costs and communication overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.2 DDD (Domain-Driven Design) Pattern
&lt;/h3&gt;

&lt;p&gt;Complex enterprise projects usually adopt Domain-Driven Design (DDD):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Top-level directories are business modules (features)&lt;/li&gt;
&lt;li&gt;Each feature contains all related logic: components, pages, state, services, types&lt;/li&gt;
&lt;li&gt;Shared modules only contain cross-feature reusable functionality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Self-contained modules: modifying a feature does not require searching across directories&lt;/li&gt;
&lt;li&gt;Clear business boundaries: team members can independently develop or test modules&lt;/li&gt;
&lt;li&gt;High scalability: new business modules can be added smoothly&lt;/li&gt;
&lt;li&gt;Easy maintenance: reduces coupling and improves readability&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.3 PawHaven’s DDD Implementation
&lt;/h3&gt;

&lt;p&gt;In PawHaven, the frontend uses a feature-first directory design:&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;user&lt;/span&gt;
&lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;features&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;RescueDetail&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsx&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;rescueDetailSlice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;components&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;RescueTimeline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsx&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;AnimalBasicInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsx&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;└─&lt;/span&gt; &lt;span class="nx"&gt;RescueInteraction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsx&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;└─&lt;/span&gt; &lt;span class="nx"&gt;apis&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;     &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;     &lt;span class="err"&gt;└─&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;ReportStray&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsx&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;constants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;reportStraySlice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;components&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;└─&lt;/span&gt; &lt;span class="nx"&gt;ReportForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsx&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;└─&lt;/span&gt; &lt;span class="nx"&gt;apis&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;     &lt;span class="err"&gt;├─&lt;/span&gt; &lt;span class="nx"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;  &lt;span class="err"&gt;│&lt;/span&gt;     &lt;span class="err"&gt;└─&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each feature (e.g., RescueDetail, ReportStray) contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Components (components)&lt;/li&gt;
&lt;li&gt;Pages (index.tsx)&lt;/li&gt;
&lt;li&gt;State management (slice)&lt;/li&gt;
&lt;li&gt;API requests (apis)&lt;/li&gt;
&lt;li&gt;Type definitions (types.ts)&lt;/li&gt;
&lt;li&gt;Constants (constants.ts)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This design ensures each business module is highly self-contained, facilitating independent development and maintenance by team members.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Component Layering Strategy
&lt;/h2&gt;

&lt;p&gt;In enterprise projects, as features grow more complex, DDD alone is not enough. A layering strategy is needed. In PawHaven, layering applies not only to UI components but to all code units, including hooks, services, and utility functions.&lt;/p&gt;

&lt;p&gt;PawHaven’s layering strategy:&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Feature Layer (Internal to Feature)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Closely tied to business logic&lt;/li&gt;
&lt;li&gt;Contains pages, business components, hooks, services, types&lt;/li&gt;
&lt;li&gt;Example: report-animal/ReportForm.tsx, report-animal/hooks/useReportAnimal.ts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.2 Cross-Feature Layer (Application Level)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Shared across multiple features but still contextually tied to business&lt;/li&gt;
&lt;li&gt;Examples: user info cards, navigation menus, layout components, cross-feature hooks&lt;/li&gt;
&lt;li&gt;Located at the application level, outside individual features&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.3 Shared Layer (Business-agnostic)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Completely decoupled from business logic&lt;/li&gt;
&lt;li&gt;Includes UI dumb components (Button, Modal, Input), utility functions, generic hooks, constants, theme configs, internationalization text, etc.&lt;/li&gt;
&lt;li&gt;Reusable across the entire monorepo and can be easily extracted into independent libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This layering strategy ensures clear business boundaries while maximizing code reuse, achieving high scalability and maintainability.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Monorepo vs Multi-Repo
&lt;/h2&gt;

&lt;p&gt;In enterprise projects, managing multiple apps and modules is a key decision. Common approaches are Multi-Repo and Monorepo.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Multi-Repo
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Each service or business has a separate repository, with tool libraries in their own repos&lt;/li&gt;
&lt;li&gt;Advantages: independent repositories, clear permission control, low initial learning curve&lt;/li&gt;
&lt;li&gt;Disadvantages: hard to share code, scattered dependencies, complex cross-repo refactoring&lt;/li&gt;
&lt;li&gt;Examples: Netflix, Spotify microservices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have personally experienced the pain of Multi-Repo, where synchronizing shared code across projects was manual and error-prone, and frequent switching between repos reduced development efficiency.&lt;/p&gt;

&lt;h2&gt;
  
  
  4.2 Monorepo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Multiple apps, services, and libraries are managed in a single repository&lt;/li&gt;
&lt;li&gt;Advantages: easy code sharing, unified dependencies and rules, simple cross-project refactoring, improved developer experience&lt;/li&gt;
&lt;li&gt;Disadvantages: requires more advanced tooling; build and test processes need optimization&lt;/li&gt;
&lt;li&gt;Examples: Google, Meta, Uber, Airbnb&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4.3 Why PawHaven Chose Monorepo
&lt;/h2&gt;

&lt;p&gt;Considering PawHaven’s development roadmap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple frontend apps: user app (pawHaven), admin panel (admin), auth app (auth)&lt;/li&gt;
&lt;li&gt;Multiple backend microservices (NestJS) and shared packages&lt;/li&gt;
&lt;li&gt;Potential global contributors requiring a clear, standardized structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Monorepo is therefore the most suitable choice.&lt;/p&gt;

&lt;p&gt;Directory structure:&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;PawHaven&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;frontend&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;apps&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;                  &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Applications&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;              &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;             &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Admin&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;              &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Shared&lt;/span&gt; &lt;span class="nx"&gt;modules&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;hooks&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;constants&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;i18n&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="kr"&gt;package&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;pnpm&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.4 Why pnpm Workspace
&lt;/h3&gt;

&lt;p&gt;There are multiple Monorepo tools (Lerna, Nx, Turborepo). I chose pnpm workspace for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast installation: hard links and content-addressable storage save disk space and time&lt;/li&gt;
&lt;li&gt;Strict dependency isolation: avoids “phantom dependencies”&lt;/li&gt;
&lt;li&gt;Native workspace support: lightweight, simple configuration&lt;/li&gt;
&lt;li&gt;Enterprise compatibility: widely adopted in large companies and community projects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example configuration:&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="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;pnpm&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yaml&lt;/span&gt;
&lt;span class="nx"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apps/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;packages/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup allows multiple frontend or backend services to collaborate in a single repository while maintaining independent build and deployment pipelines.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Summary
&lt;/h2&gt;

&lt;p&gt;PawHaven demonstrates that enterprise-level architecture is not just about organizing code—it’s about scalability, maintainability, and team efficiency.&lt;/p&gt;

&lt;p&gt;From the start, PawHaven adopted a DDD / Feature-first approach with self-contained modules, ensuring clear business boundaries; combined with a component layering strategy (Feature layer, Cross-Feature layer, Shared layer) to maximize reuse; and implemented Monorepo + pnpm workspace to unify frontend, backend, and shared modules while keeping responsibilities separate.&lt;/p&gt;

&lt;p&gt;This architecture not only facilitates current development but also lays a solid foundation for future feature expansion, team collaboration, and global contributor participation.&lt;/p&gt;

&lt;p&gt;Ultimately, PawHaven provides a practical example of building an enterprise-level full-stack project from scratch: define clear boundaries, structure layers effectively, reuse shared modules, and unify management for sustainable long-term operation.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>architecture</category>
    </item>
    <item>
      <title>That Weird Font Size Bug: A Deep Dive into Browser Settings and Front-End Font Strategies</title>
      <dc:creator>Aoda Zhang</dc:creator>
      <pubDate>Fri, 26 Sep 2025 05:52:14 +0000</pubDate>
      <link>https://dev.to/aoda-zhang/that-weird-font-size-bug-a-deep-dive-into-browser-settings-and-front-end-font-strategies-7kp</link>
      <guid>https://dev.to/aoda-zhang/that-weird-font-size-bug-a-deep-dive-into-browser-settings-and-front-end-font-strategies-7kp</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;PawHaven is an open-source full-stack project built with React and Node.js. With this project, you can learn how to build an enterprise-grade application from scratch — covering everything from frontend and backend development to CI/CD pipelines and cloud deployment, along with best practices at every stage.&lt;/p&gt;

&lt;p&gt;This article is based on the architecture and practical experience of PawHaven. If you’d like to explore more runnable code examples, feel free to check out the &lt;a href="https://github.com/aoda-zhang/PawHaven-frontEnd" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt; and give it a star ⭐️.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A few days ago, I stumbled upon a bug that, at first, looked pretty trivial — but it ended up teaching me a lot about how browsers handle fonts.  &lt;/p&gt;

&lt;p&gt;One of our QA testers told me: &lt;em&gt;“Hey, this page’s font size looks wrong.”&lt;/em&gt; I checked the page myself and everything looked fine. Fonts matched the design specs perfectly. For a second, I honestly thought they were mistaken.  &lt;/p&gt;

&lt;p&gt;But then I asked them to share their screen. Boom — on their machine, the font size was &lt;strong&gt;20px&lt;/strong&gt;, while on mine it was &lt;strong&gt;16px&lt;/strong&gt;. Same page, totally different look.  &lt;/p&gt;

&lt;p&gt;At that point, I knew something funky was going on. I switched to another browser, and sure enough, the bug showed up again. After a bit of digging, I realized the culprit: &lt;strong&gt;Chrome’s default font size setting&lt;/strong&gt;. Turns out, Chrome (and other browsers) let users customize their default font size, and if you’re using &lt;code&gt;rem&lt;/code&gt; without defining a root &lt;code&gt;font-size&lt;/code&gt;, your entire design can shift.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core of &lt;code&gt;rem&lt;/code&gt;: It All Starts with the Root
&lt;/h2&gt;

&lt;p&gt;Here’s the deal: &lt;code&gt;rem&lt;/code&gt; units depend on the root element’s &lt;code&gt;font-size&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;If you explicitly define it, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then your design is safe — user browser settings won’t mess with it. But if you don’t, you’re leaving it up to the browser.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Different Sites, Different Philosophies
&lt;/h2&gt;

&lt;p&gt;Curious, I went down the rabbit hole and checked a few popular websites to see how they handle this.  &lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub &amp;amp; YouTube
&lt;/h3&gt;

&lt;p&gt;Their font sizes don’t change when you tweak the browser’s font settings. They’ve locked down the root &lt;code&gt;font-size&lt;/code&gt;, which guarantees &lt;strong&gt;pixel-perfect consistency&lt;/strong&gt;. Makes sense for design-driven products where brand identity matters a lot.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Shopify
&lt;/h3&gt;

&lt;p&gt;On the flip side, Shopify actually responds to browser font settings. If you bump up your default font size, their site adjusts accordingly. That’s a &lt;strong&gt;user-first, accessibility-friendly approach&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;Neither is “right” or “wrong.” It’s all about what the product values more: consistency or accessibility.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility vs. Consistency: The Eternal Trade-Off
&lt;/h2&gt;

&lt;p&gt;So here’s the big question: &lt;strong&gt;Should we let users change font sizes through their browser settings?&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From an &lt;strong&gt;accessibility perspective&lt;/strong&gt;, yes! It helps people with vision impairments and respects their reading habits.
&lt;/li&gt;
&lt;li&gt;From a &lt;strong&gt;design/brand perspective&lt;/strong&gt;, not so much. Changing font sizes can break layouts, shift components, and ruin carefully crafted visuals.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 My takeaway:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Content-driven sites&lt;/strong&gt; (blogs, e-commerce) → better to respect user settings.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design-driven products&lt;/strong&gt; (brand sites, design tools) → safer to lock fonts for consistency.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Designers vs. Developers: The px → rem Struggle
&lt;/h2&gt;

&lt;p&gt;If you’ve worked on front-end projects, you probably know this pain: designers hand over mockups in &lt;code&gt;px&lt;/code&gt;, but developers often need to translate them into &lt;code&gt;rem&lt;/code&gt;. That conversion step is error-prone and slows everyone down.  &lt;/p&gt;

&lt;h3&gt;
  
  
  What’s worked for me:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Decide early whether you want to support user font scaling.
&lt;/li&gt;
&lt;li&gt;If you go with &lt;code&gt;rem&lt;/code&gt;, ask designers to also use &lt;code&gt;rem&lt;/code&gt; or at least provide conversion references.
&lt;/li&gt;
&lt;li&gt;Keep units consistent across design and code.
&lt;/li&gt;
&lt;li&gt;Document your project’s font strategy upfront (seriously, this saves so many headaches later).
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping Up: What This Bug Taught Me
&lt;/h2&gt;

&lt;p&gt;That one “tiny” bug turned into a pretty valuable lesson:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser settings can unexpectedly affect your UI.
&lt;/li&gt;
&lt;li&gt;If you’re using &lt;code&gt;rem&lt;/code&gt;, always define the root &lt;code&gt;font-size&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Accessibility and consistency are often at odds — you have to pick what matters for your product.
&lt;/li&gt;
&lt;li&gt;The way big sites (GitHub, YouTube, Shopify) handle fonts reflects their product philosophy.
&lt;/li&gt;
&lt;li&gt;Aligning designers and developers early on avoids painful conversions and layout issues.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;My personal rule of thumb:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If your product needs to look exactly like the design mockups → set root &lt;code&gt;font-size&lt;/code&gt; explicitly.
&lt;/li&gt;
&lt;li&gt;If your product leans toward accessibility → let the browser handle it.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This whole experience reminded me that even “small” bugs can uncover bigger product questions. Fonts aren’t just about styling — they’re about philosophy: who are you optimizing for, the brand or the user?  &lt;/p&gt;

&lt;p&gt;And honestly, that’s the kind of stuff that makes front-end dev both challenging and fun. 🚀&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Have you ever run into font-size issues caused by browser settings? How did you handle it?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Let me know in the comments — I’d love to hear your take!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>👋 Hi, I’m Aoda — Excited to Join dev.to!</title>
      <dc:creator>Aoda Zhang</dc:creator>
      <pubDate>Thu, 25 Sep 2025 10:16:50 +0000</pubDate>
      <link>https://dev.to/aoda-zhang/hi-im-aoda-excited-to-join-devto-2l64</link>
      <guid>https://dev.to/aoda-zhang/hi-im-aoda-excited-to-join-devto-2l64</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;PawHaven is an open-source full-stack project built with React and Node.js that i am building. With this project, you can learn how to build an enterprise-grade application from scratch — covering everything from frontend and backend development to CI/CD pipelines and cloud deployment, along with best practices at every stage.&lt;/p&gt;

&lt;p&gt;If you’d like to explore more runnable code examples, feel free to check out the &lt;a href="https://github.com/aoda-zhang/PawHaven" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt; and give it a star ⭐️.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hello everyone!&lt;br&gt;&lt;br&gt;
I’m really happy to finally write my very first post here on dev.to. Since this is my introduction, let me share a bit about who I am, what I’m working on, and what I hope to learn and give back to this amazing community.  &lt;/p&gt;




&lt;h2&gt;
  
  
  🧑‍💻 Who am I?
&lt;/h2&gt;

&lt;p&gt;I’m &lt;strong&gt;Aoda&lt;/strong&gt;, a software developer who has been building with JavaScript for many years. I enjoy turning ideas into digital products and exploring how technology can improve everyday life. Over the years, I’ve worked with &lt;strong&gt;React, Node.js, NestJS, TypeScript&lt;/strong&gt;, and various tools for &lt;strong&gt;frontend/backend architecture, monorepos, and cloud deployment&lt;/strong&gt;. more &lt;a href="https://aoda.vercel.app" rel="noopener noreferrer"&gt;about me&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But beyond the tech stack, what excites me most is &lt;strong&gt;problem-solving&lt;/strong&gt; and &lt;strong&gt;sharing knowledge&lt;/strong&gt;—whether it’s through open-source projects, technical writing, or community discussions.  &lt;/p&gt;




&lt;h2&gt;
  
  
  🐾 What I’m working on
&lt;/h2&gt;

&lt;p&gt;Currently, I’m creating &lt;strong&gt;PawHaven&lt;/strong&gt;, an open-source platform designed to help stray animals.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A place where people can &lt;strong&gt;share information about rescues and adoptions&lt;/strong&gt;,
&lt;/li&gt;
&lt;li&gt;connect with others who care about animals, and
&lt;/li&gt;
&lt;li&gt;hopefully, make a real-world difference.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This project challenges me not only technically (React, Node.js, Kubernetes, CI/CD, i18n, etc.) but also personally—it’s about combining &lt;strong&gt;code with compassion&lt;/strong&gt;.   &lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Why I’m here
&lt;/h2&gt;

&lt;p&gt;I joined dev.to because I believe in &lt;strong&gt;learning in public&lt;/strong&gt;. Writing about the challenges I face (and solutions I find) not only helps me grow, but also hopefully helps someone else who might run into the same issues.  &lt;/p&gt;

&lt;p&gt;I’ll be sharing posts about:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open-source journey building PawHaven
&lt;/li&gt;
&lt;li&gt;Frontend architecture patterns in React
&lt;/li&gt;
&lt;li&gt;DevOps practices for small projects
&lt;/li&gt;
&lt;li&gt;Reflections on tech, learning, and career growth
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🤝 Let’s connect!
&lt;/h2&gt;

&lt;p&gt;I’d love to hear from you—whether you’re working on similar projects, curious about modern JavaScript stacks, or just want to chat about animals 🐕🐈.  &lt;/p&gt;

&lt;p&gt;Thanks for reading my first post here. I’m really looking forward to learning from this community and contributing my part!  &lt;/p&gt;




&lt;h2&gt;
  
  
  ⭐ Support PawHaven
&lt;/h2&gt;

&lt;p&gt;If you find PawHaven interesting, I’d be grateful if you could check it out and give it a &lt;strong&gt;star&lt;/strong&gt; on GitHub. Your support will help this open-source project reach more people and, hopefully, create more possibilities for stray animals in need.  &lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://github.com/aoda-zhang/PawHaven-frontEnd" rel="noopener noreferrer"&gt;Frontend Repo&lt;/a&gt;&lt;br&gt;&lt;br&gt;
🔗 &lt;a href="https://github.com/aoda-zhang/PawHaven-backEnd" rel="noopener noreferrer"&gt;Backend Repo&lt;/a&gt;  &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>opensource</category>
    </item>
    <item>
      <title>AI-Era Philanthropic Practice: From Inspiration to the Exploration of PawHaven</title>
      <dc:creator>Aoda Zhang</dc:creator>
      <pubDate>Tue, 23 Sep 2025 13:18:26 +0000</pubDate>
      <link>https://dev.to/aoda-zhang/ai-era-philanthropic-practice-from-inspiration-to-the-exploration-of-pawhaven-3npg</link>
      <guid>https://dev.to/aoda-zhang/ai-era-philanthropic-practice-from-inspiration-to-the-exploration-of-pawhaven-3npg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;PawHaven is an open-source full-stack project built with React and Node.js. With this project, you can learn how to build an enterprise-grade application from scratch — covering everything from frontend and backend development to CI/CD pipelines and cloud deployment, along with best practices at every stage.&lt;/p&gt;

&lt;p&gt;This article is based on the architecture and practical experience of PawHaven. If you’d like to explore more runnable code examples, feel free to check out the &lt;a href="https://github.com/aoda-zhang/PawHaven-frontEnd" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt; and give it a star ⭐️.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;During the widespread AI revolution, new ideas are easy to come by—but "technology with a heart" that actually works needs something to keep it grounded. For me, that grounding came from real moments with my wife. She’s both a vet and a dedicated animal lover: how focused she is when treating hurt stray cats late at night, how worried she gets when finding foster homes for abandoned puppies, and how helpless she sounds when she says, "One person can’t rescue that many animals"—these small daily moments made me ask: Can AI help spread kindness, so more people can join in rescuing stray animals?  &lt;/p&gt;

&lt;p&gt;As someone who works with technology, I’m used to breaking down problems step by step. The main problems in stray animal rescue are basically two things: "not enough shared information" and "hard to join in": rescuers can’t find animals that need help, people who want to help don’t know where to start, and no one can see how rescue efforts are going. So, the first version of PawHaven started to take shape. It’s not just a "place to write down things," but a tech tool that connects "people who want to help" with "animals that need help"—and it’s my first try at mixing tech work with my wish to do good. Right now, PawHaven is still being tested and improved, but this process has already shown me clear ways AI can help with charity work.  &lt;/p&gt;

&lt;h2&gt;
  
  
  1. From Inspiration to Requirements: AI Turns "Vague Ideas" into "Clear Scenarios"
&lt;/h2&gt;

&lt;p&gt;Inspiration comes from life, but turning the fuzzy thought of "wanting to help stray animals" into real product features means figuring out exactly what users need. At first, I tried getting info from public groups (like pet rescue forums and Xiaohongshu’s stray animal topics), but sorting through all those talks by hand often left me with a mess of repeated or messy info—this is where AI really helped.  &lt;/p&gt;

&lt;p&gt;When I was gathering what the product needed, AI worked like a "fast organizer":  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Finding main problems&lt;/strong&gt;: After I put over 2,000 community talks into AI, it quickly found three big issues: "rescue info is all over the place (stray animals in the same area get reported many times)," "hard to match volunteers (people want to help but can’t find nearby rescue spots)," and "no way to see rescue progress (people donate supplies but don’t know where they go)"—this helped me avoid making features that no one actually needs.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sorting user needs&lt;/strong&gt;: It automatically grouped needs by "what kind of user you are"—people who start rescues (need to report animals and track progress), volunteers (need to sign up and get tasks), and regular users (need to donate or follow updates)—so the product’s features fit each group better.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filling in missing parts&lt;/strong&gt;: For the "adoption process," AI used examples from other charity products to add details like "showing animal health papers publicly" and "checking if adopters are a good fit first"—this stopped me from missing important steps because I didn’t have enough experience.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But I always stuck to one rule: AI gives "ideas to check," not "final answers." For example, AI once suggested adding "AI that automatically tells an animal’s breed," but I thought, "In rescue work, breed doesn’t matter—health does." So I put that feature on hold and first made an "injury note template" (to help non-experts write down an animal’s injuries quickly).  &lt;/p&gt;

&lt;h2&gt;
  
  
  2. Technical Feasibility Verification: AI Helps with "Trying Things Out," Humans Control the "Important Parts"
&lt;/h2&gt;

&lt;p&gt;After figuring out what the product needs, the next step is to check if "the tech can actually be built." Since I’m working on this project alone, I needed to quickly test which features "can be made and are useful" without spending too much time. AI’s job here was to "make it easier to fix mistakes," but the key tech choices were still mine.  &lt;/p&gt;

&lt;p&gt;In practice, I did two things:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;First, build a "simple working version"&lt;/strong&gt;: I used React + Node.js to make an MVP (Minimum Viable Product)—a basic version with three key features: reporting stray animal info (with location tags), a volunteer sign-up page, and updating rescue progress. I didn’t use AI here because basic features have simple, well-known tech solutions, and making choices myself was faster.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Then, use AI to find "ways to make it better"&lt;/strong&gt;: I put the MVP’s steps (like filling out a report form or checking rescue progress) into AI and asked it to act like a "new user." It found two problems: first, choosing a location meant switching to a map app (which was a hassle), and second, rescue status words were too hard to understand (like "pending transfer"). Using this feedback, I made address selection easier (letting people type in addresses or use auto-complete) and changed status words to simpler ones (like "waiting to go to a rescue station").
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, for the key feature of "matching volunteers by location," AI helped me compare two tech options: using a third-party map tool (good because it’s ready to use, bad because it costs money) vs. saving location data locally (good because it’s free, bad because it’s not super accurate). Since the project is "non-commercial and cheap," I chose the free option—prioritizing "rough location matching" and planning to make it more accurate later if I get more resources.  &lt;/p&gt;

&lt;h2&gt;
  
  
  3. UI Design and Experience Optimization: AI Makes "First Drafts," Humans Fix the "Small Things"
&lt;/h2&gt;

&lt;p&gt;A charity product’s design doesn’t need to be "fancy," but it must be "warm and easy to use." For example, photos of stray animals should look real (not over-edited), and buttons should be clear enough for both old and young users. But as a developer without design training, I often struggled with making things look good and easy to use—this is when AI became a "fast draft helper."  &lt;/p&gt;

&lt;p&gt;Here’s how I worked with AI:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Set the style, AI makes drafts&lt;/strong&gt;: First, I told AI I wanted a "warm, simple" look: "A stray animal rescue platform, main colors soft orange + white (no cold colors); rounded buttons to feel friendly." It quickly made 3 homepage layouts (different ways to show info lists and report buttons), so I didn’t have to start drawing from nothing (which saves time).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix small details myself&lt;/strong&gt;: AI’s first draft had a "report button" that was too bright (a loud red, which felt pushy), so I changed it to a softer orange-red. Also, AI put "animal injury info" at the bottom of the page—but since injury info is super important for rescues, I moved it under the animal’s photos to show it first.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use AI to help with different devices&lt;/strong&gt;: For "responsive design" (making it work on phones and computers), AI automatically suggested layout changes for different screen sizes—like hiding side menus on phones (using bottom tabs instead) and adding "rescue count cards" on computers (to show how many animals have been rescued).
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The current design is still a "test version," but compared to designing everything by hand, AI saved me at least 40% of the time—letting me focus on "testing how easy it is to use." For example, when I asked pet lovers to try it, they said that after adding "batch photo upload," we needed a "preview and delete" button. AI didn’t think of this, which made me realize: "How easy a product is to use" finally depends on "real users’ feedback"—AI can only give suggestions.  &lt;/p&gt;

&lt;h2&gt;
  
  
  4. Summary and Future Outlook: AI is a "Tool," Philanthropy is the "Heart"
&lt;/h2&gt;

&lt;p&gt;Looking back at how PawHaven was built, AI has really been a helpful "assistant": it helped me sort needs quickly, made it easier to fix tech mistakes, and made design simpler—so I could turn a personal project into a test version fast. But I know clearly that AI can’t replace the "heart of charity": caring about stray animals, respecting what users need, and knowing which features matter.  &lt;/p&gt;

&lt;p&gt;Right now, there are still many things to test for PawHaven:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Short-term&lt;/strong&gt;: Make "rescue record keeping" better (letting users upload medical reports and supply lists) so everyone can see how things are going.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mid-term&lt;/strong&gt;: Try "AI that helps check injuries at first" (users upload animal photos, and AI says, "Possible injuries—check the legs first") to help non-experts write down key info fast.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-term&lt;/strong&gt;: Make a "volunteer trust system" (matching reliable volunteers based on how often they help and feedback) to fix the problem of "people signing up but not showing up" in charity work.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But no matter how tech changes, one rule will stay the same: PawHaven’s main job is "connecting people"—linking those who want to help with animals that need it. AI just makes this connection faster and warmer.  &lt;/p&gt;

&lt;h2&gt;
  
  
  5. Participation and Support: Inviting "Fellow Helpers" to Improve Together
&lt;/h2&gt;

&lt;p&gt;Right now, PawHaven is still a "test version"—it hasn’t been launched widely, and there are no plans to make money from it. If you, like me, believe "tech can help charity work," you’re welcome to join in these ways:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For developers&lt;/strong&gt;: The project code is open-source &lt;a href="https://github.com/aoda-zhang/PawHaven-frontEnd" rel="noopener noreferrer"&gt;FrontEnd&lt;/a&gt; and &lt;a href="https://github.com/aoda-zhang/PawHaven-backEnd" rel="noopener noreferrer"&gt;BackEnd&lt;/a&gt;. If you have ideas to make the code better, please submit a PR. Developers who know about "location matching" and "multilingual support" are especially welcome.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For volunteers/rescue groups&lt;/strong&gt;: You can test the test version and share feedback on "which features work and which don’t" in real rescue work—like whether you need a "temporary foster info page" or a "rescue supply connection" tool.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For regular users&lt;/strong&gt;: You can star the project to follow updates. When the platform launches later, every time you "report an animal" or "share a rescue update," you’ll be helping stray animals.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Charity has never been "one person’s job," and tech isn’t just "cold code." I hope PawHaven can be a "starting point"—bringing more people to use their skills (tech, time, resources) to do more for stray animals. Even if it just helps one stray cat avoid one night of hunger or one injury, it’s worth it.&lt;/p&gt;

</description>
      <category>fullstack</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
