- Why the old argument is dead
- Team personas, not user personas
- Where AI agents actually help
- Eleven teams, one decision
- Why shared logic still wins
- The counterarguments worth taking seriously
- Where native still wins
- A decision framework
- The stack question is an org-design question
Why the old ‘cost’ argument is obsolete
For a decade, the case for cross-platform frameworks was simple arithmetic. Two platforms meant two codebases, two teams, and twice the cost. Flutter, React Native, and Xamarin before them promised to cut that bill in half.
AI coding agents have since broken that arithmetic. An agent can now take a feature spec and produce a reasonable Jetpack Compose screen and a reasonable SwiftUI screen in the time it takes to review one of them. If writing code twice is nearly free, the original argument for cross-platform looks dead.
The fact is that writing the code has always been only a small part of what it costs to build software. The expensive part is owning it: reviewing it, testing it, debugging it at 2am, and keeping two implementations in agreement every time a requirement changes. AI made generation cheap. It did nothing for verification. In fact, it made verification the bottleneck.
AI can write your app twice. It can’t make sure both versions agree.
That’s the argument of this post. I’ll make it through a lens borrowed from design: personas. Not user personas, but team personas, because what kind of team you are should decide your stack far more than any framework benchmark.
A disclosure up front. I’ve spent 12+ years building Android and cross-platform apps, so when I end up recommending Kotlin Multiplatform for shared logic, you’re entitled to be suspicious. I’ll try to earn the conclusion by arguing the other side harder than its own advocates usually do, and by showing where my own recommendation loses.
Team personas, not user personas
Designers don’t argue about whether a button should be blue in the abstract. They ask who the user is: a commuter checking a ride on a crowded platform, a first-time investor nervous about hitting “Buy”. The persona makes the trade-offs discussable.
The stack debate has never had that discipline. We argue about native vs cross-platform as if there were one right answer for everyone. Users mostly can’t tell which stack you picked, as long as you picked it well. What varies wildly is the team: how many engineers it has, whether they’re split by platform, where its correctness risk lives, and how much code it can realistically review.
So instead of asking “which stack is best?”, this post asks “which stack fits this team?” Every persona below is described along the same five axes.

The last axis is the new one. It barely mattered when humans wrote every line, because writing speed and review speed were roughly matched. With agents, a team can generate far more code than it can meaningfully verify, and that gap changes the answer for almost every persona.
Where AI agents actually help
Agents are excellent at the work that used to justify cross-platform frameworks, and much weaker at the work that actually causes production incidents.

The left column is about producing code. The right column is about being correct. Agents move the left column close to zero cost, which means the right column is now where nearly all engineering effort goes.
Here is what that looks like when a single business rule changes, say a new fee cap on transfers.

On the left, the question “do they agree?” has no automated answer. Two reviewers each approve code that looks right in isolation. On the right, agreement isn’t a question at all, because there is only one implementation.
Agents do help KMP too. They’re good at generating expect/actual declarations, Swift-friendly wrappers, and interop boilerplate, which used to be KMP’s biggest tax. One honest caveat: models have seen far more idiomatic Swift and Android Kotlin than KMP-specific code, so expect the occasional confident mistake around multiplatform tooling.
Eleven teams, one decision
Here are eleven teams facing the same question, side by side. Shared logic in KMP wins in roughly half of them, and I think that’s the most important finding in this post.

