<?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: Trường Trịnh Đức</title>
    <description>The latest articles on DEV Community by Trường Trịnh Đức (@trng_trnhc_cd88085f).</description>
    <link>https://dev.to/trng_trnhc_cd88085f</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%2F3627850%2F3395b723-7094-462c-ac3f-e92112fcc55f.png</url>
      <title>DEV Community: Trường Trịnh Đức</title>
      <link>https://dev.to/trng_trnhc_cd88085f</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/trng_trnhc_cd88085f"/>
    <language>en</language>
    <item>
      <title>Tích hợp Hot Update vào dự án để tối ưu thời gian cho internal testing</title>
      <dc:creator>Trường Trịnh Đức</dc:creator>
      <pubDate>Fri, 20 Mar 2026 09:01:49 +0000</pubDate>
      <link>https://dev.to/trng_trnhc_cd88085f/tich-hop-hot-update-vao-du-an-de-toi-uu-thoi-gian-cho-internal-testing-iag</link>
      <guid>https://dev.to/trng_trnhc_cd88085f/tich-hop-hot-update-vao-du-an-de-toi-uu-thoi-gian-cho-internal-testing-iag</guid>
      <description>&lt;h2&gt;
  
  
  Giới thiệu
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;const x = "dừng tay, chuyển nhánh, pull code, build app"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Nếu anh em đã từng làm React Native một thời gian, chắc chắn không ít lần nghe câu quen thuộc từ QA: &lt;em&gt;"Em ơi build lại cho chị cái task này với."&lt;/em&gt; Rồi phải &lt;code&gt;x&lt;/code&gt; đợi 20–30 phút, gửi file, rồi lại ngồi chờ phản hồi. Cứ lặp đi lặp lại như vậy mỗi ngày.&lt;/p&gt;

&lt;p&gt;Vấn đề &lt;code&gt;x&lt;/code&gt; có thể giải quyết bằng việc tích hợp CI/CD để tự động hóa quá trình này. Nhưng vẫn không tránh khỏi việc mất thời gian, QA vẫn phải đợi pipeline build xong mới có app để test. Chưa kể có các task cần khách UAT trên TestFlight thì vẫn không tránh khỏi việc dev cần tự mình build lại app để đẩy lên cho khách UAT.  &lt;/p&gt;

&lt;p&gt;Đây không phải vấn đề của một người — đây là vấn đề của cả team. Và nó hoàn toàn có thể giải quyết được.&lt;/p&gt;

&lt;p&gt;Tài liệu này mô tả hệ thống &lt;strong&gt;Dynamic Hot Update Channel&lt;/strong&gt; — một giải pháp OTA (Over-The-Air) update được thiết lập trong project, cho phép dev deploy thay đổi JavaScript trực tiếp lên thiết bị của QA trong vài giây, không cần build lại native app, không cần qua TestFlight, không cần chờ đợi.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Vấn Đề Cần Giải Quyết
&lt;/h2&gt;

&lt;p&gt;Trước khi đi vào giải pháp, hãy nhìn thẳng vào những điểm bất cập hiện tại: &lt;/p&gt;

&lt;h3&gt;
  
  
  🐢 QA Cycle quá chậm
&lt;/h3&gt;

&lt;p&gt;Mỗi lần có thay đổi JavaScript — dù chỉ là sửa một dòng text hay điều chỉnh màu button — team vẫn phải trải qua toàn bộ quy trình: CI/CD build ra file &lt;code&gt;.ipa&lt;/code&gt; hoặc &lt;code&gt;.apk&lt;/code&gt;, đợi 15–20 phút, rồi QA mới có app để test -&amp;gt; Lãng phí thời gian không cần thiết.&lt;/p&gt;

&lt;h3&gt;
  
  
  🐢 🐢 Khách UAT các task riêng lẻ
&lt;/h3&gt;

&lt;p&gt;Flow CI/CD hiện tại chỉ support internal test cho case app IOS. Nên việc khách UAT task thì vẫn cần dev phải tự build app rồi đẩy lên TestFlight để khách có thể test. Case này là gây mất thời gian nhất vì dev phải thực hiện đúng một vòng &lt;code&gt;x&lt;/code&gt; để có app cho khách UAT. &lt;/p&gt;




&lt;h2&gt;
  
  
  2. Giải Pháp: OTA Update + Switch Channel
&lt;/h2&gt;

&lt;h3&gt;
  
  
  OTA Update là gì?
&lt;/h3&gt;

&lt;p&gt;Hãy tưởng tượng một ứng dụng React Native như một &lt;strong&gt;rạp chiếu phim&lt;/strong&gt;. Phần &lt;strong&gt;native&lt;/strong&gt; (iOS/Android shell) chính là toàn bộ cơ sở hạ tầng của rạp — tòa nhà, màn hình, hệ thống âm thanh, ghế ngồi. Phần &lt;strong&gt;JavaScript bundle&lt;/strong&gt; chính là bộ phim đang chiếu - nội dung thực sự mà khán giả đến để xem.&lt;/p&gt;

&lt;p&gt;Khi muốn chiếu phim mới, bạn không cần phá rạp đi xây lại. Bạn chỉ cần &lt;strong&gt;thay cuộn phim&lt;/strong&gt;. Đó chính xác là cách OTA update hoạt động: thay vì build lại toàn bộ app, chúng ta chỉ đẩy một JavaScript bundle mới lên server. App tự kiểm tra, tải về và chạy — người dùng thấy version mới mà không cần làm gì cả.&lt;/p&gt;

&lt;p&gt;Nhưng nếu muốn nâng cấp hệ thống âm thanh Dolby hay lắp thêm máy chiếu 4K — lúc đó mới cần đụng đến phần xây dựng vật lý. Tương tự, khi app cần thêm native module, thay đổi cấu hình iOS/Android, hay cập nhật dependency có native code — bạn vẫn phải build lại native app như bình thường.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Giới hạn quan trọng:&lt;/strong&gt; OTA update chỉ áp dụng được cho thay đổi ở tầng JavaScript và Assets (hình ảnh, font). Nếu thêm native module mới, thay đổi cấu hình iOS/Android, hay cập nhật dependency có native code — vẫn phải build lại native app như bình thường.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Switch Channel là gì?
&lt;/h3&gt;

&lt;p&gt;Thay vì có một staging environment duy nhất cho tất cả, mình sẽ tạo ra các &lt;strong&gt;channel&lt;/strong&gt; riêng biệt — mỗi channel là một "làn đường" độc lập chứa JavaScript bundle của một task cụ thể.&lt;/p&gt;

