<?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: Shivkrishna Shah</title>
    <description>The latest articles on DEV Community by Shivkrishna Shah (@shivkrishna-dev).</description>
    <link>https://dev.to/shivkrishna-dev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4022229%2F1a77a60a-879f-4fd3-b190-35c574a5bdd8.jpg</url>
      <title>DEV Community: Shivkrishna Shah</title>
      <link>https://dev.to/shivkrishna-dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shivkrishna-dev"/>
    <language>en</language>
    <item>
      <title>Offline-First in React Native: Building an Auto-Sync Engine That Users Never Think About</title>
      <dc:creator>Shivkrishna Shah</dc:creator>
      <pubDate>Thu, 27 Aug 2026 06:13:44 +0000</pubDate>
      <link>https://dev.to/shivkrishna-dev/offline-first-in-react-native-building-an-auto-sync-engine-that-users-never-think-about-3833</link>
      <guid>https://dev.to/shivkrishna-dev/offline-first-in-react-native-building-an-auto-sync-engine-that-users-never-think-about-3833</guid>
      <description>&lt;p&gt;By Shivkrishna Shah · Engineer Philosophy — @shivkrishnashah · &lt;a href="https://engineerphilosophy.com/" rel="noopener noreferrer"&gt;@engineerphilosophy&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your app shouldn't have a "no internet" screen. Here's the architecture I use to make mobile apps write locally, sync automatically, and survive the messy reality of field connectivity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every mobile developer has shipped this screen at least once: a sad cloud icon and the words &lt;em&gt;"No internet connection. Please try again."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For consumer apps, that's an annoyance. For &lt;strong&gt;enterprise field apps&lt;/strong&gt; — sales reps in hospital basements, auditors in warehouses, technicians in rural areas — it's a dealbreaker. If the app stops working when the signal drops, people stop trusting it. And once field users stop trusting an app, they go back to paper and WhatsApp.&lt;/p&gt;

&lt;p&gt;I spent the last few years building and maintaining an offline-first React Native platform used daily by field teams across multiple countries. This post is the architecture I wish someone had handed me on day one: &lt;strong&gt;how to structure local storage, detect connectivity, queue writes, auto-sync in the background, and avoid the two bugs that will absolutely bite you&lt;/strong&gt; (duplicates and conflicts).&lt;/p&gt;

&lt;p&gt;Everything here is generic — I'll use &lt;strong&gt;Realm DB&lt;/strong&gt; and &lt;strong&gt;NetInfo&lt;/strong&gt; in the examples, but the pattern maps cleanly onto WatermelonDB, SQLite, or MMKV-backed queues.&lt;/p&gt;




&lt;h2&gt;
  
  
  The one rule that changes everything
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The local database is the source of truth. The server is just a replica you happen to reconcile with.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most apps are built the other way around: the server is the truth, and the app is a thin cache over &lt;code&gt;fetch()&lt;/code&gt;. Offline-first inverts this. &lt;strong&gt;Every read comes from the local DB. Every write goes to the local DB first.&lt;/strong&gt; The network is an implementation detail that a background service worries about — never the UI.&lt;/p&gt;

&lt;p&gt;This single inversion gives you three things for free:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-latency UX.&lt;/strong&gt; Saves are instant because they're local writes. No spinners on submit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Airplane-mode parity.&lt;/strong&gt; The app behaves identically online and offline, because the UI never talks to the network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crash safety.&lt;/strong&gt; Data is durable the moment the user taps "Save" — even if the app is killed a second later.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────┐   write    ┌───────────────────┐  reconcile  ┌──────────────┐
│  UI/Screens  │ ─────────► │  Local DB (Realm) │ ◄─────────► │  Sync Engine │
│              │ ◄───────── │  SOURCE OF TRUTH  │             │  (background)│
└──────────────┘  live query└───────────────────┘             └──────┬───────┘
                                                                     │ when online
        The UI never touches the network. Ever.               ┌──────▼───────┐
                                              NetInfo ───────►│   REST API   │
                                           (wake / pause)     │   (replica)  │
                                                              └──────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 1 — Give every record a sync passport
&lt;/h2&gt;

&lt;p&gt;Offline-first lives or dies on &lt;strong&gt;per-record sync metadata&lt;/strong&gt;. Every table that can be written on-device carries the same extra fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Every offline-writable schema carries the same sync metadata&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;VisitLogSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;VisitLog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;primaryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;localId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// UUID generated on-device&lt;/span&gt;
    &lt;span class="na"&gt;serverId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;int?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// assigned by the server after first sync&lt;/span&gt;
    &lt;span class="c1"&gt;// ...domain fields (accountId, notes, timestamp, ...)&lt;/span&gt;

    &lt;span class="na"&gt;syncStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// 'pending' | 'syncing' | 'synced' | 'failed'&lt;/span&gt;
    &lt;span class="na"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;date&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// device clock, for conflict resolution&lt;/span&gt;
    &lt;span class="na"&gt;syncedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;date?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// last successful server ack&lt;/span&gt;
    &lt;span class="na"&gt;retryCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;int&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// exponential backoff counter&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;Three deliberate choices here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Client-generated primary keys (UUIDs).&lt;/strong&gt; The device must be able to create records — and relate them to each other — without asking the server for an ID. The server ID arrives later and is stored alongside, never as the primary key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;syncStatus&lt;/code&gt; is data, not app state.&lt;/strong&gt; It survives restarts, it's queryable (&lt;code&gt;syncStatus == 'pending'&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; your sync queue), and you can surface it in the UI as a per-record badge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;retryCount&lt;/code&gt; lives on the record.&lt;/strong&gt; Backoff shouldn't reset because the user relaunched the app.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;💡 &lt;strong&gt;Design note:&lt;/strong&gt; You don't need a separate "outbox" table if your DB is queryable — the pending queue is simply a live query over &lt;code&gt;syncStatus IN ('pending','failed')&lt;/code&gt; ordered by &lt;code&gt;updatedAt&lt;/code&gt;. One source of truth, no queue/table drift.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — The write path: local first, always
&lt;/h2&gt;

&lt;p&gt;Every save in the app goes through one door. No screen ever calls the API directly on submit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;saveVisitLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;realm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getRealm&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;realm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;realm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;VisitLog&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;localId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;syncStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// ← queued by definition&lt;/span&gt;
      &lt;span class="na"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;retryCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;requestSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;write&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// nudge the engine — fire and forget&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what's missing: no &lt;code&gt;await fetch()&lt;/code&gt;, no try/catch around a network call, no "are we online?" check. The save is complete the moment the Realm transaction commits. The UI can navigate away immediately and show a &lt;code&gt;PENDING&lt;/code&gt; badge on the record.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User taps "Save" ──► Realm write (status: pending) ──┬──► UI updates instantly ✓
                                                     └╌╌► Sync engine nudged ╌╌► Server (eventually)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3 — Connectivity: react to events, verify before flushing
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@react-native-community/netinfo&lt;/code&gt; gives you connectivity &lt;em&gt;events&lt;/em&gt;, but two gotchas matter in production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;isConnected&lt;/code&gt; means "has a network interface", not "can reach your API". Captive portals and dead corporate Wi-Fi will lie to you. Use &lt;code&gt;isInternetReachable&lt;/code&gt;, and treat even that as a hint.&lt;/li&gt;
&lt;li&gt;Connectivity flaps. Walking through a building can fire a dozen transitions per minute — debounce before triggering a flush.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;NetInfo&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@react-native-community/netinfo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;online&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;NetInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isConnected&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isInternetReachable&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;online&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;requestSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;network-restored&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// debounced inside&lt;/span&gt;
  &lt;span class="nx"&gt;online&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;next&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;Hard-won lesson:&lt;/strong&gt; Never gate the &lt;em&gt;save&lt;/em&gt; on connectivity — only gate the &lt;em&gt;flush&lt;/em&gt;. The moment you write &lt;code&gt;if (online) api.post() else saveLocally()&lt;/code&gt; you have two write paths, and they &lt;em&gt;will&lt;/em&gt; drift apart. One door: local write, then sync.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4 — The sync engine: a state machine, not a loop
&lt;/h2&gt;

&lt;p&gt;The engine is a single background service that drains the pending queue in batches. Every record moves through an explicit lifecycle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             batch picked            2xx + ack
 ┌─────────┐ ──────────► ┌─────────┐ ──────────► ┌──────────┐
 │ PENDING │             │ SYNCING │             │ SYNCED ✓ │
 └─────────┘ ◄╌╌╌╌╌╌╌╌╌╌ └─────────┘ ──────────► ┌──────────┐
       ▲   retry after        error / timeout    │  FAILED  │
       └╌╌ backoff: 2^retryCount × 30s (capped)  └──────────┘
                                                      │ retryCount &amp;gt; N
                                                      ▼ flag for support
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flushing&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;online&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="c1"&gt;// single-flight guard&lt;/span&gt;
  &lt;span class="nx"&gt;flushing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;batch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;realm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;VisitLog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;syncStatus == "pending" AND nextRetryAt &amp;lt;= $0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;updatedAt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BATCH_SIZE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;            &lt;span class="c1"&gt;// e.g. 50 records per request&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;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&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="nf"&gt;markStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;syncing&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/sync&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;records&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Server acks per record — never all-or-nothing&lt;/span&gt;
    &lt;span class="nx"&gt;realm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ack&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;acks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;realm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;objectForPrimaryKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;VisitLog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serverId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serverId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;syncStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;synced&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;syncedAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;syncStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;retryCount&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nextRetryAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;backoff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;retryCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// 2^n × 30s, capped at 1h&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;moreRemaining&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="nf"&gt;requestSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;drain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// keep draining&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;flushing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;The details that matter more than they look:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single-flight guard.&lt;/strong&gt; Multiple triggers (timer + network event + fresh write) must never produce two concurrent flushes. One boolean saves you from the nastiest class of duplicate bug.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batching.&lt;/strong&gt; Field users come back from a no-signal day with hundreds of pending records. One request per record melts your server and their battery; one giant request times out on 2G. Batch (~50) and drain iteratively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-record acks.&lt;/strong&gt; If record 37 of 50 fails validation, the other 49 must still succeed. All-or-nothing batches turn one bad record into a permanently stuck queue.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 5 — Auto-sync: triggers, not polling