Six of these are worth walking through in detail.
1. The global ride-hailing platform
Maps, background location, battery tuning, and live trip updates are platform-deep problems, so native UI is non-negotiable. Uber has written publicly about going native and building its own architecture framework (RIBs) to manage scale.
The shared-logic opportunity is narrow but valuable: the trip state machine (requested, matched, arriving, in trip, completed, disputed) and its offline and reconnect behaviour. A trip that shows “arriving” on iOS and “matched” on Android is exactly the kind of silent divergence this post is about.
The twist is that rider and driver apps are different personas. In markets like India, drivers are overwhelmingly on Android and use the app as a work tool. The platform mix of your users is a persona input most teams forget.
2. The solo entrepreneur
This is where “AI writes native twice” is most tempting and most dangerous. An agent will happily generate a SwiftUI app and a Compose app. But one person can meaningfully review and maintain roughly one codebase. Verification capacity is the binding constraint.
Pick one codebase: React Native if you come from the web, Flutter otherwise, KMP with Compose Multiplatform if you already think in Kotlin. Also ask whether a PWA would validate the idea faster.
3. The Neobank
This is the flagship case for shared logic. Fee calculations, transaction validation, KYC flow rules, offline transaction queues, and cryptographic signing all run on the device, and each has regulatory weight. Two implementations of a fee rule means two audits and two chances to be wrong.
Share the logic in KMP with one test suite, and keep the UI native: biometric prompts, trust signals, and accessibility are platform territory. Cash App is a well-known public example of sharing Kotlin across platforms. Nubank went the other way with Flutter, and that’s defensible when UI consistency and team scaling matter more than platform feel.
4. The quick-commerce app in India
The user base is huge, mostly on Android, often on budget phones with patchy networks. App size and cold-start time are business metrics, so native Android performance is critical.
The team is usually asymmetric, with a much bigger Android team. That makes KMP organisationally natural: the Android team already writes the logic, and sharing cart and pricing-display rules multiplies a smaller iOS team. Merchandising surfaces change daily, so they belong in server-driven UI anyway.
5. The enterprise field-service app
Inspectors, logistics drivers, and insurance assessors work offline for hours with complex forms, validation, and sync conflict resolution. Nobody cares whether the app feels iOS-native. Everybody cares whether the data is right.
This is the one persona where I’d share the UI as well, with Compose Multiplatform. It’s a useful stress test of my own “logic yes, UI no” rule: when native feel doesn’t matter, sharing UI is fine.
6. The legacy bank with two native apps
Ten years of Swift and Kotlin, two platform teams of 30 people each, and a long history of “why does iOS show a different balance?” tickets. This is the most common real-world situation, and the one most blog posts ignore.
Don’t rewrite anything. Extract logic into KMP one module at a time, starting with the module that has the worst divergence history. Staff it as a shared-logic team with engineers from both platforms, not as an Android-owned library the iOS team is told to consume.
Why shared logic still wins
The usual claim is that most bugs live in business logic. I don’t think that’s provable, and crash dashboards often say otherwise: lifecycle, concurrency, and memory issues are loud and frequent. The better claim is about cost and visibility.
UI bugs are loud. Someone sees a broken layout and files a ticket within hours. Logic bugs are silent. The wrong fee is charged, a sync conflict resolves differently on iOS, or a validation rule lets bad data through, and nobody notices until it has reached production, spread, and corrupted data or trust.
Two correct-looking implementations can disagree
When an agent writes the same rule in Kotlin and in Swift, each version can look idiomatic and pass its own review while behaving differently. The languages themselves make different default choices.

None of these is a bug that a reviewer would catch by reading one file. Each implementation is correct by its own platform’s conventions. The bug only exists in the gap between them.
One source of truth, one test suite
With KMP, the rule exists once and its tests exist once. Both apps consume the same compiled module, and the same commonTest suite runs on the JVM and on iOS targets.

The UI layers stay fully native and platform-owned. Everything below them is written, reviewed, and tested once:
// commonTest: runs against both the Android and iOS builds
class TransferFeeTest {
@Test
fun feeIsCappedAtTheLimit() {
assertEquals(Money("25.00"), TransferFee.calculate(Money("10000.00")))
}
@Test
fun roundingIsHalfEvenEverywhere() {
assertEquals(Money("2.34"), TransferFee.round(Money("2.345")))
}
}
This changes three things:
- Correctness is proven once. A rule that passes commonTest passes on both platforms, because it is the same code.
- Changes propagate atomically. A new fee cap is one pull request and one review, with no chance that only one platform got updated.
- Review effort halves where it matters most. Reviewers spend their limited attention on the logic that can quietly cost money, not on reading it twice.
The important scoping: this matters in proportion to how much logic truly runs on the client. For a thin client over a smart backend, there may be little worth sharing.
The counterarguments worth taking seriously
Four objections come up every time I make this case. Each one is partly right.
1. “Just put the logic on the server”
This is the strongest objection. If you want a single source of truth, a thin client with logic behind an API gives you one without any multiplatform tooling. For many consumer apps, like the D2C retail persona, it’s the correct answer.
It stops working when logic has to run on the device: offline-first workflows, sync and conflict resolution, latency-sensitive validation, on-device calculation, encryption and signing, and complex local state machines. Those are exactly the personas where shared logic won in the table above.
2. “Share the spec and the tests, not the code”
A team can keep one written spec plus a set of JSON test fixtures (inputs and expected outputs), let agents generate native Kotlin and Swift, and run both against the same fixtures. That’s a legitimate middle path, and it’s often the politically easiest first step for a brownfield team.
The weakness is that it creates four artifacts that can drift: the spec, the fixtures, and two test harnesses. KMP collapses that into one compiled source that the build enforces.