&lt;p&gt;Ví dụ thực tế:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Channel &lt;code&gt;stg&lt;/code&gt; → chứa code của nhánh &lt;code&gt;develop&lt;/code&gt;- nhánh ổn định nhất và sẽ là nhánh được chọn để build Native App. &lt;/li&gt;
&lt;li&gt;Channel &lt;code&gt;V567APP-199&lt;/code&gt; → chứa code JS của task V567APP-199 rẽ từ &lt;code&gt;develop&lt;/code&gt;, QA muốn test task này thì chỉ cần switch sang "làn đường" - Channel V567APP-199 là có code mới nhất của task để test.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;QA có thể switch qua lại giữa các channel &lt;strong&gt;ngay trên một app đã cài sẵn&lt;/strong&gt;, không cần cài lại, không cần bản build mới. &lt;/p&gt;




&lt;h2&gt;
  
  
  3. Kiến Trúc &amp;amp; Infrastructure
&lt;/h2&gt;

&lt;p&gt;Hệ thống được xây dựng trên &lt;strong&gt;&lt;code&gt;hot-updater&lt;/code&gt;&lt;/strong&gt; với &lt;strong&gt;Cloudflare&lt;/strong&gt; làm backend, được chọn vì chi phí thấp, tốc độ cao phù hợp với mục đích phục vụ cho internal test hiện tại.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────┐
│                    Cloudflare Infrastructure            │
│                                                         │
│  ┌──────────────────┐      ┌──────────────────────────┐ │
│  │  Cloudflare R2   │      │     Cloudflare D1        │ │
│  │  (Object Storage)│      │     (SQLite Database)    │ │
│  │                  │      │                          │ │
│  │  • bundle.js     │      │  channel | version | ... │ │
│  │  • assets/       │      │  stg     | 4.0.0   | ... │ │
│  │  • fonts/        │      │  V567-99 | 4.0.0   | ... │ │
│  └──────────────────┘      └──────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
         ▲                              ▲
         │ deploy                       │ query metadata
         │                              │