&lt;/h2&gt;

&lt;p&gt;"Auto" sync is just wiring the same &lt;code&gt;requestSync()&lt;/code&gt; nudge to every moment connectivity or data might have changed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trigger&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;After every local write&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;saveX()&lt;/code&gt; helpers&lt;/td&gt;
&lt;td&gt;Online users sync within seconds — feels real-time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network restored&lt;/td&gt;
&lt;td&gt;NetInfo listener (debounced)&lt;/td&gt;
&lt;td&gt;The classic "walked out of the basement" moment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App → foreground&lt;/td&gt;
&lt;td&gt;AppState listener&lt;/td&gt;
&lt;td&gt;OS may have suspended timers while backgrounded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Periodic timer&lt;/td&gt;
&lt;td&gt;~every 2–5 min while active&lt;/td&gt;
&lt;td&gt;Safety net for missed events; also pulls server changes down&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual pull-to-refresh&lt;/td&gt;
&lt;td&gt;User&lt;/td&gt;
&lt;td&gt;Trust: users want a button even if they never need it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All five funnel into one debounced entry point — which is exactly why the single-flight guard in Step 4 exists.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6 — The duplicate problem (this one &lt;em&gt;will&lt;/em&gt; get you)
&lt;/h2&gt;

&lt;p&gt;Here's the failure sequence that produces duplicate records in every naive sync implementation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Device sends batch → server &lt;strong&gt;inserts&lt;/strong&gt; the rows…&lt;/li&gt;
&lt;li&gt;…but the response is lost (timeout, tunnel, app killed mid-request).&lt;/li&gt;
&lt;li&gt;Device never got the ack → records stay &lt;code&gt;PENDING&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Next flush re-sends them → server inserts them &lt;strong&gt;again&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The network being unreliable in &lt;em&gt;both directions&lt;/em&gt; is the whole premise of offline-first, so retries are guaranteed. The fix is &lt;strong&gt;idempotency&lt;/strong&gt;, enforced on both sides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client:&lt;/strong&gt; every record carries its device-generated &lt;code&gt;localId&lt;/code&gt; (UUID) in the payload — the same one on every retry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server:&lt;/strong&gt; a unique constraint on &lt;code&gt;(device_id, local_id)&lt;/code&gt; and upsert semantics: &lt;em&gt;"if I've seen this localId, return the previous ack instead of inserting."&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Server-side: retries become no-ops, not duplicates&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;visit_logs&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&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;ON&lt;/span&gt; &lt;span class="n"&gt;DUPLICATE&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LAST_INSERT_ID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;-- return existing row's id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ &lt;strong&gt;Also on the client:&lt;/strong&gt; If you have &lt;em&gt;two&lt;/em&gt; possible senders — say, a foreground "submit now" path and a background flush service — they can race and double-send the same rows before either ack lands. Either collapse them into one sender, or add a mutex so only one path can flush a given record type at a time. We learned this from a production duplicate-insert bug that only reproduced on slow networks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7 — Conflicts: pick a policy before you need one
&lt;/h2&gt;

