What Kotlin Multiplatform is — and is not

Kotlin Multiplatform (KMP) lets code written in Kotlin run on more than one platform. For a mobile product, that usually means the rules, data models, networking, storage and validation are written once and compiled for both iOS and Android. The user interface can stay native on each side, or be shared as well with Compose Multiplatform where the design allows it.

It is not “write once, run anywhere” in the old sense. It is an honest tool for removing duplication: you decide what to share, and you keep the platform-specific parts where they belong. That is exactly why it works for products with real logic in them rather than a thin wrapper around a website.

What we put in the shared layer

The shared layer is where a product's behaviour lives. That starts with the domain model — what an item is, what states it can be in, which transitions are allowed — and continues through networking, local persistence, validation, date and time handling, and the rules that decide what the user sees next. Anything that must behave identically on both platforms is a candidate.

Putting these in one place means a fix lands on both platforms at once. It also means the tests are written once and prove the same behaviour from both sides, which cuts down on the slowest kind of bug: one that exists on Android but not on iOS, or the other way round.

What stays native

Screens, navigation and the details that make an app feel at home on a platform stay native when that is the better call. Camera, notifications, background execution, secure storage, widgets and deep links are all better handled with the platform's own APIs, wrapped behind a small interface the shared code can call. The shared layer does not need to know how iOS stores a token; it only needs to ask for one.

This split is the whole design decision. Too little shared code and you have two separate apps with two sets of bugs. Too much and you spend the project fighting the platform. We aim for the middle and adjust per product.

Compose Multiplatform for the interface

Where the product's design is genuinely the same on both platforms, Compose Multiplatform can share the interface as well as the logic. That suits focused utilities, internal tools and products with a strong, consistent visual language. Where each platform has its own conventions, or where the design leans on native components, we keep the interface native and share only the logic. The choice follows the product, not the other way round.

Proven on our own product

We did not adopt this approach from a conference talk. Paussible, our 60-second wellbeing app, is built with Kotlin Multiplatform and Compose, with a local-first design: the pauses, the copy and the user's preferences live on the device, so the app works offline and collects nothing. That constraint forced a clean separation between content, state and interface — the kind of discipline that pays off on client work too. Read the Paussible case study.

When we would not use it

KMP is a poor fit when the app is mostly platform-specific, when the team is not using Kotlin, or when the product is small enough that two simple native apps would be easier to maintain than a shared build. It also adds build complexity that has to be worth paying for. We will tell you when a straightforward native build is the better answer, even though it means less shared code for us to point at.

Structure and handover

A KMP project is easiest to live with when it is divided by feature rather than by layer: a core module for shared infrastructure, then feature modules that each own their logic and expose a small surface to the native apps. Builds stay fast, boundaries stay clear, and one part can change without shaking the rest. The same repository builds both apps, so there is one history to read and one place to change.

If you are weighing up native versus shared, bring us the product description and we will give you a direct recommendation with the trade-offs spelled out.

Ask us whether KMP fits your app