<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Abdullah Tayyab Web Dev]]></title><description><![CDATA[Practical notes on software development, architecture, reliability, and real-world projects, with lessons from building and operating software systems.]]></description><link>https://abdullahtayyab.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6aa53883e1ef31abc8cb809f/de11b8b0-8d43-43bb-9b08-2d877932c9a8.png</url><title>Abdullah Tayyab Web Dev</title><link>https://abdullahtayyab.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 25 Sep 2026 07:16:34 GMT</lastBuildDate><atom:link href="https://abdullahtayyab.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Offline-First Is Not a Feature]]></title><description><![CDATA[Most applications are built with one assumption:
The internet is available.
The UI loads data from an API. The user submits a form. The server saves it. The application moves on.
That works well when ]]></description><link>https://abdullahtayyab.hashnode.dev/offline-first-is-not-a-feature</link><guid isPermaLink="true">https://abdullahtayyab.hashnode.dev/offline-first-is-not-a-feature</guid><category><![CDATA[offline first]]></category><category><![CDATA[offline-first-architecture]]></category><category><![CDATA[abdullahtayyab]]></category><category><![CDATA[ellifedash]]></category><dc:creator><![CDATA[Abdullah Tayyab]]></dc:creator><pubDate>Fri, 18 Sep 2026 10:02:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6aa53883e1ef31abc8cb809f/7afb4684-43c1-4ed4-9fac-17bcbade3966.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most applications are built with one assumption:</p>
<p><strong>The internet is available.</strong></p>
<p>The UI loads data from an API. The user submits a form. The server saves it. The application moves on.</p>
<p>That works well when connectivity is reliable.</p>
<p>But what happens when it isn't?</p>
<p>A clinic loses its connection. A field worker has no signal. A power outage takes the network down. A small business is working from an area with inconsistent connectivity.</p>
<p>The user's work still needs to continue.</p>
<p>That is where offline-first architecture becomes important.</p>
<h2>Offline-first starts before the first route or component</h2>
<p>I don't think of offline-first as a feature that gets added after an application is already built.</p>
<p>It is an architectural decision.</p>
<p>The difference matters because offline-first changes how you think about:</p>
<ul>
<li><p>Data storage</p>
</li>
<li><p>Application state</p>
</li>
<li><p>Reads and writes</p>
</li>
<li><p>Synchronization</p>
</li>
<li><p>Failed requests</p>
</li>
<li><p>Conflicts</p>
</li>
<li><p>Deletes</p>
</li>
<li><p>Network availability</p>
</li>
</ul>
<p>If the application is designed around a remote API from the beginning, adding offline support later can become a significant architectural change.</p>
<p>With offline-first, the local data store becomes part of the application's normal operation.</p>
<p>The application should be able to do its core job whether the network is available or not.</p>
<h2>Offline mode vs offline-first</h2>
<p>These two ideas are often used interchangeably, but they are different.</p>
<h3>Offline mode</h3>
<p>Offline mode usually means the application has an online-first architecture with a fallback.</p>
<p>The application detects that the connection is gone and might:</p>
<ul>
<li><p>Show cached data</p>
</li>
<li><p>Disable certain features</p>
</li>
<li><p>Display an offline message</p>
</li>
<li><p>Wait for the connection to return</p>
</li>
</ul>
<p>The network is still the default assumption.</p>
<h3>Offline-first</h3>
<p>With offline-first, local storage is part of the normal architecture.</p>
<p>The application reads and writes locally first.</p>
<p>Synchronization with the server happens separately, usually in the background.</p>
<p>The basic flow becomes:</p>
<p><strong>User → Local database → UI</strong></p>
<p>and then:</p>
<p><strong>Local database → Sync queue → Server</strong></p>
<p>The important difference is that the user's core action does not have to wait for the network.</p>
<h2>How I'm using this in Aafiyat</h2>
<p>Aafiyat is a clinic management system I'm building for private practices.</p>
<p>One of the questions I asked while designing it was simple:</p>
<p><strong>What happens if the clinic loses its internet connection in the middle of the working day?</strong></p>
<p>A doctor shouldn't have to stop because the cloud is temporarily unavailable.</p>
<p>Aafiyat is built as an Electron application with SQLite as the local database.</p>
<p>Patient records, consultations, prescriptions and billing data are written locally first.</p>
<p>The application does not make a network request part of the critical save path.</p>
<p>The basic flow is:</p>
<ol>
<li><p>Generate a UUID for the record.</p>
</li>
<li><p>Open the local database.</p>
</li>
<li><p>Insert the record into SQLite.</p>
</li>
<li><p>Notify the application that the local change happened.</p>
</li>
<li><p>Add a synchronization job to the sync queue.</p>
</li>
<li><p>Synchronize with Supabase when connectivity is available.</p>
</li>
</ol>
<p>The important part is step three.</p>
<p><strong>The user's data is saved before we worry about the network.</strong></p>
<p>The UI can confirm the action immediately instead of waiting for an API response.</p>
<h2>The sync queue</h2>
<p>Saving locally is only half of the problem.</p>
<p>Eventually, the local data needs to reach the server.</p>
<p>That's where the synchronization queue comes in.</p>
<p>Aafiyat keeps pending synchronization jobs in a SQLite <code>sync_queue</code>.</p>
<p>A simplified flow looks like this:</p>
<pre><code class="language-text">User creates record
        ↓
