Skip to content
Advanced10 min readUpdated September 2026

Native Versus Cross Platform Economics

The shared-code decision is an economic one, not a technical preference. What genuinely shares, what never does, the residue that eats the savings, and how to make the decision once so it is not relitigated every quarter.

The native versus cross-platform argument is the longest-running unresolved debate in mobile engineering, and it stays unresolved because both sides argue about the wrong thing. The question is almost never "which technology is better". It is "for this product, this team and this time horizon, what is the total cost of ownership of each option, and which risks are we choosing to carry". Framed that way, the answer varies legitimately between organisations. Framed as a technology preference, it becomes an identity question, and identity questions are relitigated forever.

The decision is also close to irreversible on any timescale a leadership team cares about. Migrating a mature application between these models is a multi-quarter programme that delivers no user-visible value, so it gets started, descoped and abandoned, leaving you with two architectures and the costs of both. It deserves real analysis once, a written record, and a commitment not to reopen it without new information.

The claim being tested

The pitch for a shared codebase is that you write a feature once and it appears on both platforms, so you approximately halve the cost of mobile. The pitch is not false. It is imprecise in a way that consistently overestimates the saving, because it counts lines of code rather than cost of ownership, and because the parts that do not share are systematically the expensive parts. Divide the work of a mobile application into four layers.

Business logic and data. Models, validation, state machines, caching, sync, serialisation, offline behaviour, analytics definitions. Genuinely platform-independent, and where the shared-code argument is strongest — and note that the argument holds here even without a cross-platform interface framework, because this layer can be shared through a library while the interface stays native.

Screen composition. Layout, navigation, lists, forms, the arrangement of standard components. This shares well with modern cross-platform frameworks, and is also, per unit of effort, the cheapest layer to write natively — worth remembering when the saving is estimated.

Platform interaction. Permissions, background execution, notifications, deep links, widgets, biometrics, accessibility services, payment and subscription flows, camera and media pipelines, keychain and keystore. This does not share. In a cross-platform stack it is written twice anyway, in native code, plus a bridge and the glue that keeps the bridge consistent. You have added a layer rather than removed one.

Platform character. Gesture and animation conventions, transition semantics, scroll physics, keyboard behaviour, dynamic type and accessibility idioms. Invisible in a specification, highly visible in use, and the layer where cross-platform applications are most often identifiable as such. The cost of matching native conventions rises steeply the closer you try to get.

The saving is real in the first two layers and illusory in the last two. Whether that is a good trade depends on how much of your product's value and risk lives in each, which varies enormously by product and is knowable in advance.

The residue nobody budgets for

Assume the shared layers share perfectly. There is still a residue of work that exists regardless, and it is what turns an expected halving into something more modest.

Two store relationships and two platform release cycles. Two submission processes, two sets of metadata, two review queues, two sets of policy — plus an operating system release from each platform every year carrying behavioural changes and deprecations. You absorb both. A shared codebase does not halve this and sometimes makes it worse, because you absorb the change and then wait for your framework to absorb it too. Platform rules differ, change independently and are enforced with judgement, so check current vendor guidance rather than carrying assumptions forward.

Native modules and the bridge. Every platform capability beyond what the framework provides becomes a native module: written twice, tested twice, wrapped in an interface that must behave identically on both sides or your shared logic is lying to itself. This is the work most often assumed away when the decision is made.

Performance work is native work. When something is slow — a list that stutters, a cold start that has crept up, memory pressure on older hardware — the investigation goes through the framework and into the platform, requiring native profiling tools and native understanding, with a fix that is frequently platform-specific. Cross-platform does not remove this skill requirement; it adds a layer between the engineer and the thing they are debugging.

Framework dependency risk. You have taken a dependency on a third party's roadmap for your entire application. When a platform ships a change, you wait; when the framework breaks something, you migrate. Not on its own a reason to avoid cross-platform, but it should be priced.

Divergence you did not plan. Conditionals accumulate: this screen differs because review required it, this flow because the payment model differs, this component because it was rewritten natively for performance. A shared codebase with heavy platform branching has the maintenance profile of two codebases plus the coordination cost of one — debt with no upside.

The decision table

Score honestly, and note that this is not a rubric to be totalled — some rows are decisive on their own.

FactorPoints towards two native codebasesPoints towards a shared codebase
Product centre of massCamera, media, hardware, background behaviour, feelContent, forms, lists, remote data, workflow
Interface fidelityMust feel unmistakably native on each platformConsistent brand experience acceptable, or preferred
Performance envelopeTight frame budgets, heavy rendering, large data on deviceOrdinary application performance is sufficient
Team sizeLarge enough to sustain two competent platform teamsToo small to staff both platforms properly
Feature parity requirementPlatforms can diverge in scope and timingStrict parity demanded by the business
Platform adoption rateNew operating system features needed on day oneYou can wait for framework support
Product maturityEstablished, long horizon, cost dominated by maintenanceEarly, validating, may pivot or be discarded
Regulatory or security posturePlatform-specific hardening, attestation, certified componentsStandard requirements
Skills and hiring marketDeep native expertise in house, or reachableThin native bench, or a web-heavy local market