┌────────┴──────────┐        ┌──────────┴──────────────┐
│   Developer CLI   │        │    React Native App     │
│  yarn hot:task:   │        │  (on QA's device)       │
│  ios V567APP-199  │        │  → checks channel,      │
└───────────────────┘        │  → downloads bundle     │
                             │  → reloads JS engine    │
                             └─────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Các thành phần chính:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Thành phần&lt;/th&gt;
&lt;th&gt;Vai trò&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cloudflare R2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lưu trữ JavaScript bundle và static assets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cloudflare D1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Database theo dõi metadata: channel nào đang chạy bundle nào, version nào&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;hot-updater&lt;/code&gt; CLI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tool deploy bundle từ máy developer lên Cloudflare&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;DevChannelSwitcher&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;UI trong app để QA switch channel&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Các channel được định nghĩa:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;stg&lt;/code&gt; — Channel mặc định, luôn phản ánh code của nhánh staging chính (&lt;code&gt;develop&lt;/code&gt;). Logic hiện tại khi build native app với config staging sẽ auto set default channel là &lt;code&gt;stg&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[Task-Name]&lt;/code&gt; — Ephemeral channel (tồn tại tạm thời) cho từng Jira ticket, ví dụ &lt;code&gt;V567APP-199&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Tích hợp
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A. Infrastructure Configuration
&lt;/h3&gt;

&lt;p&gt;Việc set-up, cấu hình anh em có thể check trực tiếp trên docs của Hot Updater nhé 🫡: &lt;a href="https://hot-updater.dev/docs/get-started/introduction" rel="noopener noreferrer"&gt;https://hot-updater.dev/docs/get-started/introduction&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  B. Deployment Scripts
&lt;/h3&gt;

&lt;p&gt;Các script được định nghĩa trong &lt;code&gt;package.json&lt;/code&gt;, sử dụng biến môi trường từ &lt;code&gt;.env.stg&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="nl"&gt;"hot:console"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="s2"&gt;"npx hot-updater console"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"hot:stg:ios"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="s2"&gt;"ENVFILE=.env.stg npx hot-updater deploy -p ios -t &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;4.0.0&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --channel stg"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"hot:stg:android"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="s2"&gt;"ENVFILE=.env.stg npx hot-updater deploy -p android -t &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;4.0.0&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --channel stg"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"hot:stg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="s2"&gt;"yarn hot:stg:ios &amp;amp;&amp;amp; yarn hot:stg:android"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"hot:task:ios"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="s2"&gt;"ENVFILE=.env.stg npx hot-updater deploy -p ios -t &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;4.0.0&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --channel"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"hot:task:android"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="s2"&gt;"ENVFILE=.env.stg npx hot-updater deploy -p android -t &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;4.0.0&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --channel"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mổ xẻ từng phần của một lệnh deploy điển hình:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ENVFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.env.stg  npx hot-updater deploy  &lt;span class="nt"&gt;-p&lt;/span&gt; ios  &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"4.0.0"&lt;/span&gt;  &lt;span class="nt"&gt;--channel&lt;/span&gt; stg
│                 │                        │        │              │
│                 │                        │        │              └─ Tên channel sẽ nhận bundle này
│                 │                        │        └─ Version targeting: chỉ apply cho app 4.0.0
│                 │                        └─ Platform: ios hoặc android
│                 └─ Lệnh deploy của hot-updater CLI
└─ Inject .env.stg làm môi trường build &lt;span class="o"&gt;(&lt;/span&gt;API keys, app config, v.v.&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tại sao cần &lt;code&gt;ENVFILE=.env.stg&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Khi JavaScript bundle được tạo ra, nó sẽ bake-in các giá trị từ file &lt;code&gt;.env&lt;/code&gt; vào trong bundle — bao gồm API endpoint, feature flags, v.v. Chỉ định &lt;code&gt;.env.stg&lt;/code&gt; đảm bảo bundle được build với đúng config của môi trường staging, không phải production. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;DEVELOPMENT_MODE=true&lt;/code&gt; để check điều kiện chỉ implement với các bản build app ở staging không gây rủi do đến các bản build của production.&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;isDevelopmentMode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Boolean&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;span class="nx"&gt;DEVELOPMENT_MODE&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&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="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;isDevelopmentMode&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;HotUpdater&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[https://hot-update.staging.workers.dev/api/check-update](https://hot-update.staging.workers.dev/api/check-update)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;updateStrategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;appVersion&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;updateMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&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;App&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tại sao cần &lt;code&gt;-t "4.0.0"&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hot-updater.config.ts&lt;/code&gt; đang dùng &lt;code&gt;updateStrategy: "appVersion"&lt;/code&gt; — nghĩa là Cloudflare D1 sẽ lưu thêm thông tin version targeting cho mỗi bundle. Khi app khởi động và check update, nó gửi kèm app version hiện tại. Cloudflare D1 chỉ trả về bundle nếu version của thiết bị thỏa điều kiện &lt;code&gt;4.0.0&lt;/code&gt; ( Version chung của các bản build staging ) &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sự khác biệt giữa &lt;code&gt;hot:stg&lt;/code&gt; và &lt;code&gt;hot:task&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hot:stg&lt;/code&gt; deploy lên channel cố định &lt;code&gt;stg&lt;/code&gt; — channel này luôn tồn tại và là nơi toàn bộ team nhận update sau mỗi lần merge.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hot:task:ios&lt;/code&gt; và &lt;code&gt;hot:task:android&lt;/code&gt; không có &lt;code&gt;--channel&lt;/code&gt; cố định — dev phải tự append tên channel khi gọi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn hot:task:ios V567APP-199
&lt;span class="c"&gt;# tương đương với:&lt;/span&gt;
&lt;span class="c"&gt;# ENVFILE=.env.stg npx hot-updater deploy -p ios -t "4.0.0" --channel V567APP-199&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  C. In-App Dev Channel Switcher
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;DevChannelSwitcher.tsx&lt;/code&gt;&lt;/strong&gt; — Một FAB (Floating Action Button) được inject vào root của app, &lt;strong&gt;chỉ hiển thị khi &lt;code&gt;DEVELOPMENT_MODE=true&lt;/code&gt;&lt;/strong&gt; (tức là chỉ có trên bản staging, không bao giờ xuất hiện trên production).&lt;/p&gt;

&lt;p&gt;UI này cung cấp một modal với 3 chức năng:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hiển thị channel đang active hiện tại&lt;/li&gt;
&lt;li&gt;Input để nhập tên channel mới và switch sang bundle tương ứng&lt;/li&gt;
&lt;li&gt;Nút Reset để quay về channel &lt;code&gt;stg&lt;/code&gt; mặc định&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Do logic của hot update, bắt buộc cần phải chuyển về default channel rồi sau đó mới có thể chuyển đến các Ephemeral channel khác. Ví dụ từ default channel &lt;code&gt;stg&lt;/code&gt; chuyển sang channel &lt;code&gt;V567APP-199&lt;/code&gt; để test. Test xong, muốn sang task &lt;code&gt;V567APP-200&lt;/code&gt; thì cần Reset về &lt;code&gt;stg&lt;/code&gt; sau đó chuyển sang task &lt;code&gt;V567APP-200&lt;/code&gt; không được chuyển trực tiếp từ &lt;code&gt;V567APP-199&lt;/code&gt; qua &lt;code&gt;V567APP-200&lt;/code&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&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%2Fdvgxgz7yiohpaet4yk02.png" alt="App ở default channel" width="800" height="1733"&gt;&lt;/th&gt;
&lt;th&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%2Fb20nps7ckhkguri9pq67.png" alt="App ở Ephemeral channel" width="800" height="1733"&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;1. App ở default channel&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;2. App ở Ephemeral channel&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  5. Quy Trình Làm Việc Hàng Ngày
&lt;/h2&gt;

&lt;h3&gt;
  
  
  👨‍💻 Dev— Deploy một task lên channel riêng
&lt;/h3&gt;

&lt;p&gt;Khi hoàn thành task &lt;code&gt;V567APP-199&lt;/code&gt; và muốn QA bắt đầu test:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bước 1:&lt;/strong&gt; Đảm bảo code đã sẵn sàng trên branch local.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bước 2:&lt;/strong&gt; Chạy lệnh deploy lên channel riêng của task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn hot:task:ios V567APP-199
yarn hot:task:android V567APP-199
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lệnh này sẽ bundle JavaScript hiện tại ở local và push lên Cloudflare dưới channel &lt;code&gt;V567APP-199&lt;/code&gt;. Toàn bộ quá trình mất khoảng &lt;strong&gt;1–2 phút&lt;/strong&gt;, thay vì 20 phút build native.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bước 3:&lt;/strong&gt; Ping QA trên Jira/Mattermost: &lt;em&gt;"Task V567APP-199 Không có phần update native code và đã được deploy lên rồi"&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lưu ý:&lt;/strong&gt; Mỗi lần chạy lại lệnh deploy cho cùng một channel, bundle cũ sẽ bị ghi đè. QA nếu đang ở channel đó rồi thì chỉ cần đợi app tải về bundle vừa được deploy lên là có code mới nhất vừa được dev update để test, không cần làm gì thêm. 😳&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🕵️‍♂️ QA — Switch sang channel của task cần test
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bước 1:&lt;/strong&gt; Mở Staging App trên thiết bị.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bước 2:&lt;/strong&gt; Tap vào icon &lt;strong&gt;🔥&lt;/strong&gt; ở góc dưới bên phải màn hình để mở &lt;strong&gt;Task Switcher&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bước 3:&lt;/strong&gt; Nhập tên task vào ô input: &lt;code&gt;V567APP-199&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bước 4:&lt;/strong&gt; Tap &lt;strong&gt;"🚀 Switch task"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;App sẽ tự động tải JavaScript bundle của task đó từ Cloudflare và reload. Sau khi reload xong, app sẽ chạy đúng code của &lt;code&gt;V567APP-199&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bước 5 (khi test xong):&lt;/strong&gt; Mở lại Task Switcher → tap &lt;strong&gt;"🔄 Reset"&lt;/strong&gt; để quay về channel &lt;code&gt;stg&lt;/code&gt; mặc định.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Có thể switch qua lại giữa nhiều task trong cùng một buổi test mà không cần cài lại app hay làm gì phức tạp. Chỉ cần nhập đúng tên channel và tap Switch. &lt;/p&gt;
&lt;/blockquote&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%2Fxeu330qbcni1dymo2ayf.gif" 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%2Fxeu330qbcni1dymo2ayf.gif" alt="GIF mô tả" width="400" height="867"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  🚀 Release Management — Merge vào Staging chính
&lt;/h3&gt;

&lt;p&gt;Khi PR của &lt;code&gt;V567APP-199&lt;/code&gt; đã được approve và merge vào nhánh staging chính, cần cập nhật channel &lt;code&gt;stg&lt;/code&gt; để toàn bộ team nhận được code mới:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bước 1:&lt;/strong&gt; Pull code staging mới nhất về máy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bước 2:&lt;/strong&gt; Deploy lên channel &lt;code&gt;stg&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn hot:stg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Script này sẽ deploy song song cho cả iOS và Android. Từ lần mở app tiếp theo, mọi thiết bị đang dùng channel &lt;code&gt;stg&lt;/code&gt; sẽ tự động fetch và apply bundle mới.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Quan trọng:&lt;/strong&gt; Chỉ chạy &lt;code&gt;yarn hot:stg&lt;/code&gt; sau khi PR đã được merge và code trên nhánh staging đã ổn định. Tránh deploy code đang dở dang lên channel &lt;code&gt;stg&lt;/code&gt; vì nó sẽ ảnh hưởng đến toàn bộ team.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🎛️ Dashboard — Quản lý và Rollback
&lt;/h3&gt;

&lt;p&gt;Để xem toàn bộ lịch sử deployment, kiểm tra bundle nào đang active trên channel nào, hoặc thực hiện rollback khi cần:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn hot:console
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lệnh này mở một local web interface kết nối trực tiếp với Cloudflare D1 và R2, cho phé:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Xem danh sách tất cả channels và bundle đang active&lt;/li&gt;
&lt;li&gt;Force rollback về một bundle cũ hơn chỉ bằng vài click&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. Tóm Tắt So Sánh
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Quy trình cũ&lt;/th&gt;
&lt;th&gt;Với Hot Update Channel&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Thời gian deploy cho QA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10–15 phút đợi tool (build native), 20-30 phút cho đẩy TestFlight UAT&lt;/td&gt;
&lt;td&gt;1–2 phút&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Khi update code ở task&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cần build lại app&lt;/td&gt;
&lt;td&gt;Không cần làm gì, app tự tải code mới nhất về&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Test song song nhiều task&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cần nhiều bản build riêng&lt;/td&gt;
&lt;td&gt;Switch channel ngay trên một app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rollback khi có bug&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Build lại từ commit cũ&lt;/td&gt;
&lt;td&gt;Rollback qua dashboard, tức thì&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Áp dụng cho&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mọi thay đổi&lt;/td&gt;
&lt;td&gt;Chỉ JS/Assets, không áp dụng cho native changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  7. Những Điều Cần Lưu Ý
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Không dùng cho native changes:&lt;/strong&gt; Nếu task của dev có thay đổi native code (thêm pod, sửa &lt;code&gt;AndroidManifest&lt;/code&gt;, cập nhật native module), dev vẫn phải build native app bình thường. Hot update chỉ dành cho JS/Assets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version targeting:&lt;/strong&gt; Bundle chỉ apply cho app &lt;code&gt;4.0.0&lt;/code&gt; (default version của staging). Thiết bị đang chạy app version khác sẽ không nhận update.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.env.hotupdater&lt;/code&gt; là file nhạy cảm:&lt;/strong&gt; Không commit file này lên git. Nếu cần onboard thành viên mới, chia sẻ qua kênh bảo mật riêng.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mobile</category>
      <category>productivity</category>
      <category>reactnative</category>
      <category>testing</category>
    </item>
    <item>
      <title>Deep Dive React Native #4: The Architect Inside — Understanding Reconciliation</title>
      <dc:creator>Trường Trịnh Đức</dc:creator>
      <pubDate>Wed, 03 Dec 2025 15:43:33 +0000</pubDate>
      <link>https://dev.to/trng_trnhc_cd88085f/deep-dive-react-native-4-the-architect-inside-understanding-reconciliation-52m7</link>
      <guid>https://dev.to/trng_trnhc_cd88085f/deep-dive-react-native-4-the-architect-inside-understanding-reconciliation-52m7</guid>
      <description>&lt;p&gt;If you ask a React Native developer: &lt;em&gt;"Does React Native use a Virtual DOM?"&lt;/em&gt;, the answer is usually a hesitant &lt;em&gt;"Yes... sort of."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Technically, the "DOM" (Document Object Model) is a Web concept. There are no &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; tags in your phone. However, the &lt;strong&gt;mechanism&lt;/strong&gt;—the core brain that makes React Native performant—is exactly the same. We call it the &lt;strong&gt;Virtual Tree&lt;/strong&gt; (or React Element Tree).&lt;/p&gt;

&lt;p&gt;In this article, we will explore &lt;strong&gt;Reconciliation&lt;/strong&gt;: The algorithm that decides when to repaint a wall and when to tear down the entire house.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Expensive Truth: Native Views
&lt;/h2&gt;

&lt;p&gt;Why do we need a "Virtual" tree in the first place?&lt;/p&gt;

&lt;p&gt;Imagine you are building a house (User Interface):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Virtual Tree:&lt;/strong&gt; This is a blueprint on paper. You can erase a wall, redraw a window, or crumble the paper up in milliseconds. It is cheap.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Native Views (&lt;code&gt;UIView&lt;/code&gt; on iOS, &lt;code&gt;android.view.View&lt;/code&gt; on Android):&lt;/strong&gt; These are the actual bricks and mortar. Creating a new native view requires memory allocation, GPU rendering, and layout calculation. It is &lt;strong&gt;expensive&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If React Native destroyed and recreated the actual Native Views every time your state changed, your app would run at 1 FPS and burn the user's battery instantly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Reconciliation&lt;/strong&gt; is the strategic process of comparing the new Blueprint with the old Blueprint to minimize the work required by the Builders (Native UI Thread).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  2. The Diffing Algorithm: O(n) Heuristics
&lt;/h2&gt;

&lt;p&gt;In computer science, comparing two trees to find the minimum number of operations is typically an &lt;strong&gt;O(n³)&lt;/strong&gt; complexity problem. For an app with 1,000 elements, that would mean one billion calculations. Too slow.&lt;/p&gt;

&lt;p&gt;React uses a "heuristic" (smart guessing) algorithm to bring this down to &lt;strong&gt;O(n)&lt;/strong&gt;. To achieve this, it relies on two major assumptions that every Good Developer must know:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule #1: Different Types = Total Destruction&lt;/strong&gt;&lt;br&gt;
If the root element type changes, React tears down the old tree and builds a new one from scratch.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Old:&lt;/strong&gt; &lt;code&gt;&amp;lt;View&amp;gt;&amp;lt;Text&amp;gt;Hi&amp;lt;/Text&amp;gt;&amp;lt;/View&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;New:&lt;/strong&gt; &lt;code&gt;&amp;lt;ScrollView&amp;gt;&amp;lt;Text&amp;gt;Hi&amp;lt;/Text&amp;gt;&amp;lt;/ScrollView&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even though the inside is the same, React sees that &lt;code&gt;View&lt;/code&gt; changed to &lt;code&gt;ScrollView&lt;/code&gt;. &lt;strong&gt;Action:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unmount&lt;/strong&gt; the old View (Native View destroyed, state lost).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mount&lt;/strong&gt; the new ScrollView (New Native View created).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pitfall:&lt;/strong&gt; Never define a component &lt;em&gt;inside&lt;/em&gt; another component.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Parent = () =&amp;gt; {
  // ❌ BAD: This creates a new 'Child' type on every render
  const Child = () =&amp;gt; &amp;lt;View /&amp;gt;;
  return &amp;lt;Child /&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This forces React to hit &lt;strong&gt;Rule #1&lt;/strong&gt; continuously, causing flickering and focus loss.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rule #2: Same Type = Update Props Only
&lt;/h3&gt;

&lt;p&gt;If the element type remains the same, React keeps the underlying Native View instance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Old:&lt;/strong&gt; &lt;code&gt;&amp;lt;View style={{ width: 100 }} /&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;New:&lt;/strong&gt; &lt;code&gt;&amp;lt;View style={{ width: 200 }} /&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Action:&lt;/strong&gt; React sees the type is still View. It calculates the difference (diff) in props and sends a specific command to the Native side: &lt;em&gt;"Update property 'width' to 200"&lt;/em&gt;. The heavy Native View is preserved.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The List Problem: Why key is Critical
&lt;/h2&gt;

&lt;p&gt;This is the most common performance bottleneck in React Native lists (FlatList, ScrollView).&lt;/p&gt;

&lt;p&gt;Imagine a list: A -&amp;gt; B. You want to insert C at the top: C -&amp;gt; A -&amp;gt; B.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without Keys:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;React compares index 0: A vs C. Different? Mutate A to C.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;React compares index 1: B vs A. Different? Mutate B to A.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;React appends B at the end. &lt;strong&gt;Result:&lt;/strong&gt; Every single row is re-rendered.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;**With Keys (&lt;code&gt;key="a"&lt;/code&gt;, &lt;code&gt;key="b"&lt;/code&gt;): React looks at the unique ID, not the index.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;It sees A and B still exist.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It realizes C is the only new item. &lt;strong&gt;Result:&lt;/strong&gt; It re-uses the native views for A and B, and only creates C.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  4. The Native Twist: The Shadow Tree
&lt;/h2&gt;

&lt;p&gt;This is where React Native differs from the Web.&lt;/p&gt;

&lt;p&gt;On the Web, React talks to the Browser DOM. In React Native, after Reconciliation is done in the JavaScript Thread, the result is not HTML. It is a set of serialized commands (UIManager operations).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;JS Thread:&lt;/strong&gt; Calculates the diff (Reconciliation).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Bridge (Old Arch) / JSI (New Arch):&lt;/strong&gt; Transports these changes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Shadow Tree (C++):&lt;/strong&gt; A dedicated tree structure used to calculate Layout (Yoga Engine).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI Thread:&lt;/strong&gt; The actual pixels are drawn on the screen.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the &lt;strong&gt;New Architecture (Fabric)&lt;/strong&gt;, the Reconciliation step can create the Shadow Tree directly in C++, reducing the overhead significantly and allowing for synchronous updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Performance Takeaways
&lt;/h2&gt;

&lt;p&gt;Understanding Reconciliation changes how you write code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stability is King:&lt;/strong&gt; Keep your component structure stable. Avoid dynamic component types.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;React.memo&lt;/code&gt; is your shield:&lt;/strong&gt; If a parent re-renders, the child re-renders by default (even if props didn't change). Use &lt;code&gt;memo&lt;/code&gt; to tell Reconciliation: &lt;em&gt;"Stop here, nothing changed."&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keys Matter:&lt;/strong&gt; Use unique IDs (from your database) for list keys. Never use the array index &lt;code&gt;(key={index})&lt;/code&gt; if the list can be reordered or filtered.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Reconciliation&lt;/strong&gt; is the "Architect" that bridges the gap between the flexibility of JavaScript and the performance of Native UI. It allows us to write declarative code ("I want a blue button") without worrying about the imperative manual labor ("Create view, set background, set frame...").&lt;/p&gt;

&lt;p&gt;Mastering this concept is the first step in moving from "My app works" to "My app flies."&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>performance</category>
      <category>reactnative</category>
    </item>
    <item>
      <title>𝐃𝐞𝐞𝐩 𝐃𝐢𝐯𝐞 𝐑𝐞𝐚𝐜𝐭 𝐍𝐚𝐭𝐢𝐯𝐞 #3: 𝐁𝐚𝐛𝐞𝐥 #2— The Real-World Battlefield</title>
      <dc:creator>Trường Trịnh Đức</dc:creator>
      <pubDate>Sun, 30 Nov 2025 10:04:56 +0000</pubDate>
      <link>https://dev.to/trng_trnhc_cd88085f/-2-the-real-world-battlefield-1g0j</link>
      <guid>https://dev.to/trng_trnhc_cd88085f/-2-the-real-world-battlefield-1g0j</guid>
      <description>&lt;p&gt;In the previous part, we established that Babel is the &lt;strong&gt;"Translator."&lt;/strong&gt; Now, let's see how this translator handles difficult situations in a real project.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Absolute Path &amp;amp; Alias — "The Triangle of Synchronization"
&lt;/h2&gt;

&lt;p&gt;Many developers configure Aliases, run the app, and it works perfectly, but VS Code is covered in red errors. Or vice versa: VS Code is happy, but the app crashes. Why?&lt;/p&gt;

&lt;p&gt;Because this is the &lt;strong&gt;"Triangle of Synchronization."&lt;/strong&gt; For an Alias (e.g., &lt;code&gt;@components/Button&lt;/code&gt;) to work smoothly, three giant systems must shake hands:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Babel:&lt;/strong&gt; So the code runs (Runtime).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript (&lt;code&gt;tsconfig.json&lt;/code&gt;):&lt;/strong&gt; So VS Code understands and provides autocomplete (Dev Experience).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metro:&lt;/strong&gt; (Sometimes) To resolve overlapping imports.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Standard Configuration&lt;/strong&gt;&lt;br&gt;
First, install the plugin: &lt;code&gt;yarn add -D babel-plugin-module-resolver&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;babel.config.js&lt;/code&gt;:&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;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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;presets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module:@react-native/babel-preset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;plugins&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module-resolver&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// The root directory to start searching&lt;/span&gt;
        &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.ios.js&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;.android.js&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;.js&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;.ts&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;.tsx&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;.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@components&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;./src/components&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/utils&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;@assets&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;./src/assets&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Trap&lt;/strong&gt; &lt;br&gt;
Many people forget the &lt;strong&gt;&lt;code&gt;extensions&lt;/code&gt; array&lt;/strong&gt;. If you don't declare &lt;code&gt;.ts&lt;/code&gt; or &lt;code&gt;.tsx&lt;/code&gt; here, Babel sometimes won't know to apply the Alias to your TypeScript files, leading to random &lt;em&gt;"Module not found"&lt;/em&gt; errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sync with &lt;code&gt;tsconfig.json&lt;/code&gt; (Mandatory)&lt;/strong&gt;&lt;br&gt;
Babel modifies the code underneath, but VS Code doesn't know that. You must "declare" it again to TypeScript:&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;"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;"baseUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"paths"&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;"@components/*"&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;"components/*"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@utils/*"&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;"utils/*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Cleaning Up for Production (Removing Console Logs)
&lt;/h2&gt;

&lt;p&gt;A good developer is always obsessed with performance. &lt;code&gt;console.log&lt;/code&gt; is incredibly useful during development, but it is &lt;strong&gt;poison&lt;/strong&gt; in a release build.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It slows down the &lt;strong&gt;JS Bridge&lt;/strong&gt; (because strings must be serialized and sent between JS and Native). &lt;/li&gt;
&lt;li&gt;It exposes sensitive information if a user connects the device to a computer to view logs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We don't delete logs manually line by line. &lt;strong&gt;We tell Babel to do it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Smart Configuration (Dynamic Configuration)&lt;/strong&gt;&lt;br&gt;
The Babel config is essentially a JS function, so we can write &lt;code&gt;if/else&lt;/code&gt; logic inside it.&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;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="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// api.cache(true) helps Babel cache this config forever until restart&lt;/span&gt;
  &lt;span class="c1"&gt;// Significantly speeds up build time&lt;/span&gt;
  &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;presets&lt;/span&gt; &lt;span class="o"&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;module:@react-native/babel-preset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="c1"&gt;// Logic: Only remove logs when building for Production&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;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&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;plugins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;transform-remove-console&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;presets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;plugins&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Developer Insight:&lt;/strong&gt; Using &lt;code&gt;api.cache(true)&lt;/code&gt; or &lt;code&gt;api.cache.using(() =&amp;gt; process.env.NODE_ENV)&lt;/code&gt; is a build time optimization technique that few notice. Without it, Babel might recalculate the config for every single file, slowing down your build process.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. Environment Variables &amp;amp; Caching Hell
&lt;/h2&gt;

&lt;p&gt;When using &lt;code&gt;react-native-dotenv&lt;/code&gt; to manage API Keys, Babel directly replaces those variables into your code as strings.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Example Code:&lt;/strong&gt; &lt;code&gt;console.log(process.env.API_URL)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Babel Translates to:&lt;/strong&gt; &lt;code&gt;console.log("https://api.example.com")&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Deadly Trap:&lt;/strong&gt;&lt;br&gt;
You change &lt;code&gt;API_URL&lt;/code&gt; in your &lt;code&gt;.env&lt;/code&gt; file from "dev" to "prod". You reload the app. &lt;strong&gt;The app still points to "dev".&lt;/strong&gt; You pull your hair out?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Reason:&lt;/strong&gt;&lt;br&gt;
Babel (via Metro) has &lt;strong&gt;CACHED&lt;/strong&gt; the old translation. It sees your JS code hasn't changed (it's still the same &lt;code&gt;process.env...&lt;/code&gt; line), so it doesn't re-translate it. It doesn't know that the &lt;code&gt;.env&lt;/code&gt; file outside has changed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Expert Solution&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Always run &lt;code&gt;npx react-native start --reset-cache&lt;/code&gt; when changing environment variables.&lt;/li&gt;
&lt;li&gt;Or configure Babel to cache based on the &lt;code&gt;.env&lt;/code&gt; file (more complex, but automated).&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;When working with &lt;code&gt;babel.config.js&lt;/code&gt;, remember these &lt;strong&gt;3 Golden Rules&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Triangle:&lt;/strong&gt; If Babel changes a path, TSConfig must know about it. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Environment:&lt;/strong&gt; Configs for Dev and Prod can be different (clean up logs in Prod). &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reset Cache:&lt;/strong&gt; The magic spell that fixes almost everything when editing Babel configs.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Thanks for reading!&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;



</description>
      <category>reactnative</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Deep Dive React Native #2: Babel — The Versatile "Translator"</title>
      <dc:creator>Trường Trịnh Đức</dc:creator>
      <pubDate>Thu, 27 Nov 2025 17:11:31 +0000</pubDate>
      <link>https://dev.to/trng_trnhc_cd88085f/deep-dive-react-native-2-babel-the-versatile-translator-agl</link>
      <guid>https://dev.to/trng_trnhc_cd88085f/deep-dive-react-native-2-babel-the-versatile-translator-agl</guid>
      <description>&lt;p&gt;In our previous post, we explored &lt;strong&gt;Metro&lt;/strong&gt;, the "Logistics Hub" that gathers and packages your code. But before Metro packs the boxes, something crucial must happen to the product inside.&lt;/p&gt;

&lt;p&gt;In your project file, &lt;code&gt;babel.config.js&lt;/code&gt; often looks very brief, even sketchy. But don't let that appearance fool you. Without it, your application would crash on the very first line of code.&lt;/p&gt;

&lt;p&gt;Why? Because you are writing code in a language your phone does not understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Mental Model: The "Diplomat"
&lt;/h2&gt;

&lt;p&gt;Imagine you (&lt;strong&gt;The Developer&lt;/strong&gt;) are a diplomat speaking &lt;em&gt;French&lt;/em&gt; (Modern JS, JSX, TypeScript). However, the listener (&lt;strong&gt;The JavaScript Engine&lt;/strong&gt; on the phone — Hermes or JSC) only understands &lt;em&gt;broken English&lt;/em&gt; (old Standard JavaScript).&lt;/p&gt;

&lt;p&gt;If you say: &lt;em&gt;"Je t'aime"&lt;/em&gt; (Fancy Code: &lt;code&gt;const App = () =&amp;gt; &amp;lt;View /&amp;gt;&lt;/code&gt;), the listener will be confused.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Babel is the interpreter standing in between.&lt;/strong&gt; He takes your French text, translates it into broken English: &lt;em&gt;"I love you"&lt;/em&gt; (Old Code: &lt;code&gt;var App = function() { return React.createElement(View)... }&lt;/code&gt;), and then hands it to the phone to execute.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In short: &lt;strong&gt;Babel turns your "modern, beautiful" code into "ugly, basic" code that any machine can run.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. Under the Hood: How Babel Works
&lt;/h2&gt;

&lt;p&gt;To become a Good Developer, you need to know Babel isn't just simple "find &amp;amp; replace" text. It operates through 3 sophisticated surgical steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Parsing (Reading)&lt;/strong&gt;: Babel reads your code and draws a tree diagram called the &lt;strong&gt;AST (Abstract Syntax Tree)&lt;/strong&gt;. It understands the logical structure of the code, not just the characters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Transformation (Editing)&lt;/strong&gt;: This is where the &lt;strong&gt;Plugins&lt;/strong&gt; jump in. They climb that AST tree, cut this branch, and graft that branch.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It sees a "JSX" branch → cuts it → replaces it with a &lt;code&gt;React.createElement&lt;/code&gt; branch.&lt;/li&gt;
&lt;li&gt;It sees a "TypeScript Type" branch → chops it off completely (since JS doesn't need types).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Code Generation (Writing)&lt;/strong&gt;: From the modified tree, Babel writes out the new code file (output) that Metro will eventually bundle.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Dissecting &lt;code&gt;babel.config.js&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This configuration file is essentially where you command the "Translator" on &lt;em&gt;what&lt;/em&gt; to translate. It has two core components: &lt;strong&gt;Presets&lt;/strong&gt; and &lt;strong&gt;Plugins&lt;/strong&gt;.&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;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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;presets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module:@react-native/babel-preset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native-reanimated/plugin&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A. Presets (The Combo Menu)&lt;/strong&gt;&lt;br&gt;
Instead of telling Babel to translate bit by bit (translate arrows &lt;code&gt;=&amp;gt;&lt;/code&gt;, translate &lt;code&gt;class&lt;/code&gt;, translate &lt;code&gt;const&lt;/code&gt;...), we use &lt;strong&gt;Presets&lt;/strong&gt;. &lt;code&gt;module:@react-native/babel-preset&lt;/code&gt; is a giant combo package containing hundreds of translation rules (plugins) essential for React Native. It tells Babel: &lt;em&gt;"Translate everything that React Native folks usually use (Flow, TS, JSX) into standard JS for me."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;B. Plugins (Side Dishes / Special Agents)&lt;/strong&gt;&lt;br&gt;
Sometimes the Combo menu isn't enough. You need special tools for specific tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Classic Example 1: Alias (The Shortcut)&lt;/strong&gt;&lt;br&gt;
For Babel to actually understand and change the code paths, we need a plugin. When Babel sees &lt;code&gt;@components&lt;/code&gt;, it silently fixes it to &lt;code&gt;./src/components&lt;/code&gt; in the final code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Classic Example 2: Reanimated (The Magic)&lt;/strong&gt;&lt;br&gt;
Why does &lt;code&gt;react-native-reanimated&lt;/code&gt; require a mandatory plugin?&lt;/p&gt;

&lt;p&gt;Because this library runs code on a separate thread (UI Thread). Its Babel plugin secretly turns your function into a &lt;strong&gt;"worklet"&lt;/strong&gt; — a small piece of code that can be detached from the main JS thread and sent to the UI thread. Without this plugin, the App crashes immediately because the UI thread receives no code.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Why You - A React Native Developer Need to Care?
&lt;/h2&gt;

&lt;p&gt;You might ask: &lt;em&gt;"It works out of the box, why touch it?"&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Order Matters (Life or Death)&lt;/strong&gt; 💀: This is a basic but headache-inducing error. Babel runs Plugins from &lt;strong&gt;top to bottom&lt;/strong&gt;. Some libraries (like &lt;code&gt;react-native-reanimated&lt;/code&gt;) require their plugin to be &lt;strong&gt;last&lt;/strong&gt; in the list. Wrong placement → Crash with unknown cause.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cache War&lt;/strong&gt; 💾: Metro caches Babel's results very tightly. When you edit &lt;code&gt;babel.config.js&lt;/code&gt; (e.g., adding a new alias), but the App doesn't update? That's because Metro says: &lt;em&gt;"I already translated this file, I'll grab the old version."&lt;/em&gt; Whenever you touch config, always run: &lt;code&gt;npx react-native start --reset-cache&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debugging and Source Maps&lt;/strong&gt; 🐞: When the App errors, the log points to &lt;code&gt;index.bundle&lt;/code&gt; (the file Babel has mashed up). How do you know where the error is in the original file? That’s thanks to &lt;strong&gt;Source Maps&lt;/strong&gt; — a map linking "ugly" code back to "beautiful" code. A wrong Babel config can break this map.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;&lt;code&gt;babel.config.js&lt;/code&gt; is not just a soulless configuration file. It is the blueprint for your code's "transformation" process.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Metro&lt;/strong&gt;: The Transporter (Logistics).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Babel&lt;/strong&gt;: The Processor (Translator) working for Metro.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding Babel helps you confidently integrate the "pickiest" libraries (like Reanimated, Skia) and structure projects professionally.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;console.log("Thanks for reading!")&lt;/code&gt;&lt;/p&gt;




</description>
      <category>reactnative</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Deep Dive React Native #1: The Most Overlooked File in Your Project: Do You Truly Understand metro.config.js</title>
      <dc:creator>Trường Trịnh Đức</dc:creator>
      <pubDate>Tue, 25 Nov 2025 16:58:09 +0000</pubDate>
      <link>https://dev.to/trng_trnhc_cd88085f/deep-dive-react-native-1-the-most-overlooked-file-in-your-project-do-you-truly-understand-3hgl</link>
      <guid>https://dev.to/trng_trnhc_cd88085f/deep-dive-react-native-1-the-most-overlooked-file-in-your-project-do-you-truly-understand-3hgl</guid>
      <description>&lt;p&gt;When you initialize a new React Native project, you will find a file quietly sitting in your root directory: &lt;code&gt;metro.config.js&lt;/code&gt; Most developers tend to ignore it until something breaks, or they need to install a specific library like &lt;code&gt;react-native-svg&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;But have you ever asked yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Why doesn't React Native use Webpack or Vite?&lt;/li&gt;
&lt;li&gt; How does the app update so instantly when I change a single line of code (Fast Refresh)?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The answer lies in &lt;strong&gt;Metro.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this article, we won't just learn how to &lt;em&gt;"configure"&lt;/em&gt; it. We are going to tear it open to understand what it actually does. Understanding Metro is the difference between a &lt;em&gt;coder&lt;/em&gt; who just copies solutions and a &lt;em&gt;developer&lt;/em&gt; who understands the first principles of their tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Mental Model: The "Logistics Hub"
&lt;/h2&gt;

&lt;p&gt;Imagine your React Native application is a fully assembled car, running inside a factory (the user's device). However, your raw materials (Source Code) are scattered everywhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Logic code lives in &lt;code&gt;ts&lt;/code&gt; and &lt;code&gt;js&lt;/code&gt; files.&lt;/li&gt;
&lt;li&gt; UI components are in &lt;code&gt;tsx&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Images are stored in an &lt;code&gt;assets&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt; Third-party parts are buried in the massive warehouse known as &lt;code&gt;node_modules&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the device had to go and fetch every single component individually at runtime, the app would be incredibly slow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metro acts as a giant Logistics Hub&lt;/strong&gt;. Its mission is to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Gather:&lt;/strong&gt; Locate every file your app needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process:&lt;/strong&gt; Translate "foreign" languages (TypeScript, JSX) into something the machine understands (Standard JavaScript).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Package:&lt;/strong&gt; Stuff everything into a single container (the &lt;code&gt;index.bundle&lt;/code&gt; file) and ship it to the device.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  2. Under the Hood: The 3 Stages of Metro
&lt;/h2&gt;

&lt;p&gt;To truly understand &lt;code&gt;metro.config.js&lt;/code&gt;, you need to visualize the lifecycle of the bundling process. It consists of three sequential stages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 1: Resolution (Finding the files)&lt;/strong&gt; 🔍&lt;br&gt;
This is where Metro answers the question: &lt;em&gt;"When you write import A from './A', where exactly is file A?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Unlike the Web, React Native has a unique mechanism called &lt;strong&gt;Platform-specific extensions&lt;/strong&gt;. When you write import &lt;code&gt;Button&lt;/code&gt;, Metro is smart enough to look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Button.ios.js&lt;/code&gt; (if running on iOS)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Button.android.js&lt;/code&gt; (if running on Android)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Button.native.js&lt;/code&gt; (fallback)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why React Native needs its own custom resolver instead of using Webpack. Metro is heavily optimized to search through thousands of modules within &lt;code&gt;node_modules&lt;/code&gt; in milliseconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 2: Transformation (Translation)&lt;/strong&gt; 🔄&lt;br&gt;
React Native (specifically the Hermes Engine or JSC) doesn't understand TypeScript, JSX, or the newest ESNext syntax. Metro pushes your code through a &lt;em&gt;"translator"&lt;/em&gt; (usually Babel).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It turns &lt;code&gt;&amp;lt;View&amp;gt;&lt;/code&gt; into &lt;code&gt;React.createElement(View)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;It strips types: &lt;code&gt;const name: string&lt;/code&gt; becomes &lt;code&gt;var name&lt;/code&gt;.
This is the most &lt;em&gt;CPU-intensive&lt;/em&gt; stage. Metro optimizes this using Caching and Parallel Processing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Stage 3: Serialization (Packaging)&lt;/strong&gt; 📦&lt;br&gt;
Once thousands of individual files have been translated, Metro will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Combine them into a single (or few) JavaScript bundle files.&lt;/li&gt;
&lt;li&gt;Assign numeric IDs to modules so the runtime knows which file to load first.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  3. Dissecting &lt;code&gt;metro.config.js&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Essentially, this config file allows you to intercept and modify the 3 stages mentioned above.&lt;br&gt;
There are two main objects you will interact with most often:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The Transformer (Intercepting Stage 2)&lt;/strong&gt;&lt;br&gt;
You use this when you want Metro to understand a file type that it doesn't support by default (e.g., SVGs). You need to tell Metro: &lt;em&gt;"Hey, don't treat .svg as text. Use &lt;code&gt;react-native-svg-transformer&lt;/code&gt; to turn it into a component."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The Resolver (Intercepting Stage 1)&lt;/strong&gt;&lt;br&gt;
You use this to guide Metro on where to look, or what not to look at.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sourceExts&lt;/code&gt;: List of extensions treated as source code (js, ts, tsx...).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;assetExts&lt;/code&gt;: List of static resources (png, jpg...).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;blockList&lt;/code&gt;: Used to forbid Metro from indexing specific folders to avoid conflicts.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  4. Real World Example
&lt;/h2&gt;

&lt;p&gt;Here is the standard code snippet that a developer would write to handle SVGs, using &lt;code&gt;mergeConfig&lt;/code&gt; (the modern approach for React Native &amp;gt; 0.72):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');

/**
 * Get the default config so we don't break standard RN settings
 */
const defaultConfig = getDefaultConfig(__dirname);
const {assetExts, sourceExts} = defaultConfig.resolver;

/**
 * Custom Config
 */
const config = {
  transformer: {
    // Use a specific transformer for SVGs
    babelTransformerPath: require.resolve('react-native-svg-transformer'),
  },
  resolver: {
    // Remove 'svg' from assetExts (so it's not copied as a raw file)
    assetExts: assetExts.filter((ext) =&amp;gt; ext !== 'svg'),

    // Add 'svg' to sourceExts (so it's processed as source code)
    sourceExts: [...sourceExts, 'svg'],
  },
};

// Merge default config with custom config
module.exports = mergeConfig(defaultConfig, config);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Why You - A React Native Developer Need to Care?
&lt;/h2&gt;

&lt;p&gt;You might ask: "As long as it runs, why should I care about this file?"&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Debugging "Haunted" Errors&lt;/strong&gt;: Someday, you will encounter an error like &lt;em&gt;"Unable to resolve module X"&lt;/em&gt; even though the module is there. Understanding the &lt;em&gt;Resolver&lt;/em&gt; and &lt;em&gt;Cache&lt;/em&gt;(&lt;code&gt;--reset-cache&lt;/code&gt;) is the key to exorcising these ghost errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance &amp;amp; Build Speed&lt;/strong&gt;: Knowing how to use &lt;code&gt;blockList&lt;/code&gt; to exclude unnecessary folders can significantly speed up your development cycle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monorepos &amp;amp; Symlinks&lt;/strong&gt;: In professional environments (Monorepo), Metro does not handle Symlinks well by default. You will eventually need to configure &lt;code&gt;watchFolders&lt;/code&gt; manually to make Metro "see" code outside the project root.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;code&gt;metro.config.js&lt;/code&gt; isn't scary. It is simply the map that guides the factory on how to build your code. The next time you see this file, remember: &lt;strong&gt;It decides what goes into your app and how it gets processed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;console.log(Thanks for reading!)&lt;/code&gt;&lt;/p&gt;

</description>
      <category>reactnative</category>
    </item>
  </channel>
</rss>
