Why we stopped building native apps

Why we stopped building native apps

We used to build apps the way most agencies did: a Swift codebase for iOS, a Kotlin codebase for Android, two teams, one product. We do not do that any more. Everything we ship now is cross-platform, in React Native.

The reason is not that cross-platform got cheap enough to compromise on. It is that we kept watching the same thing happen on native projects, and eventually we stopped pretending it was normal.

Two codebases means two of everything

This sounds obvious written down. It is much less obvious when you are three months into a project and the cost is arriving in instalments.

Every bug gets fixed twice

A user reports something. Someone reproduces it, finds the cause, writes the fix, tests it, ships it. Then someone else does all of that again in a different language, in a different codebase, with a different set of platform quirks in the way.

Sometimes the second fix is quick because the first one showed the way. Often it is not, because the two implementations had drifted far enough apart that the bug lives somewhere else entirely on the other platform. Either way you are paying for the same problem twice, and the second payment is the one nobody put in the estimate.

The two apps stop being the same app

This is the part that actually damages products. Not a dramatic divergence — nobody sets out to build two different apps. It happens in small increments. A validation rule gets tightened on iOS during a rush and not mirrored. An empty state gets better copy on Android because that developer noticed it. An edge case in the sync logic gets handled on one side and not the other.

Six months in, your support team is asking which platform a user is on before they can answer a question. Your QA matrix has quietly doubled. And when someone asks what the app does when X happens, the honest answer is that it depends.

The maintenance bill nobody quotes for

Apps do not sit still after launch. iOS and Android each ship a major version every year, deprecate APIs on their own schedule, and change store policy when it suits them. With one codebase that is one round of work. With two it is two rounds, in two languages, needing two sets of specialist knowledge on the team, permanently.

That cost does not show up in the build quote. It shows up every year afterwards, and it is the reason a native app that was affordable to build turns out to be expensive to own. More on that in application support.

What changed to make this a real choice

Ten years ago the argument for native was straightforward: cross-platform frameworks produced apps that looked wrong, felt slow, and could not reach half the platform APIs. That was true and it was disqualifying.

It stopped being true a while ago. React Native renders real platform components rather than approximations of them. The performance gap for the kind of app most businesses actually need — lists, forms, media, network calls, local storage — is not something a user can perceive. And where a genuine platform capability is missing, you write that one piece natively and call it from the shared codebase, which is a very different proposition from maintaining two entire applications.

So the question changed. It stopped being whether cross-platform could do this, and became whether there was any reason to pay twice.

The objection worth taking seriously

The strongest argument against cross-platform is not performance. It is that apps built this way often feel generic — same layout on both platforms, navigation that matches neither, gestures that behave like a website.

That is a real failure and it is common. It is also a choice, not a consequence. Users have expectations about how their phone works: where the back gesture lives, what a share sheet looks like, how a date picker behaves. Meeting those expectations is work, and the reason so many cross-platform apps feel off is that the work was skipped in the name of the saving.

We do that work. An iPhone user should not be able to tell how the app was built. If they can, the saving was not worth having.

When native is still the right answer

There are cases where we will tell you to build native, and we would rather say so during scoping than after you have signed something.

  • Real-time graphics and games. Anything driving a rendering loop at frame rate wants to be as close to the metal as possible.
  • Deep OS integration. Complex widgets, custom camera pipelines, sustained background processing, AR, tight integration with platform health or wallet frameworks.
  • Products whose entire value is one platform-specific capability. If the app exists because of something only iOS does, the shared codebase is solving a problem you do not have.
  • Hard binary size or startup constraints, usually in markets with older devices and constrained data.

None of those describe most business applications. If yours is on that list, we will say so.

What we build instead

One codebase in React Native, running on both stores, maintained by one team. Fixes land on both platforms at once. The two versions behave the same way because they are the same code. Platform conventions are followed where users expect them, deliberately, as part of the build rather than as a polish pass at the end.

Underneath that, the parts that decide whether an app survives contact with real users: a proper data layer rather than a cache bolted on late, offline-first storage and sync with server-side authority over conflicts, accounts and roles, and the backend the whole thing talks to — in PHP with Laravel, Node.js and NestJS, or Python with Django, depending on what you already have.

Flutter is in our stack and we are open to projects that call for it. React Native is where our depth is, and we will say which one we think fits your product rather than which one we would prefer to write.

How to decide for your own product

Three questions, in this order.

  1. Does the product depend on something only a native build can do? If yes, the decision is made. If no, keep going.
  2. Can you afford to maintain two codebases for as long as the app exists — not to build them, to maintain them? Most companies can afford the build and have not priced the decade after it.
  3. Do you need the two versions to behave identically? If your support, compliance or reporting depends on that, one codebase gives it to you for free and two make it a permanent effort.

If you get to the end of those and the answer is still native, that is a real answer and you should build native. In our experience most products get one question in.

Where this leaves you

We made this decision because we were tired of charging clients twice for the same work and calling it thoroughness. Cross-platform is not the budget option in what we do — it is the whole approach, and the money it saves goes into the parts of the app users actually notice.

If you are weighing this up for a product of your own, we will give you a rough estimate and an honest view of which approach fits, before anyone signs anything.

Related reading

Previous Article
Building an app that keeps working when the connection drops
Previous Article
Next Article
React Native or Flutter: how we actually choose
Next Article

Let’s talk about your project

welcome@ideus.biz