SQLite
        ↓
sync_queue
        ↓
Internet available?
        ↓
   ┌────┴────┐
   │         │
  Yes        No
   │         │
Sync      Stay queued
   │
Supabase
</code></pre>
<p>If synchronization fails, the item stays in the queue and can be retried later.</p>
<p>This is one of the reasons offline-first applications require more planning than ordinary CRUD applications.</p>
<p>You aren't only building:</p>
<pre><code class="language-text">Create → API → Database
</code></pre>
<p>You're building something closer to:</p>
<pre><code class="language-text">Create
  ↓
Local storage
  ↓
Queue
  ↓
Retry
  ↓
Synchronize
  ↓
Resolve conflicts
</code></pre>
<h2>The Nankana Home Care PWA</h2>
<p>I'm using a similar idea in the Nankana Home Care project.</p>
<p>The platform includes an offline-first PWA for medical assistants who may need to record patient information without an internet connection.</p>
<p>The local data is stored using IndexedDB.</p>
<p>When connectivity becomes available, a synchronization worker processes the queued information and sends it to Supabase.</p>
<p>For example, patient data can be queued locally and synchronized later.</p>
<p>If a request fails, it isn't simply discarded.</p>
<p>The item remains queued so the application has another opportunity to synchronize it.</p>
<p>The architecture is different from Aafiyat because the technology is different, but the underlying idea is the same:</p>
<p><strong>Local work first. Cloud synchronization second.</strong></p>
<h2>Synchronization is the hard part</h2>
<p>This is where offline-first becomes much more interesting.</p>
<p>What happens when the same record changes in two places?</p>
<p>For example:</p>
<pre><code class="language-text">Local device
Patient name → "Muhammad Ali"

        ↓

Server
Patient name → "Muhammad A."
</code></pre>
<p>Which version should win?</p>
<p>There isn't one universal answer.</p>
<p>Possible strategies include:</p>
<ul>
<li><p>Last-write-wins</p>
</li>
<li><p>Server-wins</p>
</li>
<li><p>Client-wins</p>
</li>
<li><p>Manual merging</p>
</li>
<li><p>Domain-specific conflict resolution</p>
</li>
</ul>
<p>For Aafiyat, I'm using a last-write-wins approach based on timestamps.</p>
<p>The most recent <code>updated_at</code> value determines which version is accepted.</p>
<p>That's a deliberate trade-off based on the workflow I'm building for.</p>
<p>The important thing isn't that one strategy is always correct.</p>
<p>The important thing is to <strong>decide what should happen before conflicts occur in production.</strong></p>
<h2>Deletes are also harder</h2>
<p>Updates aren't the only problem.</p>
<p>Consider a record deleted while the device is offline.</p>
<p>The application needs to remember that deletion and eventually communicate it to the server.</p>
<p>Otherwise, synchronization could accidentally recreate the record.</p>
<p>Offline-first means thinking about the entire lifecycle of data:</p>
<pre><code class="language-text">Create
Read
Update
Delete
Sync
Retry
Conflict
</code></pre>
<p>Not just the happy path.</p>
<h2>Do you actually need offline-first?</h2>
<p>No.</p>
<p>Not every application needs this architecture.</p>
<p>A simple marketing website probably doesn't need a local database and synchronization queue.</p>
<p>A basic landing page doesn't need to continue operating without connectivity.</p>
<p>But the situation changes when the user's core job depends on the application.</p>
<p>Think about:</p>
<ul>
<li><p>Clinics</p>
</li>
<li><p>Pharmacies</p>
</li>
<li><p>Field workers</p>
</li>
<li><p>POS systems</p>
</li>
<li><p>Local businesses</p>
</li>
<li><p>Applications used in areas with unreliable connectivity</p>
</li>
</ul>
<p>In these environments, connectivity can become an operational constraint rather than a minor inconvenience.</p>
<p>That's when offline-first starts making sense.</p>
<h2>The trade-off</h2>
<p>Offline-first isn't free.</p>
<p>It adds complexity.</p>
<p>You now have to think about:</p>
<ul>
<li><p>Local storage</p>
</li>
<li><p>Two sources of data</p>
</li>
<li><p>Synchronization</p>
</li>
<li><p>Retry mechanisms</p>
</li>
<li><p>Conflicts</p>
</li>
<li><p>Storage limits</p>
</li>
<li><p>Deletes</p>
</li>
<li><p>Background processing</p>
</li>
</ul>
<p>The initial architecture takes longer.</p>
<p>But the benefit is that the application doesn't have to stop doing its core job every time the network disappears.</p>
<p>For the right environment, that trade-off is worth considering.</p>
<h2>Build for the environment your users actually have</h2>
<p>This is probably the biggest lesson I've taken from building these systems.</p>
<p>It's easy to design software around an ideal environment:</p>
<p>Fast internet. Modern hardware. Stable power. Constant connectivity.</p>
<p>Real users don't always have that environment.</p>
<p>In the applications I'm building, I want the architecture to reflect that reality.</p>
<p>The internet is useful.</p>
<p>The cloud is useful.</p>
<p>But neither should automatically become a requirement for every core action.</p>
<p><strong>Offline-first isn't a feature you add at the end.</strong></p>
<p>It's a decision you make when you design the application.</p>
<p>And sometimes, that decision determines whether the software keeps working when everything around it doesn't.</p>
]]></content:encoded></item></channel></rss>