&lt;p&gt;Downstream sync (server → device) eventually meets a record edited in both places. There is no universally correct answer — there is only &lt;strong&gt;a policy chosen per table, on purpose&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;Strategy&lt;/th&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Use for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Last-write-wins&lt;/td&gt;
&lt;td&gt;Higher &lt;code&gt;updatedAt&lt;/code&gt; wins&lt;/td&gt;
&lt;td&gt;Single-owner data (a rep's own notes) — simple, predictable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server-wins&lt;/td&gt;
&lt;td&gt;Server copy always replaces local&lt;/td&gt;
&lt;td&gt;Reference/master data the device merely displays&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client-wins&lt;/td&gt;
&lt;td&gt;Device copy survives until explicitly synced&lt;/td&gt;
&lt;td&gt;In-progress work the user is actively editing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Field-level merge&lt;/td&gt;
&lt;td&gt;Compare per column, merge non-overlapping edits&lt;/td&gt;
&lt;td&gt;High-value shared records — costs real complexity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual resolution&lt;/td&gt;
&lt;td&gt;Park both versions, ask a human&lt;/td&gt;
&lt;td&gt;Rare, high-stakes conflicts (approvals, financial data)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In a field-team context, &lt;strong&gt;last-write-wins with per-record ownership&lt;/strong&gt; covers ~90% of cases, because most offline-written records have exactly one author — the device that created them. Design your data model so this stays true and you may never need the expensive strategies.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Clock warning:&lt;/strong&gt; LWW compares timestamps, and device clocks lie. Record the device's &lt;code&gt;updatedAt&lt;/code&gt; but let the &lt;em&gt;server&lt;/em&gt; stamp arrival time and sanity-check drift (e.g., reject client timestamps from the future). Never resolve conflicts with unvetted device time alone.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd tell you before you build it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Show sync state honestly.&lt;/strong&gt; A tiny &lt;code&gt;PENDING&lt;/code&gt; / &lt;code&gt;SYNCED&lt;/code&gt; badge per record, plus a "3 records waiting to sync" strip, converts anxiety into trust. Users forgive a delayed sync; they never forgive silent data loss.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sync your error logs too.&lt;/strong&gt; When a device misbehaves offline, the evidence is trapped on it. Treat error logs as just another offline-writable table that flushes with everything else — production debugging becomes possible again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test with the network chaos monkey.&lt;/strong&gt; Airplane mode mid-flush, killed app mid-request, captive-portal Wi-Fi, 48 hours offline then 400 pending records. Every one of these is a Tuesday for a field user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never let a stuck record block the queue.&lt;/strong&gt; Cap retries, quarantine poison records, flag them for support — and keep draining the rest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design the server API for sync from day one.&lt;/strong&gt; Batched upserts, per-record acks, idempotency keys. Retrofitting idempotency onto a plain CRUD API is far more painful than building it in.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The payoff
&lt;/h2&gt;

&lt;p&gt;The architecture above is maybe 500 lines of engine code plus a schema convention — small enough to own completely, with no black-box sync SaaS in the middle. What it buys you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Saves that complete in &lt;strong&gt;milliseconds, every time&lt;/strong&gt;, regardless of signal.&lt;/li&gt;
&lt;li&gt;A field workforce that can work a full offline day and reconcile automatically over lunch Wi-Fi.&lt;/li&gt;
&lt;li&gt;Zero duplicate submissions — idempotency by construction, not by luck.&lt;/li&gt;
&lt;li&gt;One write path, one queue, one state machine: an architecture a new team member can hold in their head.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Offline-first isn't a feature you add. It's a decision about where the truth lives — and everything else follows from it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you're building something similar — or wrestling with sync bugs in production — I'm happy to compare notes. Drop a comment or reach out.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#ReactNative #OfflineFirst #MobileArchitecture #RealmDB #SyncEngine #MobileDevelopment&lt;/code&gt;&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>offlinefirst</category>
      <category>mobilearchitecture</category>
      <category>syncengine</category>
    </item>
    <item>
      <title>Shipping Whisper-Powered Voice Input in a Mobile App: What Actually Works</title>
      <dc:creator>Shivkrishna Shah</dc:creator>
      <pubDate>Thu, 27 Aug 2026 06:03:19 +0000</pubDate>
      <link>https://dev.to/shivkrishna-dev/shipping-whisper-powered-voice-input-in-a-mobile-app-what-actually-works-1bl1</link>
      <guid>https://dev.to/shivkrishna-dev/shipping-whisper-powered-voice-input-in-a-mobile-app-what-actually-works-1bl1</guid>
      <description>&lt;p&gt;Voice input demos beautifully and ships painfully.&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;&lt;a href="https://engineerphilosophy.com" rel="noopener noreferrer"&gt;Engineer Philosophy&lt;/a&gt;&lt;/strong&gt;, my firm, we recently built a voice-first data entry flow for a field-facing mobile app: a user taps a mic, talks through a long structured form naturally, and the app transcribes their speech, interprets it, and fills the form — with a confirmation step before anything is saved. Under the hood it runs on OpenAI's Whisper family of speech-to-text models.&lt;/p&gt;

&lt;p&gt;Getting a transcript back from an API took an afternoon. Getting voice input that field users trust took weeks — and almost none of that time was spent on the API call itself. Here's what actually moved the needle, in three parts: architecture, voice detection and recording, and turning transcripts into responses you can use.&lt;/p&gt;

&lt;p&gt;![The Voice → Form Pipeline]&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj5egi0up4f6z1pr1x36t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj5egi0up4f6z1pr1x36t.png" alt=" " width="800" height="827"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: Using Whisper in an application — two paths, not one
&lt;/h2&gt;

&lt;p&gt;Most tutorials show one flow: record an audio file, POST it to &lt;code&gt;/v1/audio/transcriptions&lt;/code&gt;, read the text. That works, but a production app benefits from &lt;strong&gt;two transcription paths running as a pair&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Batch (record → upload).&lt;/strong&gt; The recorder captures compressed audio (AAC), and on stop the file goes to the transcriptions endpoint. Models like &lt;code&gt;whisper-1&lt;/code&gt; or the newer &lt;code&gt;gpt-4o-mini-transcribe&lt;/code&gt; (~$0.003/min) work here. It's simple, robust, and handles long takes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Realtime (streaming).&lt;/strong&gt; For live "words appear as you speak" feedback, we stream raw PCM (16-bit, 24 kHz, mono) chunks from a native microphone tap over a WebSocket to the Realtime API and render transcription deltas as they arrive. Seeing your words appear live builds enormous trust — users stop wondering whether the app heard them.&lt;/p&gt;

&lt;p&gt;The crucial design decision: &lt;strong&gt;realtime is an enhancement, batch is the contract.&lt;/strong&gt; If the WebSocket fails to open, or the native audio streamer is unavailable, the app silently falls back to record-then-upload. The user never sees a difference except the live transcript. Every layer of the feature follows this "fail open to the simpler path" rule — including an env-var kill switch that can restore the old behavior without touching a line of code.&lt;/p&gt;

&lt;p&gt;Two gotchas worth knowing up front:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Models are endpoint-specific.&lt;/strong&gt; The realtime transcription model only exists on the WebSocket API — sending it to the file endpoint 404s. Verify each model against the endpoint you're actually calling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The file endpoint has a 25 MB hard limit.&lt;/strong&gt; Check the size client-side and fail fast rather than uploading for minutes just to receive a 413. At ~128 kbps AAC that's roughly 26 minutes of audio — which directly informed our recording cap (more below).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 2: Improving voice detection and recording
&lt;/h2&gt;

&lt;p&gt;This is where the real engineering lived. Three lessons stand out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Measure loudness in dBFS, and use ONE scale everywhere
&lt;/h3&gt;

&lt;p&gt;Our first silence detector compared the mic level against a threshold of &lt;code&gt;0.08&lt;/code&gt;. The problem: one capture path reported a logarithmic dBFS-derived level and the other reported linear amplitude. The same threshold meant "−46 dBFS" on one path and "amplitude 0.016" on the other — so the same speech produced different meter readings and different auto-stop behavior depending on which path happened to be active.&lt;/p&gt;

&lt;p&gt;The fix: both paths now convert amplitude → dBFS → the same normalization, and every threshold in the system is expressed in dBFS. Decibels are how audio actually behaves; a linear 0–1 "level" is a lie that will eventually bite you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Detect silence relative to the speaker, not against a fixed floor
&lt;/h3&gt;

&lt;p&gt;The naive auto-stop rule — "stop when the level drops below X for N seconds" — fails in the field, and the reason is subtle: &lt;strong&gt;automatic gain control&lt;/strong&gt;. In a noisy clinic or a car, the phone's AGC lifts the noise floor between utterances, so a fixed silence threshold simply never triggers.&lt;/p&gt;

&lt;p&gt;Our detector uses layered rules instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A speech gate (−35 dBFS):&lt;/strong&gt; above this, a sample is definitely speech. Room tone sits below it; a person talking does not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relative silence:&lt;/strong&gt; quiet is judged 18 dB &lt;em&gt;below the speaker's own loudest speech&lt;/em&gt;, not against an absolute floor. Speech runs 20–30 dB above ambient noise in almost any room, so this survives noisy environments where a fixed threshold is useless.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2.5 seconds of continuous silence&lt;/strong&gt; to finish — long enough to survive a mid-sentence breath or a pause to think. Stopping too eagerly is worse than stopping late, because truncated words are simply lost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A minimum of 800 ms of heard speech&lt;/strong&gt; before silence can end the recording — otherwise the mic closes during the gap between tapping the button and starting to talk.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One rule we added and removed the &lt;em&gt;same day&lt;/em&gt;: a live "you're too quiet" warning based on the average of the last ~20 samples. The average includes the silence between words, so ordinary speech averaged below the threshold and the warning fired at people who were being heard perfectly. &lt;strong&gt;Anything you surface live must use a peak or percentile, never a mean.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep audio levels out of your UI framework's state
&lt;/h3&gt;

&lt;p&gt;We shipped a regression where users reported "mic performance reduced." The cause: the input level was stored as React state, so every metering sample — 4 to 10 per second — re-rendered the entire recording screen, &lt;em&gt;on the same JS thread that was streaming audio to the transcription socket&lt;/em&gt;. A decorative gradient ring being reconciled at metering rate made it worse.&lt;/p&gt;

&lt;p&gt;The fix: the level became an animated value that bypasses the framework's render cycle entirely (in React Native terms, an &lt;code&gt;Animated.Value&lt;/code&gt; eased on the native driver). Samples never reach React; the UI interpolates. We deliberately removed any numeric level from the API so nobody could reintroduce the problem.&lt;/p&gt;

&lt;p&gt;The same discipline applies to the auto-stop countdown: instead of one state write per sample, it runs as two animations per quiet episode. If your meter UI costs more than your audio pipeline, you've built it wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pause/Resume: three details that matter
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Actually stop the mic natively&lt;/strong&gt; — don't fake it in the UI. Showing "Paused" over a live microphone is a trust violation worth extra code to avoid, so if the native pause call fails, roll back and keep showing "Recording."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Close the silence detector while paused&lt;/strong&gt; — otherwise the pause itself triggers auto-stop 2.5 seconds in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reset the detector on resume&lt;/strong&gt; — so the pause's own quiet doesn't count toward the silence budget.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 3: Transcribing and getting the response you need
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pass a language hint — it's the cheapest accuracy win available
&lt;/h3&gt;

&lt;p&gt;Both Whisper paths accept a &lt;code&gt;language&lt;/code&gt; parameter, and for months we weren't passing it — every recording ran on auto-detect even though the app &lt;em&gt;already knew&lt;/em&gt; which of its 13 supported languages the user had chosen. Auto-detect is a real accuracy cost: it's the usual cause of a transcript coming back in the wrong script, and of code-switched speech (Hindi-English mixes like &lt;em&gt;"do accounts pending hain"&lt;/em&gt;) being classified as the wrong language outright.&lt;/p&gt;

&lt;p&gt;The fix is one function: map the app's language setting to a base ISO-639-1 code (dropping script tags — &lt;code&gt;zh-Hant&lt;/code&gt; becomes &lt;code&gt;zh&lt;/code&gt;) and pass it on every request. Unknown language → omit the parameter and let the model auto-detect, exactly as before. Tiny change, outsized effect on multilingual accuracy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handle the WebSocket closing &lt;em&gt;cleanly&lt;/em&gt; — not just erroring
&lt;/h3&gt;

&lt;p&gt;The nastiest bug we shipped: mid-recording, the live transcript silently stopped growing while the level meter kept moving. Users kept talking; every word after a certain point was lost.&lt;/p&gt;

&lt;p&gt;The cause: our streaming client handled &lt;code&gt;onerror&lt;/code&gt; but its &lt;code&gt;onclose&lt;/code&gt; handler did nothing once the session was established. Sockets can close &lt;strong&gt;cleanly&lt;/strong&gt; — server idle timeouts, session-duration limits, a network blip with no error event — and our feed loop kept pumping audio into a dead socket whose send-failures were deliberately swallowed.&lt;/p&gt;

&lt;p&gt;Three fixes: &lt;code&gt;onclose&lt;/code&gt; marks the session closed and reports it; the feeder stops pumping a closed socket; and the UI surfaces "live transcription stopped" &lt;em&gt;while recording&lt;/em&gt; — not only after the take ends. And note the interaction with another change: when we raised the recording cap from 2 to 15 minutes, long takes started hitting session limits that short takes never reached. &lt;strong&gt;Every limit you relax exposes failure modes you've never seen.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The transcript is not the answer — interpret it
&lt;/h3&gt;

&lt;p&gt;For our use case, the transcript is an intermediate artifact. The raw text goes to an LLM agent that maps it onto the form's actual questions — single choice, multi-select, numeric, percentage groups, rankings — producing structured draft answers. Two principles keep this safe:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Confirm before writing.&lt;/strong&gt; The user reviews a summary of interpreted answers; nothing touches the database until they approve. "Start over" genuinely discards.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Show progress honestly.&lt;/strong&gt; Interpretation takes seconds; a phased progress view (route → read → act → reply) replaced a spinner and dramatically reduced abandoned sessions.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Don't ship your API key in the app
&lt;/h3&gt;

&lt;p&gt;One security note that belongs in every article like this: an API key baked into a mobile binary is extractable by anyone with the app file — no jailbreak required — and certificate pinning doesn't save you. The only real fix is the key not being on the device: route through your backend proxy, and for the realtime WebSocket use server-minted ephemeral tokens. For a POC, at minimum use a dedicated key with a hard spend limit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The meta-lesson: test on real hardware
&lt;/h2&gt;

&lt;p&gt;Simulator microphones never produced audio loud enough to cross our −35 dBFS speech gate — so the entire "speech was heard" branch of the system was unverifiable there. The meter zones, the silence countdown, the interpret flow: all of it needed a physical device. If your feature has a threshold, your test environment must be able to cross it.&lt;/p&gt;




&lt;p&gt;Voice input isn't a transcription API call. It's a signal-processing problem (dBFS, AGC, relative thresholds), a performance problem (keep samples off the render path), a distributed-systems problem (sockets close cleanly), and a UX problem (live feedback, honest failure states, confirm-before-write). Whisper solves exactly one layer of that stack — brilliantly — and the rest is where your product is actually built.&lt;/p&gt;

&lt;p&gt;That layered, measure-first way of working is the whole reason my firm is called &lt;strong&gt;&lt;a href="https://engineerphilosophy.com" rel="noopener noreferrer"&gt;Engineer Philosophy&lt;/a&gt;&lt;/strong&gt;: every threshold in this feature earned its number from real field behavior, every new capability fails open to a simpler path, and nothing was declared "done" from a simulator. That's the philosophy; the engineering follows from it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Building voice features into your app? I'd love to hear what tripped you up — the failure modes above can't be the whole list. And if you want help shipping one, that's exactly what we do at Engineer Philosophy.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;#AI #Whisper #SpeechToText #MobileDevelopment #ReactNative #VoiceUI #OpenAI #ProductEngineering&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Tokens, Context, Parameters: The Real Mechanics of an LLM</title>
      <dc:creator>Shivkrishna Shah</dc:creator>
      <pubDate>Wed, 29 Jul 2026 07:19:52 +0000</pubDate>
      <link>https://dev.to/shivkrishna-dev/tokens-context-parameters-the-real-mechanics-of-an-llm-466i</link>
      <guid>https://dev.to/shivkrishna-dev/tokens-context-parameters-the-real-mechanics-of-an-llm-466i</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fecgjgbw3xq3mqx3p5heb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fecgjgbw3xq3mqx3p5heb.png" alt=" " width="800" height="1686"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Tokens, Context, and Parameters: How Large Language Models Actually Work
&lt;/h1&gt;

&lt;p&gt;Large language models can answer questions, generate software, summarize documents, and produce human-like writing. These abilities emerge from three closely connected elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tokens&lt;/strong&gt; represent the information entering and leaving the model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context&lt;/strong&gt; contains the information available during the current prediction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameters&lt;/strong&gt; encode the statistical patterns learned during training.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, these components allow an LLM to predict one token at a time.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Tokens: The Model’s Units of Language
&lt;/h2&gt;

&lt;p&gt;A language model does not process text directly as sentences or complete words. It first uses a &lt;strong&gt;tokenizer&lt;/strong&gt; to divide text into smaller units called tokens.&lt;/p&gt;

&lt;p&gt;A token can represent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A complete word&lt;/li&gt;
&lt;li&gt;Part of a word&lt;/li&gt;
&lt;li&gt;Punctuation&lt;/li&gt;
&lt;li&gt;A number&lt;/li&gt;
&lt;li&gt;Whitespace&lt;/li&gt;
&lt;li&gt;A special control symbol&lt;/li&gt;
&lt;/ul&gt;

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

&lt;blockquote&gt;
&lt;p&gt;The model is learning.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;might become:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;The&lt;/code&gt; · &lt;code&gt;model&lt;/code&gt; · &lt;code&gt;is&lt;/code&gt; · &lt;code&gt;learn&lt;/code&gt; · &lt;code&gt;ing&lt;/code&gt; · &lt;code&gt;.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Breaking uncommon words into smaller pieces allows the model to represent a very large vocabulary without storing every possible word.&lt;/p&gt;

&lt;h3&gt;
  
  
  From text to token IDs
&lt;/h3&gt;

&lt;p&gt;Every token is assigned an integer called a &lt;strong&gt;token ID&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"The"     → 464
" model"  → 2746
" is"     → 318
" learn"  → 2193
"ing"     → 278
"."        → 13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact IDs depend on the tokenizer used by the model.&lt;/p&gt;

&lt;p&gt;Token IDs are only labels. Before they can be processed mathematically, the model converts them into vectors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Token embeddings
&lt;/h3&gt;

&lt;p&gt;The model contains a learned &lt;strong&gt;embedding matrix&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vocabulary size × embedding dimension
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the vocabulary contains 50,000 tokens and the model’s internal dimension is 4,096, the embedding matrix has the shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;50,000 × 4,096
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each row is a learned numerical representation of one token. Looking up a token ID selects the corresponding row from the matrix.&lt;/p&gt;

&lt;p&gt;A token therefore becomes a vector such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[0.14, -0.82, 0.37, ..., 0.09]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The individual values usually do not have simple human-readable meanings. Meaning is distributed across many dimensions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Position information
&lt;/h3&gt;

&lt;p&gt;Transformers process multiple tokens in parallel, so they also need information about token order. The model adds or applies a &lt;strong&gt;positional representation&lt;/strong&gt; to each token embedding.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input representation = token embedding + position information
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helps the model distinguish between sentences such as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The dog chased the cat.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The cat chased the dog.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They contain similar tokens, but their order changes their meaning.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Context: The Model’s Temporary Working Information
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;context&lt;/strong&gt; is the complete sequence of tokens available to the model during a prediction.&lt;/p&gt;

&lt;p&gt;It can include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System instructions&lt;/li&gt;
&lt;li&gt;The user’s prompt&lt;/li&gt;
&lt;li&gt;Previous conversation messages&lt;/li&gt;
&lt;li&gt;Retrieved information&lt;/li&gt;
&lt;li&gt;Uploaded document content&lt;/li&gt;
&lt;li&gt;Tool results&lt;/li&gt;
&lt;li&gt;Tokens already generated in the response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A model’s &lt;strong&gt;context window&lt;/strong&gt; defines the maximum number of tokens it can process at one time.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Instructions + conversation + documents + generated output ≤ context window
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the combined content exceeds the limit, some information must be removed, compressed, summarized, or handled through another retrieval mechanism.&lt;/p&gt;

&lt;h3&gt;
  
  
  Context is not permanent memory
&lt;/h3&gt;

&lt;p&gt;Context acts more like temporary working memory than permanent storage. Information inside the active window can influence the next prediction. Information outside it normally cannot unless it is supplied again.&lt;/p&gt;

&lt;p&gt;This creates an important distinction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Parameters = patterns learned during training
Context    = information supplied for the current task
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The context changes from one conversation to another. The model’s parameters normally remain fixed during ordinary inference.&lt;/p&gt;

&lt;h3&gt;
  
  
  Causal attention
&lt;/h3&gt;

&lt;p&gt;Most text-generating LLMs use &lt;strong&gt;causal attention&lt;/strong&gt;. When predicting a token, the model may attend to previous tokens but not future tokens.&lt;/p&gt;

&lt;p&gt;For a sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The capital of Japan is
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the representation at the final position can use information from all preceding positions. It cannot use the answer token because that token has not been generated yet.&lt;/p&gt;

&lt;p&gt;A causal mask enforces this direction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Token 1 can attend to: 1
Token 2 can attend to: 1, 2
Token 3 can attend to: 1, 2, 3
Token 4 can attend to: 1, 2, 3, 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what makes autoregressive generation possible.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Parameters: The Model’s Learned Numerical Structure
&lt;/h2&gt;

&lt;p&gt;Parameters are the trainable numerical values inside the neural network. They include large matrices and smaller vectors used throughout the transformer.&lt;/p&gt;

&lt;p&gt;The symbol &lt;strong&gt;θ&lt;/strong&gt;, pronounced “theta,” is often used to represent all model parameters collectively.&lt;/p&gt;

&lt;p&gt;A model with billions of parameters contains billions of learned floating-point values. These parameters do not form a searchable database of sentences. Instead, information is distributed across many interacting weights.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where the parameters are located
&lt;/h3&gt;

&lt;p&gt;A transformer language model typically contains parameters in several major components.&lt;/p&gt;

&lt;h4&gt;
  
  
  Embedding matrix
&lt;/h4&gt;

&lt;p&gt;The embedding matrix converts token IDs into vectors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;token ID → embedding vector
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These embeddings are learned during training.&lt;/p&gt;

&lt;h4&gt;
  
  
  Attention projections
&lt;/h4&gt;

&lt;p&gt;Inside each self-attention layer, the input representation is transformed into &lt;strong&gt;queries&lt;/strong&gt;, &lt;strong&gt;keys&lt;/strong&gt;, and &lt;strong&gt;values&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Q = XWQ
K = XWK
V = XWV
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;X&lt;/code&gt; is the current sequence representation.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WQ&lt;/code&gt; is the query projection matrix.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WK&lt;/code&gt; is the key projection matrix.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WV&lt;/code&gt; is the value projection matrix.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Attention scores are calculated using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Attention(Q, K, V) = softmax(QKᵀ / √dk)V
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is commonly passed through another learned matrix, &lt;code&gt;WO&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These matrices let the model determine which previous positions are relevant to the current token.&lt;/p&gt;

&lt;h4&gt;
  
  
  Multi-head attention
&lt;/h4&gt;

&lt;p&gt;Transformers usually divide attention into multiple &lt;strong&gt;attention heads&lt;/strong&gt;. Each head has separate projection weights and can learn different relationships.&lt;/p&gt;

&lt;p&gt;Different heads may become sensitive to patterns involving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nearby words&lt;/li&gt;
&lt;li&gt;Long-distance dependencies&lt;/li&gt;
&lt;li&gt;Grammatical structure&lt;/li&gt;
&lt;li&gt;References between names and pronouns&lt;/li&gt;
&lt;li&gt;Formatting or positional relationships&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The heads are combined and projected back into the model’s main representation space.&lt;/p&gt;

&lt;h4&gt;
  
  
  Feed-forward network
&lt;/h4&gt;

&lt;p&gt;Each transformer block also contains a feed-forward network, often called an &lt;strong&gt;MLP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A simplified version is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MLP(X) = activation(XW1 + b1)W2 + b2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first projection usually expands the representation into a larger intermediate dimension. The second projects it back to the model dimension.&lt;/p&gt;

&lt;p&gt;Modern models may use gated structures such as SwiGLU, but the purpose is similar: transform the representation at each token position using learned nonlinear features.&lt;/p&gt;

&lt;h4&gt;
  
  
  Layer normalization
&lt;/h4&gt;

&lt;p&gt;Layer-normalization components help keep internal activations numerically stable. They may contain learned scaling and shifting parameters.&lt;/p&gt;

&lt;h4&gt;
  
  
  Output projection
&lt;/h4&gt;

&lt;p&gt;After the final transformer layer, the model converts the hidden representation into one score, or &lt;strong&gt;logit&lt;/strong&gt;, for every possible token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hidden state → vocabulary logits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A softmax function converts these logits into a probability distribution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;P(next token | previous tokens; θ)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model can then select or sample the next token from this distribution.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Inside a Transformer Block
&lt;/h2&gt;

&lt;p&gt;An LLM contains many transformer blocks stacked on top of one another. Each block gradually refines the representation of every token.&lt;/p&gt;

&lt;p&gt;A simplified block contains:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Layer normalization&lt;/li&gt;
&lt;li&gt;Multi-head self-attention&lt;/li&gt;
&lt;li&gt;A residual connection&lt;/li&gt;
&lt;li&gt;Another normalization stage&lt;/li&gt;
&lt;li&gt;A feed-forward network&lt;/li&gt;
&lt;li&gt;Another residual connection&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The general flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input representations
        ↓
Layer normalization
        ↓
Self-attention
        ↓
Residual addition
        ↓
Layer normalization
        ↓
Feed-forward network
        ↓
Residual addition
        ↓
Output representations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Residual connections
&lt;/h3&gt;

&lt;p&gt;Residual connections add a block’s input to its output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;output = input + transformation(input)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They help information and gradients travel through deep networks. Without them, training models with many layers would be considerably more difficult.&lt;/p&gt;

&lt;h3&gt;
  
  
  What changes through the layers?
&lt;/h3&gt;

&lt;p&gt;Early layers may identify local or surface-level patterns. Deeper layers can combine information across positions and represent more abstract relationships.&lt;/p&gt;

&lt;p&gt;The model’s internal representation of a token therefore depends on both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The token’s original embedding&lt;/li&gt;
&lt;li&gt;The surrounding tokens in the current context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The word “bank,” for example, may develop different contextual representations in:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;She deposited money at the bank.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;They sat on the river bank.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  5. How an LLM Is Trained
&lt;/h2&gt;

&lt;p&gt;Before an LLM can generate useful text, its parameters must be learned from data.&lt;/p&gt;

&lt;p&gt;The basic training objective is &lt;strong&gt;next-token prediction&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Prepare the training data
&lt;/h3&gt;

&lt;p&gt;Training text is cleaned, filtered, and converted into token sequences.&lt;/p&gt;

&lt;p&gt;Suppose a training sequence is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Large language models predict tokens
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model receives shifted input-and-target pairs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input:  Large
Target: language

Input:  Large language
Target: models

Input:  Large language models
Target: predict
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In practice, many positions are trained simultaneously.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Run a forward pass
&lt;/h3&gt;

&lt;p&gt;The token sequence passes through the embedding layer and transformer blocks.&lt;/p&gt;

&lt;p&gt;For every position, the model produces a probability distribution over the vocabulary.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"tokens"      0.61
"text"        0.14
"words"       0.09
"outputs"     0.04
other tokens  0.12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Measure the error
&lt;/h3&gt;

&lt;p&gt;The predicted distribution is compared with the actual next token using &lt;strong&gt;cross-entropy loss&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For the correct token &lt;code&gt;y&lt;/code&gt;, the loss can be written as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;L = −log P(y | previous tokens; θ)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the model gives the correct token a high probability, the loss is low. If it assigns the correct token a low probability, the loss is high.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Backpropagate the error
&lt;/h3&gt;

&lt;p&gt;The training system uses &lt;strong&gt;backpropagation&lt;/strong&gt; to calculate how much each parameter contributed to the error.&lt;/p&gt;

&lt;p&gt;These derivatives form the gradient:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;∇θL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gradient indicates which direction would increase the loss. Training moves the parameters in the opposite direction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Update the parameters
&lt;/h3&gt;

&lt;p&gt;An optimizer updates the weights:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;θ ← θ − η∇θL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;θ&lt;/code&gt; represents the model’s parameters.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;L&lt;/code&gt; represents the loss.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;∇θL&lt;/code&gt; represents the loss gradient.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;η&lt;/code&gt; represents the learning rate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Real training usually employs optimizers such as Adam or AdamW rather than basic gradient descent, but the underlying idea is the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Repeat across many batches
&lt;/h3&gt;

&lt;p&gt;This process is repeated across enormous numbers of token sequences:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Forward pass
    ↓
Calculate loss
    ↓
Backpropagation
    ↓
Parameter update
    ↓
Next batch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Over time, the model becomes better at predicting tokens across many types of text.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Pretraining, Fine-Tuning, and Alignment
&lt;/h2&gt;

&lt;p&gt;LLM development commonly involves several training stages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pretraining
&lt;/h3&gt;

&lt;p&gt;During pretraining, the model learns general language patterns from a large and diverse dataset through next-token prediction.&lt;/p&gt;

&lt;p&gt;It may learn patterns involving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grammar and syntax&lt;/li&gt;
&lt;li&gt;Semantic relationships&lt;/li&gt;
&lt;li&gt;Writing styles&lt;/li&gt;
&lt;li&gt;Factual associations&lt;/li&gt;
&lt;li&gt;Programming languages&lt;/li&gt;
&lt;li&gt;Common reasoning structures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pretraining creates the model’s broad foundational capabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Supervised fine-tuning
&lt;/h3&gt;

&lt;p&gt;The pretrained model may then be trained on curated examples containing prompts and high-quality responses.&lt;/p&gt;

&lt;p&gt;This teaches it to follow instructions and produce answers in more useful formats.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preference optimization
&lt;/h3&gt;

&lt;p&gt;Human or AI-generated preference data can be used to improve helpfulness, safety, and response quality.&lt;/p&gt;

&lt;p&gt;Possible approaches include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reinforcement learning from human feedback&lt;/li&gt;
&lt;li&gt;Direct Preference Optimization&lt;/li&gt;
&lt;li&gt;Related preference-learning techniques&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These stages still modify parameters, but they use more targeted data and objectives than general pretraining.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. How Generation Works After Training
&lt;/h2&gt;

&lt;p&gt;During normal use, the model performs &lt;strong&gt;inference&lt;/strong&gt;. Its learned parameters are usually fixed.&lt;/p&gt;

&lt;p&gt;Suppose the prompt is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The capital of Japan is&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The generation process is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The tokenizer converts the text into token IDs.&lt;/li&gt;
&lt;li&gt;The embedding layer converts the IDs into vectors.&lt;/li&gt;
&lt;li&gt;Position information is incorporated.&lt;/li&gt;
&lt;li&gt;Transformer layers process the context.&lt;/li&gt;
&lt;li&gt;The output layer produces vocabulary logits.&lt;/li&gt;
&lt;li&gt;Softmax converts the logits into probabilities.&lt;/li&gt;
&lt;li&gt;A decoding strategy chooses the next token.&lt;/li&gt;
&lt;li&gt;The chosen token is added to the context.&lt;/li&gt;
&lt;li&gt;The process repeats.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The model might produce:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tokyo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After generating &lt;code&gt;Tokyo&lt;/code&gt;, it predicts the token that should follow it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decoding strategies
&lt;/h3&gt;

&lt;p&gt;The model does not always select the single highest-probability token. Generation can be controlled using methods such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Greedy decoding:&lt;/strong&gt; choose the most probable token.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temperature:&lt;/strong&gt; adjust how sharp or varied the probability distribution is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Top-k sampling:&lt;/strong&gt; sample only from the &lt;code&gt;k&lt;/code&gt; most likely tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Top-p sampling:&lt;/strong&gt; sample from the smallest group whose combined probability reaches a threshold.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These settings affect how the model selects output tokens. They do not retrain its parameters.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Parameters Versus Context
&lt;/h2&gt;

&lt;p&gt;Parameters and context influence the same prediction in different ways.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Context&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Purpose&lt;/td&gt;
&lt;td&gt;Store learned statistical patterns&lt;/td&gt;
&lt;td&gt;Supply information for the current prediction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Origin&lt;/td&gt;
&lt;td&gt;Learned during training&lt;/td&gt;
&lt;td&gt;Provided at inference time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Duration&lt;/td&gt;
&lt;td&gt;Persists across prompts&lt;/td&gt;
&lt;td&gt;Temporary and task-specific&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical scale&lt;/td&gt;
&lt;td&gt;Millions or billions of values&lt;/td&gt;
&lt;td&gt;Thousands or millions of tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Changed by normal prompting?&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Example&lt;/td&gt;
&lt;td&gt;Learned language structure&lt;/td&gt;
&lt;td&gt;The current conversation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This distinction explains why a model can adapt to instructions without changing its weights. The instructions alter the context, which changes the model’s internal activations and predictions.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. What Parameter Count Does—and Does Not—Mean
&lt;/h2&gt;

&lt;p&gt;A parameter count measures the model’s numerical capacity. A larger model can potentially represent more complex patterns, but size alone does not determine performance.&lt;/p&gt;

&lt;p&gt;Quality also depends on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Training-data quality and diversity&lt;/li&gt;
&lt;li&gt;Model architecture&lt;/li&gt;
&lt;li&gt;Tokenizer design&lt;/li&gt;
&lt;li&gt;Training duration&lt;/li&gt;
&lt;li&gt;Optimization methods&lt;/li&gt;
&lt;li&gt;Context handling&lt;/li&gt;
&lt;li&gt;Fine-tuning and alignment&lt;/li&gt;
&lt;li&gt;Inference techniques&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A smaller model trained efficiently on appropriate data can outperform a larger model on a specialized task.&lt;/p&gt;

&lt;p&gt;Parameter count should therefore be treated as one engineering characteristic—not a complete measure of intelligence or quality.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Common Misconceptions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  “One token equals one word”
&lt;/h3&gt;

&lt;p&gt;Not necessarily. A word may be one token, several tokens, or part of a larger token.&lt;/p&gt;

&lt;h3&gt;
  
  
  “The context window is permanent memory”
&lt;/h3&gt;

&lt;p&gt;It is temporary information available during the current inference process.&lt;/p&gt;

&lt;h3&gt;
  
  
  “Parameters contain copies of documents”
&lt;/h3&gt;

&lt;p&gt;Parameters encode distributed numerical patterns. They are not equivalent to files or database records, although models can sometimes reproduce memorized material.&lt;/p&gt;

&lt;h3&gt;
  
  
  “The model searches its training data for every answer”
&lt;/h3&gt;

&lt;p&gt;During standard inference, the model calculates predictions using its parameters and current context. External search or retrieval occurs only when a separate tool or retrieval system is connected.&lt;/p&gt;

&lt;h3&gt;
  
  
  “The model learns from every conversation immediately”
&lt;/h3&gt;

&lt;p&gt;Ordinary conversations usually do not update the model’s parameters. Updating parameters requires a separate training process.&lt;/p&gt;

&lt;h3&gt;
  
  
  “More parameters always produce a better model”
&lt;/h3&gt;

&lt;p&gt;Model size is only one factor. Architecture, data, training, and task fit are equally important.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Complete Picture
&lt;/h2&gt;

&lt;p&gt;The complete LLM pipeline can be summarized as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Text
  ↓
Tokenizer
  ↓
Token IDs
  ↓
Token embeddings + position information
  ↓
Transformer layers
  ├─ Self-attention
  ├─ Feed-forward networks
  ├─ Normalization
  └─ Residual connections
  ↓
Vocabulary logits
  ↓
Probability distribution
  ↓
Next token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During training, prediction errors are used to update the parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Training data
  ↓
Next-token prediction
  ↓
Cross-entropy loss
  ↓
Backpropagation
  ↓
Optimizer update
  ↓
Improved parameters
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During inference, those learned parameters are reused without normally being changed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current tokens + context + learned parameters
                         ↓
              next-token probability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tokens carry the information, context determines what the model can currently consider, and parameters define the learned transformations used to predict what comes next.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At &lt;strong&gt;Engineer Philosophy&lt;/strong&gt;, our objective is not simply to connect an interface to an LLM. We design the complete information flow around the model—controlling the tokens it receives, constructing useful context, selecting the appropriate model, and validating its output for the intended application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0ncxrckj0rf0zuy35b49.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0ncxrckj0rf0zuy35b49.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>engineerphilosophy</category>
      <category>agentskills</category>
      <category>programming</category>
    </item>
    <item>
      <title>RAG Explained Simply: How AI Finds the Right Information Before Answering</title>
      <dc:creator>Shivkrishna Shah</dc:creator>
      <pubDate>Sat, 18 Jul 2026 21:43:34 +0000</pubDate>
      <link>https://dev.to/shivkrishna-dev/rag-explained-simply-how-ai-finds-the-right-information-before-answering-271m</link>
      <guid>https://dev.to/shivkrishna-dev/rag-explained-simply-how-ai-finds-the-right-information-before-answering-271m</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F41pgm4bfa8dypbzsob2q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F41pgm4bfa8dypbzsob2q.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;Most AI demos look impressive until you ask a question about your own company.&lt;/p&gt;

&lt;p&gt;Ask an ordinary language model:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Which customers recently complained about product pricing?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The model may understand the question perfectly. But unless your customer feedback was included in its training data or supplied in the prompt, it does not know the answer.&lt;/p&gt;

&lt;p&gt;It may refuse.&lt;/p&gt;

&lt;p&gt;It may give a generic response.&lt;/p&gt;

&lt;p&gt;Worse, it may produce an answer that sounds convincing but is not supported by your data.&lt;/p&gt;

&lt;p&gt;This is the problem Retrieval-Augmented Generation—better known as &lt;strong&gt;RAG&lt;/strong&gt;—is designed to solve.&lt;/p&gt;

&lt;h2&gt;
  
  
  The simple idea behind RAG
&lt;/h2&gt;

&lt;p&gt;RAG gives an AI model access to relevant external information before asking it to answer.&lt;/p&gt;

&lt;p&gt;Instead of using this flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Question
   ↓
LLM
   ↓
Answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;RAG uses:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Question
   ↓
Retrieve relevant information
   ↓
Give that information to the LLM
   ↓
Generate a grounded answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The original RAG research combined a language model’s learned knowledge with external, non-parametric memory retrieved at runtime. This made it possible to update accessible knowledge without retraining the entire language model. citeturn146662search0&lt;/p&gt;

&lt;p&gt;The important word here is &lt;strong&gt;runtime&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;RAG does not automatically train the model on your database. It finds useful information when the user asks a question and temporarily places that information inside the model’s context window.&lt;/p&gt;
&lt;h2&gt;
  
  
  A practical example
&lt;/h2&gt;

&lt;p&gt;Imagine that your application stores these survey responses:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customer 101:
The product works well, but its price is too high.

Customer 205:
Availability is inconsistent in smaller cities.

Customer 318:
The product is affordable and easy to access.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The user asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Which customers are concerned about affordability?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A normal SQL keyword search may look for the exact word &lt;code&gt;affordability&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That could miss:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Its price is too high.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A semantic search system can recognize that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;affordability&lt;/li&gt;
&lt;li&gt;high price&lt;/li&gt;
&lt;li&gt;expensive&lt;/li&gt;
&lt;li&gt;cost concern&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;are related concepts.&lt;/p&gt;

&lt;p&gt;That is where embeddings and vector databases enter the system.&lt;/p&gt;
&lt;h1&gt;
  
  
  How the complete RAG pipeline works
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Step 1: Prepare your source data
&lt;/h2&gt;

&lt;p&gt;Your source data may come from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MySQL or PostgreSQL&lt;/li&gt;
&lt;li&gt;Realm&lt;/li&gt;
&lt;li&gt;PDFs&lt;/li&gt;
&lt;li&gt;Word documents&lt;/li&gt;
&lt;li&gt;CRM records&lt;/li&gt;
&lt;li&gt;support tickets&lt;/li&gt;
&lt;li&gt;survey answers&lt;/li&gt;
&lt;li&gt;product documentation&lt;/li&gt;
&lt;li&gt;internal policies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Structured database rows are usually converted into consistent text.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Account: ABC Pharmacy.
City: Indore.
Specialty: Retail pharmacy.
Survey response: Product pricing is too high for many patients.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You usually do not need an LLM to create this sentence. A deterministic template is cheaper, faster and more consistent.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2: Create an embedding
&lt;/h2&gt;

&lt;p&gt;An embedding model converts the text into a vector:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Text
  ↓
Embedding model
  ↓
[0.021, -0.184, 0.537, ...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The vector is a numerical representation of meaning.&lt;/p&gt;

&lt;p&gt;It is not encrypted text, and it cannot normally be decoded back into the original sentence.&lt;/p&gt;

&lt;p&gt;Similar meanings produce nearby vectors.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cardiologist
Heart specialist
Cardiac physician
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;should be positioned closer together than:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cardiologist
Dermatologist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 3: Store the searchable representation
&lt;/h2&gt;

&lt;p&gt;A vector database can store:&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FEEDBACK_184"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"document"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The product is effective, but its price is too high."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"metadata"&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;"survey_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SURVEY_10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"account_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ACC_101"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entity_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"survey_feedback"&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;"embedding"&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="mf"&gt;0.021&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-0.184&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.537&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The important parts are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ID:&lt;/strong&gt; connects the vector result to the source record&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;document:&lt;/strong&gt; readable text that may be returned during retrieval&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;metadata:&lt;/strong&gt; exact fields used for filtering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;embedding:&lt;/strong&gt; used for similarity search&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MySQL or Realm should normally remain the source of truth for changing business data. The vector database should act as a semantic index.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 4: Embed the user’s question
&lt;/h2&gt;

&lt;p&gt;The user asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Which customers are concerned about affordability?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The same embedding model converts this question into a vector.&lt;/p&gt;

&lt;p&gt;Using the same embedding model is important because the stored documents and the query must exist in the same vector space.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 5: Retrieve the nearest matches
&lt;/h2&gt;

&lt;p&gt;The vector database searches for records whose embeddings are closest to the query embedding.&lt;/p&gt;

&lt;p&gt;It may return:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. FEEDBACK_184 — pricing is too high
2. FEEDBACK_422 — customers cannot afford the product
3. FEEDBACK_091 — treatment cost is a concern
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is not ordinary keyword matching. It is meaning-based retrieval.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 6: Fetch current records when necessary
&lt;/h2&gt;

&lt;p&gt;For documentation and relatively static content, the document returned by the vector database may be enough.&lt;/p&gt;

&lt;p&gt;For live enterprise data, use the returned IDs to fetch current records from Realm or MySQL:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;account_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;updated_at&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;survey_answers&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;184&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;422&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;91&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This prevents an old vector index from becoming the final authority.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 7: Build the model context
&lt;/h2&gt;

&lt;p&gt;The application constructs a controlled prompt:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;System instruction:
Answer only from the supplied evidence.
Do not invent customers or conclusions.

User question:
Which customers are concerned about affordability?

Retrieved evidence:
1. Account 101 said the price is too high.
2. Account 205 said many patients cannot afford the product.
3. Account 318 said treatment cost is a concern.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 8: Generate a grounded answer
&lt;/h2&gt;

&lt;p&gt;The language model can now respond:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Three customers expressed affordability concerns. Their feedback focuses on high product prices, limited patient affordability and overall treatment cost.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The model did not independently know this.&lt;/p&gt;

&lt;p&gt;The system retrieved the evidence and placed it in the model’s temporary context.&lt;/p&gt;
&lt;h1&gt;
  
  
  RAG is not only a vector database
&lt;/h1&gt;

&lt;p&gt;A common mistake is to think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;RAG = embeddings + vector database.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is incomplete.&lt;/p&gt;

&lt;p&gt;A reliable RAG system also needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;document preparation&lt;/li&gt;
&lt;li&gt;chunking&lt;/li&gt;
&lt;li&gt;metadata design&lt;/li&gt;
&lt;li&gt;access control&lt;/li&gt;
&lt;li&gt;query understanding&lt;/li&gt;
&lt;li&gt;retrieval&lt;/li&gt;
&lt;li&gt;filtering&lt;/li&gt;
&lt;li&gt;reranking&lt;/li&gt;
&lt;li&gt;context construction&lt;/li&gt;
&lt;li&gt;answer generation&lt;/li&gt;
&lt;li&gt;citations&lt;/li&gt;
&lt;li&gt;evaluation&lt;/li&gt;
&lt;li&gt;monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The language model is only one component.&lt;/p&gt;
&lt;h1&gt;
  
  
  SQL search and vector search solve different problems
&lt;/h1&gt;

&lt;p&gt;Use SQL for exact and mathematical questions:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;How many surveys are pending?
What is the average call frequency?
Which city has the highest sales?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Use SQL or deterministic tools for those.&lt;/p&gt;

&lt;p&gt;Use vector search for semantic questions:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What complaints are related to product affordability?
Find doctors similar to this profile.
Which responses discuss supply problems?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The strongest enterprise architecture combines both:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User request
    ↓
Request router
    ├── Exact analytics → SQL / Realm
    ├── Semantic retrieval → Vector database
    ├── Business action → Trusted application tool
    └── Explanation → LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h1&gt;
  
  
  RAG does not eliminate hallucination
&lt;/h1&gt;

&lt;p&gt;RAG can improve factuality, but it does not guarantee truth.&lt;/p&gt;

&lt;p&gt;A RAG system can still fail when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the wrong documents are retrieved&lt;/li&gt;
&lt;li&gt;important records are missing&lt;/li&gt;
&lt;li&gt;chunks lose necessary context&lt;/li&gt;
&lt;li&gt;stale data remains indexed&lt;/li&gt;
&lt;li&gt;access filters are incorrect&lt;/li&gt;
&lt;li&gt;the prompt allows unsupported conclusions&lt;/li&gt;
&lt;li&gt;the model misinterprets good evidence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Broader RAG research describes retrieval as a way to improve the accuracy and credibility of knowledge-intensive generation while helping address outdated knowledge and weak provenance. It is still an engineering system that must be evaluated rather than blindly trusted. citeturn146662search8&lt;/p&gt;
&lt;h1&gt;
  
  
  What is the future of RAG?
&lt;/h1&gt;

&lt;p&gt;RAG is moving beyond the simple “retrieve once, then answer” pipeline.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Hybrid retrieval
&lt;/h2&gt;

&lt;p&gt;Future-ready systems combine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vector similarity&lt;/li&gt;
&lt;li&gt;keyword search&lt;/li&gt;
&lt;li&gt;SQL filters&lt;/li&gt;
&lt;li&gt;metadata filters&lt;/li&gt;
&lt;li&gt;graph relationships&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vector search finds meaning. Keyword search preserves exact names and terminology. Metadata applies strict business boundaries.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Reranking
&lt;/h2&gt;

&lt;p&gt;The first search may retrieve 20 candidates.&lt;/p&gt;

&lt;p&gt;A reranking model then evaluates those candidates more carefully and selects the strongest evidence for the final prompt.&lt;/p&gt;

&lt;p&gt;This improves retrieval quality without filling the context window with weak results.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Multimodal RAG
&lt;/h2&gt;

&lt;p&gt;Enterprise knowledge is not limited to text.&lt;/p&gt;

&lt;p&gt;RAG systems increasingly retrieve from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;images&lt;/li&gt;
&lt;li&gt;screenshots&lt;/li&gt;
&lt;li&gt;charts&lt;/li&gt;
&lt;li&gt;audio&lt;/li&gt;
&lt;li&gt;video&lt;/li&gt;
&lt;li&gt;scanned forms&lt;/li&gt;
&lt;li&gt;maps and spatial records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A user may eventually ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Compare the issue shown in this site photograph with last month’s inspection report.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The system will need to retrieve across multiple data types.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Graph-based retrieval
&lt;/h2&gt;

&lt;p&gt;Some questions depend on relationships rather than isolated documents:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customer
  ↓ belongs to
Territory
  ↓ managed by
Representative
  ↓ assigned to
Campaign
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Knowledge graphs and graph-aware retrieval can help answer multi-hop questions where information is distributed across related entities.&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Real-time and event-driven RAG
&lt;/h2&gt;

&lt;p&gt;Instead of rebuilding the entire vector index, production systems can update affected records when business data changes.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MySQL record updated
       ↓
Event published
       ↓
Text regenerated
       ↓
Embedding updated
       ↓
Vector record replaced
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is especially important for CRM, operational and mobile applications.&lt;/p&gt;
&lt;h2&gt;
  
  
  6. Agentic RAG
&lt;/h2&gt;

&lt;p&gt;Traditional RAG follows a fixed pipeline.&lt;/p&gt;

&lt;p&gt;Agentic RAG allows an agent to decide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;whether retrieval is needed&lt;/li&gt;
&lt;li&gt;which data source to search&lt;/li&gt;
&lt;li&gt;how to rewrite the query&lt;/li&gt;
&lt;li&gt;whether evidence is sufficient&lt;/li&gt;
&lt;li&gt;whether another search is required&lt;/li&gt;
&lt;li&gt;which tool should calculate the answer&lt;/li&gt;
&lt;li&gt;whether the response needs human approval&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recent Agentic RAG literature describes systems using planning, reflection, tool use and multi-agent collaboration to adapt retrieval for complex, multi-step work. citeturn146662search5turn146662search10&lt;/p&gt;

&lt;p&gt;A future workflow may look like:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User goal
   ↓
Agent creates a plan
   ↓
Search documentation
   ↓
Query SQL
   ↓
Check Realm data
   ↓
Compare evidence
   ↓
Retry weak retrieval
   ↓
Generate answer
   ↓
Request confirmation
   ↓
Execute an approved action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Research published in 2025 and 2026 is actively exploring multi-agent retrieval, hierarchical retrieval interfaces and more efficient agentic search. These approaches are promising, but they also introduce more latency, token consumption and operational complexity. citeturn146662search33turn146662search14turn146662academia56&lt;/p&gt;
&lt;h1&gt;
  
  
  A practical mobile architecture
&lt;/h1&gt;

&lt;p&gt;For an offline-first React Native application using Realm:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;React Native application
        ↓
Chat interface and voice input
        ↓
Chatbot orchestrator
        ├── Realm query tools
        ├── Validation action tools
        ├── What-if simulation tools
        └── Server RAG API
                ├── Embedding model
                ├── Vector database
                ├── MySQL
                └── LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Realm stores current offline application data.&lt;/p&gt;

&lt;p&gt;MySQL remains the server-side source of truth.&lt;/p&gt;

&lt;p&gt;The vector database provides semantic retrieval.&lt;/p&gt;

&lt;p&gt;The LLM interprets questions and explains verified data.&lt;/p&gt;

&lt;p&gt;Trusted application code performs business actions.&lt;/p&gt;
&lt;h1&gt;
  
  
  The key lesson
&lt;/h1&gt;

&lt;p&gt;RAG is not a magical database that makes an LLM intelligent.&lt;/p&gt;

&lt;p&gt;It is a controlled information pipeline.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The embedding model represents meaning.

The vector database retrieves related information.

MySQL and Realm preserve current business facts.

The LLM turns evidence into language.

Application tools enforce business rules.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The future is not an AI model replacing every database.&lt;/p&gt;

&lt;p&gt;The future is an AI system that knows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which database to use&lt;/li&gt;
&lt;li&gt;what information to retrieve&lt;/li&gt;
&lt;li&gt;how to verify it&lt;/li&gt;
&lt;li&gt;when to ask for approval&lt;/li&gt;
&lt;li&gt;how to act safely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is where RAG becomes more than a chatbot feature.&lt;/p&gt;

&lt;p&gt;It becomes the knowledge layer behind an enterprise AI assistant.&lt;/p&gt;

&lt;p&gt;At Engineer Philosophy Web Services Pvt. Ltd., we are exploring how RAG, Agentic AI, mobile applications, offline databases, and enterprise systems can work together to create AI assistants that do more than answer questions.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://engineerphilosophy.com/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;engineerphilosophy.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;h1&gt;
  
  
  AgenticAI #ArtificialIntelligence #AIAgents #GenerativeAI #EnterpriseAI #Automation #Innovation #DigitalTransformation #EngineerPhilosophy #MAPOG #shivkrishnashah
&lt;/h1&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>llm</category>
      <category>mcp</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Shivkrishna Shah</dc:creator>
      <pubDate>Thu, 09 Jul 2026 11:50:09 +0000</pubDate>
      <link>https://dev.to/shivkrishna-dev/-hfp</link>
      <guid>https://dev.to/shivkrishna-dev/-hfp</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/shivkrishna-dev/beyond-chatbots-building-an-agentic-ai-assistant-for-react-native-10mo" class="crayons-story__hidden-navigation-link"&gt;Building an Enterprise AI Assistant That Can Actually Take Action&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/shivkrishna-dev" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4022229%2F1a77a60a-879f-4fd3-b190-35c574a5bdd8.jpg" alt="shivkrishna-dev profile" class="crayons-avatar__image" width="800" height="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/shivkrishna-dev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Shivkrishna Shah
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Shivkrishna Shah
                
              
              &lt;div id="story-author-preview-content-4102463" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/shivkrishna-dev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4022229%2F1a77a60a-879f-4fd3-b190-35c574a5bdd8.jpg" class="crayons-avatar__image" alt="" width="800" height="800"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Shivkrishna Shah&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/shivkrishna-dev/beyond-chatbots-building-an-agentic-ai-assistant-for-react-native-10mo" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jul 9&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/shivkrishna-dev/beyond-chatbots-building-an-agentic-ai-assistant-for-react-native-10mo" id="article-link-4102463"&gt;
          Building an Enterprise AI Assistant That Can Actually Take Action
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mobile"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mobile&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/reactnative"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;reactnative&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/shivkrishna-dev/beyond-chatbots-building-an-agentic-ai-assistant-for-react-native-10mo" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/shivkrishna-dev/beyond-chatbots-building-an-agentic-ai-assistant-for-react-native-10mo#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

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

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Building an Enterprise AI Assistant That Can Actually Take Action</title>
      <dc:creator>Shivkrishna Shah</dc:creator>
      <pubDate>Thu, 09 Jul 2026 06:30:35 +0000</pubDate>
      <link>https://dev.to/shivkrishna-dev/beyond-chatbots-building-an-agentic-ai-assistant-for-react-native-10mo</link>
      <guid>https://dev.to/shivkrishna-dev/beyond-chatbots-building-an-agentic-ai-assistant-for-react-native-10mo</guid>
      <description>&lt;p&gt;Imagine you're a field representative visiting customers all day.&lt;/p&gt;

&lt;p&gt;It's 6:30 PM.&lt;/p&gt;

&lt;p&gt;You still have 40 survey responses to complete before heading home.&lt;/p&gt;

&lt;p&gt;Instead of navigating through multiple screens, wouldn't it be easier if you could simply say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Mark Question 3 as Yes for Northside Clinic and tell me what's left before I submit."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most AI assistants can't do this.&lt;/p&gt;

&lt;p&gt;They can answer questions.&lt;/p&gt;

&lt;p&gt;They can summarize information.&lt;/p&gt;

&lt;p&gt;They can explain how to complete a task.&lt;/p&gt;

&lt;p&gt;But they &lt;strong&gt;can't actually perform the task&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's the difference between a chatbot and an &lt;strong&gt;Agentic AI system&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Is Agentic AI?
&lt;/h1&gt;

&lt;p&gt;In the context of mobile applications, Agentic AI means giving an LLM the ability to reason, plan, and safely perform actions inside your application.&lt;/p&gt;

&lt;p&gt;Instead of telling users where to click, the assistant actually performs the required operations using your application's tools.&lt;/p&gt;

&lt;p&gt;An agent can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search application data&lt;/li&gt;
&lt;li&gt;Read local state&lt;/li&gt;
&lt;li&gt;Validate information&lt;/li&gt;
&lt;li&gt;Update records&lt;/li&gt;
&lt;li&gt;Execute business workflows&lt;/li&gt;
&lt;li&gt;Ask follow-up questions when information is missing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of becoming another search box, AI becomes another user of your application.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why Most Mobile AI Projects Fail
&lt;/h1&gt;

&lt;p&gt;After building and experimenting with production-grade AI assistants, I noticed two common mistakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistake #1 — AI Wrapped Around If-Else Statements
&lt;/h2&gt;

&lt;p&gt;Many applications still rely on intent matching.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;survey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)){&lt;/span&gt;
   &lt;span class="nf"&gt;openSurvey&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;An LLM simply generates the response.&lt;/p&gt;

&lt;p&gt;The routing is still deterministic.&lt;/p&gt;

&lt;p&gt;This works during demos.&lt;/p&gt;

&lt;p&gt;It usually breaks in production.&lt;/p&gt;

&lt;p&gt;Users never ask questions exactly the way developers expect.&lt;/p&gt;


&lt;h2&gt;
  
  
  Mistake #2 — Giving the AI Unlimited Access
&lt;/h2&gt;

&lt;p&gt;The opposite mistake is allowing the model to directly modify application data.&lt;/p&gt;

&lt;p&gt;Without proper validation, an AI can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update the wrong record&lt;/li&gt;
&lt;li&gt;Modify unintended data&lt;/li&gt;
&lt;li&gt;Hallucinate IDs&lt;/li&gt;
&lt;li&gt;Save information the user never approved&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither approach belongs in a production application.&lt;/p&gt;


&lt;h1&gt;
  
  
  The Architecture That Worked
&lt;/h1&gt;

&lt;p&gt;After several iterations, I settled on one simple principle.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The model makes decisions. The application guarantees safety.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The AI should handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reasoning&lt;/li&gt;
&lt;li&gt;Planning&lt;/li&gt;
&lt;li&gt;Selecting tools&lt;/li&gt;
&lt;li&gt;Recovering from failures&lt;/li&gt;
&lt;li&gt;Asking follow-up questions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your application should handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validation&lt;/li&gt;
&lt;li&gt;Permissions&lt;/li&gt;
&lt;li&gt;Confirmations&lt;/li&gt;
&lt;li&gt;Database writes&lt;/li&gt;
&lt;li&gt;Privacy&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Execution limits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once those responsibilities are separated, the architecture becomes both flexible and reliable.&lt;/p&gt;

&lt;p&gt;*&lt;br&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn8a555bkoq6c66gder7a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn8a555bkoq6c66gder7a.png" alt=" " width="799" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;*&lt;/p&gt;


&lt;h1&gt;
  
  
  The Agent Loop
&lt;/h1&gt;

&lt;p&gt;Everything begins with one simple loop.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receive the user's message&lt;/li&gt;
&lt;li&gt;Send conversation context to the LLM&lt;/li&gt;
&lt;li&gt;Receive requested tool calls&lt;/li&gt;
&lt;li&gt;Execute tools locally&lt;/li&gt;
&lt;li&gt;Return tool results&lt;/li&gt;
&lt;li&gt;Continue until the task is complete&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it.&lt;/p&gt;

&lt;p&gt;No keyword routing.&lt;/p&gt;

&lt;p&gt;No intent classifier.&lt;/p&gt;

&lt;p&gt;No hardcoded workflows.&lt;/p&gt;

&lt;p&gt;The model decides what happens next.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Insert simplified agent loop diagram here.)&lt;/em&gt;&lt;/p&gt;


