<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged kotlinmultiplatform</title>
<link>https://coderlegion.com/tag/kotlinmultiplatform</link>
<description>Powered by Question2Answer</description>
<item>
<title>Offline-first with no server: why HelperBook is local-only</title>
<link>https://coderlegion.com/29954/offline-first-with-no-server-why-helperbook-is-local-only</link>
<description>&lt;hr&gt;
&lt;p&gt;title: Offline-first with no server: why HelperBook is local-only&lt;br&gt;
published: true&lt;br&gt;
date: 2026-09-24 00:00:00 UTC&lt;br&gt;
tags: offlinefirst,kotlinmultiplatform,privacy,android&lt;/p&gt;
&lt;h3 id=&quot;canonical-url-https-abhishekgoyal-me-writing-offline-first-without-a-server&quot;&gt;canonical_url: &lt;a rel=&quot;nofollow&quot; href=&quot;https://abhishekgoyal.me/writing/offline-first-without-a-server&quot;&gt;https://abhishekgoyal.me/writing/offline-first-without-a-server&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A lot of my work has been on offline-first apps that still, eventually, talk to a server. At Pulse, I designed the sync contract with the backend and web teams: payloads, upload semantics, failure behaviour. The app worked without signal, but it was always working towards a moment when it would have signal again and reconcile.&lt;/p&gt;
&lt;p&gt;HelperBook, the app I’m building now for Indian households to track staff attendance, advances and salary settlements, doesn’t have that moment. There is no signup and no server. Records live on the device, full stop. I want to explain why that’s a design decision and not a missing feature, and what it costs.&lt;/p&gt;
&lt;h3 id=&quot;the-data-is-the-reason-not-to-have-a-server&quot;&gt;The data is the reason not to have a server&lt;/h3&gt;
&lt;p&gt;HelperBook stores wage data: how many days someone worked, what they were advanced, what they’re owed at month end. That’s exactly the kind of data a household might hesitate to put into an app if it thought the numbers were going somewhere.&lt;/p&gt;
&lt;p&gt;Keeping wage data off any server keeps the app outside data-fiduciary scope under India’s DPDP Act, and it gives households a reason to enter real numbers. Those two things are connected. An app that can’t leak your payroll data because it never has it in the first place is a different trust proposition than one that promises to look after your payroll data on a server somewhere.&lt;/p&gt;
&lt;h3 id=&quot;no-sync-engine-to-design&quot;&gt;No sync engine to design&lt;/h3&gt;
&lt;p&gt;The technical consequence is that there’s no sync contract to design, no conflict resolution, no retry queue, no “what happens if the upload fails halfway through” states. Records live on-device via SQLDelight, inside a shared Kotlin Multiplatform module. Users own their data through export and restore.&lt;/p&gt;
&lt;p&gt;That’s a smaller surface area than Pulse’s sync engine, and it’s smaller on purpose. Every state a sync engine has to handle — offline, online, mid-sync, conflicting edits from two devices — is a state HelperBook doesn’t have. The trade-off is direct: no server means no multi-device. If a household member wants to check the ledger from a second phone, they can’t. And if the phone is lost or wiped without an export first, the data is gone. There’s no account to recover it from. That’s a real cost, and it’s one I chose deliberately rather than one I didn’t notice.&lt;/p&gt;
&lt;h3 id=&quot;building-the-honesty-into-the-data-model-not-the-ui&quot;&gt;Building the honesty into the data model, not the UI&lt;/h3&gt;
&lt;p&gt;A local-only app has one more responsibility than a server-backed one: the rules have to be right, because there’s no admin panel on the backend to fix a bad month for someone later. So the product rules that matter most live in the data model rather than as UI copy or a setting someone can miss.&lt;/p&gt;
&lt;p&gt;Unmarked days default to present, so a worker is never underpaid because someone forgot to tap. Half-days deduct exactly half, with no rounding surprises at month end. And settlement statements are generated bilingually, in English and Hindi, so the employer and the helper can both read every line without one of them having to trust the other’s translation.&lt;/p&gt;
&lt;p&gt;As a simplified illustration, the “unmarked day” rule looks something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;// Simplified illustration of the rule, not HelperBook's actual code.
fun attendanceValue(mark: DayMark?): Double = when (mark) {
    DayMark.ABSENT -&amp;gt; 0.0
    DayMark.HALF_DAY -&amp;gt; 0.5
    DayMark.PRESENT, null -&amp;gt; 1.0
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The interesting part isn’t the code, it’s the default: a missing mark resolves to &lt;code&gt;1.0&lt;/code&gt;, not &lt;code&gt;0.0&lt;/code&gt;. In a local-only app, that decision is the whole safety net. There’s no support ticket, no server-side correction, no second system checking the math. Whatever the function returns is what the worker gets paid.&lt;/p&gt;
&lt;h3 id=&quot;release-engineering-without-a-backend-to-hide-behind&quot;&gt;Release engineering without a backend to hide behind&lt;/h3&gt;
&lt;p&gt;Going local-only doesn’t remove the need for release discipline, it just moves where it matters. HelperBook ships as a signed AAB with R8 rules written for SQLDelight and kotlinx-serialization, so obfuscation doesn’t break the database layer or the serialised export format. Crashlytics is wired in, but the logs carry no names or amounts. Play’s Data Safety declarations describe what the app actually does. And there’s a staged-rollout plan, so a wider release doesn’t reach everyone at once.&lt;/p&gt;
&lt;p&gt;None of that is specific to offline-first apps. But when there’s no backend team and no server logs to lean on for debugging, the discipline around what you ship and how you roll it out is the only safety net you have. It’s currently in closed beta on Google Play with 12–20 households, built with Jetpack Compose, min SDK 24, target API 35.&lt;/p&gt;
&lt;h3 id=&quot;what-i-d-tell-another-builder&quot;&gt;What I’d tell another builder&lt;/h3&gt;
&lt;p&gt;If you’re weighing whether a product needs a backend at all, the question I’d ask first isn’t “can we build the sync engine,” it’s “what does the user lose if there’s no sync engine.” For HelperBook, the loss is multi-device access and recovery-without-export, and I judged that a household tracking one home’s payroll would rather have neither of those than have their wage data sitting on a server. For a different product, that trade might go the other way.&lt;/p&gt;
&lt;p&gt;The second thing I’d say: local-only doesn’t mean less engineering. It means the engineering moves into the data model and the release pipeline instead of into a sync contract. You still have to get the rules right, you still have to obfuscate and log carefully, you still have to plan the rollout in stages. You just don’t get to blame a server for anything.&lt;/p&gt;
&lt;p&gt;More on how HelperBook is built is in the &lt;a rel=&quot;nofollow&quot; href=&quot;https://abhishekgoyal.me/work/helperbook&quot;&gt;case study&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29954/offline-first-with-no-server-why-helperbook-is-local-only</guid>
<pubDate>Tue, 06 Oct 2026 08:24:11 +0000</pubDate>
</item>
<item>
<title>Native, Kotlin Multiplatform or React Native: how I'd choose</title>
<link>https://coderlegion.com/29953/native-kotlin-multiplatform-or-react-native-how-id-choose</link>
<description>&lt;hr&gt;
&lt;p&gt;title: Native, Kotlin Multiplatform or React Native: how I'd choose&lt;br&gt;
published: true&lt;br&gt;
date: 2026-09-25 00:00:00 UTC&lt;br&gt;
tags: kotlinmultiplatform,reactnative,android,ios&lt;/p&gt;
&lt;h3 id=&quot;canonical-url-https-abhishekgoyal-me-writing-native-kmp-or-react-native&quot;&gt;canonical_url: &lt;a rel=&quot;nofollow&quot; href=&quot;https://abhishekgoyal.me/writing/native-kmp-or-react-native&quot;&gt;https://abhishekgoyal.me/writing/native-kmp-or-react-native&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;“Should we build this in Kotlin Multiplatform or React Native?” is a question teams often ask before anyone has written down what the app has to do. That’s backwards. I’ve shipped all three approaches to production: native Android and iOS apps, a shared Kotlin Multiplatform data layer in HelperBook, and a React Native rewrite of two native apps at Pulse. The framework has never been the first decision. The features are.&lt;/p&gt;
&lt;h3 id=&quot;start-with-the-features-not-the-framework&quot;&gt;Start with the features, not the framework&lt;/h3&gt;
&lt;p&gt;Before choosing anything, I list what the app actually has to do, and which of those things lean on the platform. Camera capture, video playback, background work, offline data, sensors, OS integrations. Then I weigh each option against that list, along with the team that will maintain it and the time and money available.&lt;/p&gt;
&lt;p&gt;Most of the heat in “Kotlin Multiplatform vs React Native” debates comes from people comparing the tools in the abstract. For a specific app, the answer is usually much clearer than the debate suggests.&lt;/p&gt;
&lt;h3 id=&quot;when-i-d-stay-fully-native&quot;&gt;When I’d stay fully native&lt;/h3&gt;
&lt;p&gt;Sharing code is not free, so sometimes the right amount to share is none.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The market is on one platform.&lt;/strong&gt; HelperBook is for Indian households, where Android is the phone most people carry, so it shipped on Android first. Its records still live in a shared Kotlin Multiplatform module with SQLDelight, so an iOS app could reuse that data layer rather than rewrite it, but the app itself is native Android.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The app leans hard on platform APIs.&lt;/strong&gt; On a video-first hiring platform I built 100% of the Android client, and the core of it was CameraX capture, Media3 playback and on-device speech transcription. That is exactly the kind of work where you want the platform’s own APIs directly, not through a layer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The two apps look and behave differently.&lt;/strong&gt; If the Android and iOS versions don’t share much UI or flow, a shared UI codebase saves less than it costs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;when-kotlin-multiplatform-fits&quot;&gt;When Kotlin Multiplatform fits&lt;/h3&gt;
&lt;p&gt;Kotlin Multiplatform shares business logic and data while each platform keeps a fully native UI. I reach for it when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Native apps already exist.&lt;/strong&gt; You can add a shared module to the apps you have and move code into it gradually, instead of rewriting both.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The team writes Kotlin.&lt;/strong&gt; Android developers can own the shared code without learning a new stack.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The business logic is complex and must match on both platforms.&lt;/strong&gt; Rules, calculations and offline data that behave slightly differently on Android and iOS are a bug factory. Writing them once removes a whole class of problems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Native feel matters.&lt;/strong&gt; The UI stays Jetpack Compose on Android and SwiftUI or UIKit on iOS.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;when-react-native-fits&quot;&gt;When React Native fits&lt;/h3&gt;
&lt;p&gt;React Native gives you one codebase for the UI as well as the logic. I recommend it when the timeline is short and the budget is small, and one team has to ship both platforms quickly. It also makes more sense when the screens are the same on both platforms, and when there’s already a React web team whose skills carry over.&lt;/p&gt;
&lt;h3 id=&quot;performance-honestly&quot;&gt;Performance, honestly&lt;/h3&gt;
&lt;p&gt;People search for “Kotlin Multiplatform vs React Native performance” a lot, so here’s my view. Kotlin Multiplatform keeps the UI fully native, and native UI is smoother. For most business apps (forms, lists, dashboards) React Native is fine, and the difference users feel comes far more from the architecture than from the framework.&lt;/p&gt;
&lt;p&gt;I haven’t shipped a Flutter app, so I won’t argue for or against it from experience. It’s a real option; it’s just not one I can speak to first-hand.&lt;/p&gt;
&lt;h3 id=&quot;what-pulse-taught-me&quot;&gt;What Pulse taught me&lt;/h3&gt;
&lt;p&gt;At Pulse I owned mobile, Android and iOS, from an early prototype to 100+ B2B enterprise clients. For years it ran as two native apps, Kotlin on Android and Swift on iOS, kept at feature parity. Keeping two native apps at parity meant building every feature twice.&lt;/p&gt;
&lt;p&gt;In 2025 I led the rewrite of both apps into one React Native codebase, architected and co-built with the team in six months, and it replaced both native apps in production. React Native fitted the situation: one codebase suited the timeline and budget, the web app was already built in React, and the screens were the same on both platforms.&lt;/p&gt;
&lt;p&gt;The hardest part of the port was reliable background uploads, which the offline engine depended on. The inspections could run to 500+ questions and 1,000+ photos, captured offline and uploaded in the background once a connection came back, so the upload behaviour had to be as dependable after the rewrite as before it.&lt;/p&gt;
&lt;p&gt;Looking back, React Native was a reasonable call for that timeline, budget and team. Knowing what I know now, I’d weigh Kotlin Multiplatform harder for an app with that much offline logic. The sync rules and the upload queue are exactly the kind of code you want written once and shared, while each platform keeps its own native background APIs.&lt;/p&gt;
&lt;h3 id=&quot;how-i-d-decide-for-your-app&quot;&gt;How I’d decide for your app&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;If your app… &lt;/th&gt;&lt;th&gt;I’d lean towards&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Targets one platform first, or leans hard on camera, video, sensors &lt;/td&gt;&lt;td&gt;Native&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Already has native apps, or complex logic that must match on both &lt;/td&gt;&lt;td&gt;Kotlin Multiplatform&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Needs both platforms fast, on a small budget, with the same screens &lt;/td&gt;&lt;td&gt;React Native&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;None of these is a rule. They’re where I start, and the feature list decides the rest. If you’re weighing this for a real app, &lt;a rel=&quot;nofollow&quot; href=&quot;https://abhishekgoyal.me/services/kmp&quot;&gt;here’s how I help teams adopt Kotlin Multiplatform&lt;/a&gt;, and every engagement starts with a paid discovery sprint that ends in a written recommendation you can use with anyone.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29953/native-kotlin-multiplatform-or-react-native-how-id-choose</guid>
<pubDate>Tue, 06 Oct 2026 08:23:00 +0000</pubDate>
</item>
</channel>
</rss>