3. “Why not share the UI too?”
Now that Compose Multiplatform is stable on iOS, this is a fair question. My reasons for keeping UI native in most personas: platform feel and conventions matter to users, accessibility and system integration are platform-deep, iOS engineers should own the iOS experience, and UI is the part agents regenerate most cheaply. When none of those apply, as with field-service apps, share the UI too.
4. “KMP has real costs”
It does. Swift interop around coroutines, Flows, sealed classes, and generics still needs care. Build times grow, debugging Kotlin from Xcode is less comfortable, and binary size needs watching.
The biggest cost is organisational. KMP often becomes “the Android team owns the logic and iOS consumes a framework it didn’t write.” That’s a recipe for resentment and slow adoption. It’s also why the final section of this post is about org design rather than technology.
Where native still wins
Fully native, with nothing shared, is the right call more often than KMP advocates admit. It wins in four situations:
- The feel is the product. Camera apps, creative tools, premium consumer social apps, and anything competing on motion and polish. Every abstraction layer is a tax on the thing users are paying for.
- The app lives in platform APIs. Widgets, Live Activities, watch apps, background execution, media pipelines, on-device ML, deep HealthKit or Health Connect use. When most of your code is platform integration, there is little left to share.
- The team is two strong specialist teams and the client is thin. If the logic sits on the server and both platform teams are excellent, shared code adds coordination cost for little correctness gain.
- You only need one platform. A rugged-Android fleet, an iPad-only enterprise tool, or a driver app in an Android-dominant market. The best cross-platform strategy for one platform is none.
Agents make this option cheaper than ever. A strong native team with good agents can ship both platforms quickly. The trade-off doesn’t disappear, though: the moment real logic moves onto the device, the divergence risk from the previous section comes back.
A decision framework
The personas are reduced to a short sequence of questions. Answer them in order, because the first one does most of the work.

The heatmap below scores every persona on the five axes. Read it left to right: the first two columns, client logic and cost of an error, predict where shared logic pays off. The right-hand columns predict how much of the UI should stay native.

Three patterns stand out:
- Client-side logic density is the strongest predictor. Every persona where shared logic won strongly (neobank, field service, health, legacy bank) scores 4 or 5 on both client logic and cost of an error.
- Verification capacity is the axis AI added. The solo founder can generate two native apps but can’t verify them. The neobank can generate them but can’t afford to audit two. Both conclusions come from the same constraint.
- Your users’ platform mix belongs in the framework. Ride-hailing drivers and Indian quick-commerce shoppers show that the answer can be asymmetric: native-first on the platform most of your users are on.
A quick self-check before choosing:
- We can name the specific business rules that must behave identically on both platforms.
- We know whether those rules run on the device or the server.
- We have estimated how much generated code we can review well each sprint.
- We have agreed who owns shared code, and it isn’t only the Android team.
- We have checked where our users are: Android, iOS, or both.
The stack question is an org-design question
When code was expensive, choosing a stack was a budgeting decision. Now that agents have made code cheap, the scarce resources are different: correctness, shared understanding, and the attention of the people who review what gets shipped.
Seen that way, choosing KMP (or not) is really a decision about where your team’s single source of truth lives and who owns it. A shared module owned by one platform team is a political problem wearing a technical label. A shared module owned jointly, reviewed by both platforms, and tested once is an organisational advantage that no amount of generated code can replicate.
So the next time someone asks whether to go native or cross-platform, don’t start with frameworks. Start with three questions: what kind of team are we, where does our logic run, and how much can we actually verify?
AI can write your app twice. Deciding that it should only be written once, in the places where being wrong is expensive, is still an engineering judgment.
Originally posted on "Anmol Verma" on October 16, 2026.


.png)
.jpg)