&lt;h1&gt;
  
  
  Skills Instead of Massive Prompts
&lt;/h1&gt;

&lt;p&gt;One of the biggest improvements was replacing one enormous system prompt with small reusable skills.&lt;/p&gt;

&lt;p&gt;Each skill contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instructions&lt;/li&gt;
&lt;li&gt;Available tools&lt;/li&gt;
&lt;li&gt;Validation logic&lt;/li&gt;
&lt;li&gt;Optional hooks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Survey Skill

Available Tools

• Find Survey
• Update Answer
• Save Draft
• Submit Survey
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Adding a new capability became as simple as adding another folder.&lt;/p&gt;

&lt;p&gt;The engine never changes.&lt;/p&gt;


&lt;h1&gt;
  
  
  Confirmation Before Every Write
&lt;/h1&gt;

&lt;p&gt;One lesson became obvious very quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never allow an AI model to write directly into your database.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every write follows the same process.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Stage Changes
      ↓
Show Confirmation
      ↓
 User Approves
      ↓
   Validate
      ↓
     Save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Even if the AI attempts to skip confirmation, the application blocks it.&lt;/p&gt;

&lt;p&gt;The runtime—not the model—controls safety.&lt;/p&gt;


&lt;h1&gt;
  
  
  Let the Model Recover