Two rows carry more weight than the others.

Product centre of mass most reliably predicts regret. Teams that chose a shared codebase for a media-heavy or hardware-heavy product describe the same trajectory: fast for two quarters, then progressively dominated by native module work and performance investigation, until the shared layer is a thin wrapper around two native implementations.

Team size most reliably predicts the opposite regret. An organisation that cannot staff two codebases with real depth ends up with a strong platform and a neglected one, which is worse than a shared compromise on both because it degrades continuously and nobody owns the degradation. If you cannot staff two, the decision is largely made for you, and the honest framing is that you are choosing which compromise to take.

The middle option people forget

The framing is usually presented as binary, and it is not. The most durable arrangement in many organisations is native interfaces over a shared logic layer: business logic, models, sync, caching and API interaction written once in a language that compiles for both platforms and exposed as a library, with a native interface on each side — SwiftUI or UIKit on one, Jetpack Compose on the other — consuming it.

This captures the saving where the saving is real. Business logic shares cleanly and is where duplication most reliably produces divergent behaviour users experience as bugs: two implementations of a sync algorithm will differ, and the difference will be a defect nobody can reproduce. Leaving the interface native means platform interaction and platform character are handled directly rather than through a bridge, performance work uses native tools, and new platform features arrive immediately.

The cost is that it does not reduce interface work, which is the work most visible to stakeholders, so the saving is harder to demonstrate. It also requires both platform teams to consume a library written in a language native to one of them and foreign to the other — a real organisational friction. A backend-for-frontend pushes this further, moving logic off the device entirely, which is worth considering for anything that need not work offline.

Team topology and the hiring market

This is also an organisational design decision, and the organisational consequences outlast the technology.

Two native codebases produce two platform teams, meaning two backlogs, two capacity constraints, and every feature requiring coordination. It behaves the way any loaded queueing system behaves: features complete at the speed of the slower platform, parity becomes coordination overhead, and the teams drift in conventions and quality. The mitigation is feature teams containing both platform skills rather than platform teams containing one, which requires enough people to form several without fragmenting the expertise.

A shared codebase produces one team, removing that coordination cost — a genuine and often underweighted benefit. It introduces a different problem: a shortage of deep native knowledge you still need and now have no structural reason to maintain. Teams discover the gap during a performance incident or a platform release that breaks something, and discover at the same moment that the market rate for the specialist they urgently need has not fallen because they stopped hiring for it.

Hiring is a real input, not a rationalisation. In most markets the pool of engineers comfortable in a web-adjacent cross-platform stack is larger than the pool of senior native specialists. This cuts both ways: choosing a stack for hiring reasons makes you dependent on that stack remaining popular, and framework popularity has been less stable than either native platform.

Ownership must be unambiguous either way. The worst configuration is a shared codebase owned by a web team who treat mobile as a deployment target. Mobile brings its own release constraints, observability problems and versioning consequences, and whoever owns the code owns those too.

Making the decision stick

A decision reopened every quarter costs more than an unanalysed one, because it consumes senior attention repeatedly and undermines commitment in between. Treat it as an architecture decision record.

Write down the decision, the date, the alternatives considered and the assumptions. Including rejected options and why, and the load-bearing inputs: team size, product direction, performance requirements, hiring plan, the fraction of work that is business logic. Most relitigation starts with someone proposing an option already evaluated, and the record ends that conversation in a minute rather than an afternoon.

Write down the triggers that would reopen it. The assumptions materially changing, a sustained inability to meet performance budgets, the framework's maintenance status changing, or two consecutive quarters in which most mobile effort went into bridging and native modules. Named triggers convert "someone is unhappy" into "has a trigger fired", which is answerable. Set an annual review date too, so you can decline an unscheduled one without appearing closed-minded.

Do not reverse it incrementally. The failure mode is rewriting one screen natively to fix a performance problem, then another, then a flow, until you have two architectures and no plan. If the decision is wrong, change it deliberately with a scoped programme and a stated end state. If it is right, fix the problem within it.

What to do on Monday

If the decision is made and working, write the record retrospectively: decision, date, assumptions, reopening triggers, review date. An hour of work that saves a recurring argument.

If it is open, do the pull request analysis first. Classify the last two quarters of merged mobile work as business logic, screen composition, platform interaction or performance. You will have a percentage split by the end of a morning, and it will tell you more than any framework comparison.

If it is being relitigated, find out whether a trigger has fired or whether someone is simply frustrated. Both are worth addressing, but only one is an architecture conversation, and mixing them up is how organisations end up with two codebases and half a migration.