&lt;/h1&gt;

&lt;p&gt;One surprising discovery was that AI becomes significantly smarter when you return errors instead of hiding them.&lt;/p&gt;

&lt;p&gt;Instead of throwing an exception:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Account not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Return:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Northside Clinic

2 matches found.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now the AI naturally responds:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Did you mean Northside East or Northside West?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This simple design choice creates much more natural conversations.&lt;/p&gt;


&lt;h1&gt;
  
  
  Privacy Matters
&lt;/h1&gt;

&lt;p&gt;Enterprise applications often contain sensitive business information.&lt;/p&gt;

&lt;p&gt;One rule dramatically improved both privacy and cost.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Raw database rows never enter the prompt.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead, tools only return:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IDs&lt;/li&gt;
&lt;li&gt;Labels&lt;/li&gt;
&lt;li&gt;Counts&lt;/li&gt;
&lt;li&gt;Status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Detailed records remain inside the application.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Better privacy&lt;/li&gt;
&lt;li&gt;Lower token usage&lt;/li&gt;
&lt;li&gt;Faster responses&lt;/li&gt;
&lt;li&gt;Smaller prompts&lt;/li&gt;
&lt;/ul&gt;


&lt;h1&gt;
  
  
  Mobile Applications Have Different Challenges
&lt;/h1&gt;

&lt;p&gt;Building Agentic AI on mobile introduces challenges that most tutorials ignore.&lt;/p&gt;

&lt;p&gt;You must think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Offline mode&lt;/li&gt;
&lt;li&gt;Background synchronization&lt;/li&gt;
&lt;li&gt;Battery consumption&lt;/li&gt;
&lt;li&gt;Poor network conditions&lt;/li&gt;
&lt;li&gt;API rate limits&lt;/li&gt;
&lt;li&gt;Provider failover&lt;/li&gt;
&lt;li&gt;Secure API key management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ignoring these realities usually produces assistants that work during demos but fail in production.&lt;/p&gt;


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

&lt;p&gt;If I were building this architecture again, I'd follow these principles from day one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Let the model handle reasoning.&lt;/li&gt;
&lt;li&gt;Keep deterministic logic only for safety.&lt;/li&gt;
&lt;li&gt;Build capabilities as independent skills.&lt;/li&gt;
&lt;li&gt;Never bypass confirmation for write operations.&lt;/li&gt;
&lt;li&gt;Return errors to the model instead of crashing.&lt;/li&gt;
&lt;li&gt;Treat offline mode as a first-class feature.&lt;/li&gt;
&lt;li&gt;Reuse existing business logic whenever possible.&lt;/li&gt;
&lt;/ul&gt;


&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Agentic AI isn't about replacing your application's business logic.&lt;/p&gt;

&lt;p&gt;It's about giving users a completely new way to interact with it.&lt;/p&gt;

&lt;p&gt;The model plans.&lt;/p&gt;

&lt;p&gt;Your tools execute.&lt;/p&gt;

&lt;p&gt;Your application guarantees safety.&lt;/p&gt;

&lt;p&gt;When those responsibilities are clearly separated, AI becomes much more than a chatbot.&lt;/p&gt;

&lt;p&gt;It becomes a reliable teammate that helps users complete real work.&lt;/p&gt;



&lt;p&gt;If you're building Agentic AI for React Native, enterprise applications, or mobile platforms, I'd love to hear about your architecture and the lessons you've learned along the way.&lt;/p&gt;

&lt;p&gt;Let's connect and continue the conversation.&lt;/p&gt;


&lt;h2&gt;
  
  
  Tags
&lt;/h2&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://engineerphilosophy.com/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;engineerphilosophy.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
React Native • Agentic AI • AI Engineering • Mobile Development • LLM • Enterprise Software • Software Architecture • Engineer Philosophy&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>mobile</category>
      <category>reactnative</category>
    </item>
  </channel>
</rss>
