September 25, 2026

Kotlin Multiplatform: a CTO's guide to adoption

A CTO's guide to Kotlin Multiplatform: 2026 stability by target, iOS interop, the migration path for existing apps, and who to hire.

Recruiting

Tech

Mobile

Kotlin Multiplatform lets an Android team and an iOS team stop writing the same business logic twice, without forcing them into one shared UI. That distinction is the whole decision. JetBrains, KMP's creator, defines the technology as "an open-source technology from JetBrains that enables sharing code across Android, iOS, desktop, web, and server while retaining the advantages of native development." The same page adds the part that actually shapes a rollout plan: "You can share code on your terms: share isolated modules, such as networking or storage, and progressively expand shared code over time. You can also share all of your business logic while keeping the UI native, or gradually migrate the UI using Compose Multiplatform."

Flutter shares both logic and UI from one codebase, rendered through its own engine instead of native platform widgets. KMP makes the opposite bet by default: native UI stays native on each platform, Jetpack Compose on Android, SwiftUI or UIKit on iOS, and only the logic underneath moves into a shared module. You can widen that scope later with Compose Multiplatform, covered below, but nothing forces you to the moment you adopt KMP.

The practical range runs from sharing one networking client to sharing the entire business logic layer while keeping two thin native UIs on top. A team can start at the narrow end, prove the pattern on a low-risk module, and expand only once that module has shipped without incident. For the case for making that bet at all, HighCircl's interview on why teams are adopting Kotlin Multiplatform covers the reasoning; what follows here is the mechanics of doing it.

Is Kotlin Multiplatform stable in 2026?

KMP crossed from beta into Stable in November 2023. JetBrains put it plainly in the stable-release announcement: the technology "has become Stable and is now 100% ready for use in production." JetBrains' own stability definitions draw a hard line between what that means and what a Beta label means: Stable is "done. We will be evolving it according to our strict backward compatibility rules." Beta is "almost done, user feedback is especially important now... changes are possible." Check which label applies to your specific target before assuming a 2023 headline covers every platform equally, because it doesn't cover all of them the same way.

Here's where each target actually sits:

TargetKotlin MultiplatformCompose Multiplatform (UI layer)
AndroidStableStable
iOSStableStable
Desktop (JVM)StableStable
Server-side (JVM)Stablenot offered
Web (Kotlin/JS)Stablenot offered
Web (Kotlin/Wasm)BetaBeta
watchOSBetanot offered
tvOSBetanot offered

For the two platforms most product teams actually ship to, Android and iOS, both the shared logic layer and, if you adopt it, the Compose Multiplatform UI layer sit at Stable. Wasm, watchOS, and tvOS are still Beta, so budget for API changes between releases rather than treat the code as settled.

How a Kotlin Multiplatform project is structured

Shared code lives in commonMain. JetBrains' documentation on the project layout is explicit: "Kotlin code shared among platforms is typically located in the commonMain directory." Everything platform-specific sits in its own source set, jvmMain, androidMain, iosMain, and so on, each one defined as a Kotlin source set, "a set of source files with its own targets, dependencies, and compiler options."

The mechanism that connects common code to platform code is expect/actual. A function, class, or property gets declared once in commonMain with the expect keyword, and each platform source set supplies its own actual implementation. JetBrains describes the pattern as letting you "access platform-specific APIs from Kotlin Multiplatform modules," while you "provide platform-agnostic APIs in the common code." In practice, it looks like this:

Kotlin
// commonMain
data class Identity(val userName: String, val processID: Long)

expect fun buildIdentity(): Identity
Kotlin
// jvmMain
actual fun buildIdentity() = Identity(
    System.getProperty("user.name") ?: "None",
    ProcessHandle.current().pid()
)
Kotlin
// nativeMain
actual fun buildIdentity() = Identity(
    getlogin()?.toKString() ?: "None",
    getpid().toLong()
)

Shared code calls buildIdentity() without knowing which platform's actual implementation it resolves to at compile time. Every platform-specific dependency the shared module needs, a secure keystore, a device identifier, a push-notification token, follows the same pattern: declare the shape once, implement it per platform.

Connecting the shared module to iOS

The shared module compiles to a native framework that an iOS app consumes like any other dependency, and JetBrains' iOS integration guide lays out three ways to wire it in. Direct integration connects the framework straight from the KMP project by adding a script to the Xcode project, and it works as long as the KMP project doesn't import CocoaPods dependencies. CocoaPods integration connects the framework through CocoaPods itself, and JetBrains recommends it for a monorepo setup where the iOS project already uses CocoaPods or imports CocoaPods dependencies. Swift Package Manager integration lets the framework depend on a local Swift package, and it fits a monorepo where the iOS side already uses Swift packages without irreplaceable CocoaPods dependencies in the way.

Which one you pick comes down to an inventory question more than a technical one: what your iOS app's dependency graph already looks like before KMP shows up. A team already deep in CocoaPods gains little from ripping it out to adopt SwiftPM on the same day it adopts KMP.

Compose Multiplatform: the optional UI layer

Compose Multiplatform sits on top of KMP as a separate, optional layer. Where KMP shares logic, Compose Multiplatform shares the UI itself, built on the same declarative model as Jetpack Compose, across Android, iOS, and desktop, all Stable targets per the table above, with web on Kotlin/Wasm still at Beta. Adopting it is a second decision, not a requirement of adopting KMP at all: plenty of production KMP apps keep native SwiftUI and Jetpack Compose UIs and share only the logic underneath. HighCircl's guide to Compose Multiplatform covers that UI-layer decision on its own.

How to adopt Kotlin Multiplatform in an existing Android + iOS app

1. Audit shared-logic candidates

List the logic currently duplicated between your Android and iOS codebases: API clients, data models, validation rules, caching, anything that isn't UI. Rank candidates by how identical the two implementations already are and how much duplicated-bug risk they carry. The module with the fewest platform-specific edge cases is your first target, not the module that would save the most lines of code.

2. Create the shared module

On the Android side, Android's own KMP migration documentation points to built-in tooling: "To create a Kotlin Multiplatform (KMP) module within your Android project, use the Kotlin Multiplatform Shared Module template, available in Android Studio Meerkat and Android Gradle Plugin version 8.8.0 and higher." Run the wizard, and Android Studio scaffolds the commonMain/androidMain/iosMain source sets for you.

The wizard stops short of wiring anything up. As the same migration guide notes, the module wizard doesn't add the newly created module as a dependency to any existing module, so you need to add the new shared module as a Gradle dependency on your existing app module, the same way you'd add any other internal library.

4. Wire expect/actual for the platform APIs the shared module needs

Every platform-specific call the shared module makes gets declared once with expect in commonMain and implemented separately in androidMain and iosMain, following the pattern covered above.

5. Connect the shared framework to your iOS app

Pick direct integration, CocoaPods, or SwiftPM based on what your iOS project's dependency graph already looks like, then build the framework into your existing Xcode project.

6. Migrate one module at a time

Ship the first shared module, watch it in production, and only then move to the next candidate on your audit list. KMP's incremental-adoption model exists specifically so you're not committing to a full rewrite before you've proven the pattern holds for your codebase.

7. Expand test coverage as shared code grows

A bug in shared logic now ships to both platforms at once instead of one. Test coverage on the shared module matters more than it did when the same logic lived in two separate, independently buggy implementations.

Who's running Kotlin Multiplatform in production

JetBrains' own case studies list companies running KMP at real scale, not in demos. McDonald's expanded KMP from an initial payments-feature test to its entire application, and its writeup credits the move with fewer crashes and stronger performance on both platforms, plus a shift from separate Android and iOS teams to one unified mobile team. Philo has run KMP "across mobile, TV, and web clients in production for three years." Forbes shares over 80% of its logic across iOS and Android through KMP, with simultaneous feature rollout on both platforms. Cash App transitioned from a shared JavaScript layer to KMP back in 2018. Duolingo cites KMP for speeding up shipping and building confidence in the technology itself. Netflix shares logic across its mobile studio apps using KMP. Baidu and Kuaishou both run it in production, with Baidu's Wonder App unifying its data and business logic layers across iOS and Android, and Kuaishou running KMP in production for two years.

McDonald's, Forbes, Cash App, and the rest already made the staffing decision the next section is about.

What to hire for when you adopt KMP

KMP doesn't create a new job title. It changes what you need from the Android and iOS engineers you already have, or are about to hire. Android engineers need to already be fluent in Kotlin, since that's the shared module's language by default, plus enough Gradle and build-tooling comfort to manage multi-target source sets without breaking the build for the other platform. iOS engineers need Swift fluency first, since that's still what ships the iOS app's UI and platform integration, plus the ability to read Kotlin-generated interop code and debug across the boundary when something in the shared framework doesn't behave the way the iOS side expects.

That second skill, reading and debugging Kotlin from the Swift side, is the one most interview loops skip, because it doesn't show up on a resume the way "5 years Swift" does. HighCircl's iOS hiring guide and Android hiring guide cover the broader vetting process; for a KMP team specifically, add a working session where the candidate reads shared-module code they didn't write and explains what it does on their platform.

HighCircl covers both sides of that hire, staffing HighCircl's Android and iOS engineers through the same four-stage, engineer-led vetting process used across its stacks: background and experience verification, a communication and product-thinking assessment, a take-home technical project, and a live session on architectural reasoning. Senior rates run €45-105/hr ($50-115/hr), with a flat 20% margin capped and shown separately from what the engineer earns.

Frequently asked questions

Is Kotlin Multiplatform production-ready?

Yes, as of November 2023, when JetBrains marked the core technology Stable. Stability varies by target: Android, iOS, desktop (JVM), server-side (JVM), and Web/Kotlin-JS are Stable, while Web/Kotlin-Wasm, watchOS, and tvOS remain at Beta.

Do I need to rewrite my whole app to use Kotlin Multiplatform?

No. KMP is built for incremental adoption. You can share one isolated module, like networking or storage, and expand from there, or keep the UI fully native while sharing all of your business logic underneath.

Does Kotlin Multiplatform replace Flutter or React Native?

KMP shares logic while keeping native UI by default. Flutter and React Native share both logic and UI through their own rendering layers from the start. KMP only closes that gap if you separately adopt Compose Multiplatform for the UI layer on top of it.

What's the difference between Kotlin Multiplatform and Compose Multiplatform?

Kotlin Multiplatform shares business logic across platforms while native UI stays native on each one. Compose Multiplatform is a separate, optional layer built on top of KMP that shares the UI itself, using the same declarative model as Jetpack Compose.

Share this article

Author Image

HighCircl Editorial Team

The HighCircl editorial team writes about hiring software engineers, nearshore development, and engineering team building. Our articles draw on direct experience sourcing and placing senior developers across Poland, Hungary, Slovakia, Serbia, Slovenia, Romania, and Spain — and on candid conversations with the CTOs and engineering leads who hire them.

HighCircl is a nearshore engineering network that delivers matched candidate shortlists in 72 hours. Every piece of content we publish is informed by real engagement data: actual developer rates, real hiring timelines, and what separates engineering teams that scale cleanly from those that stall.

Take Me to the Experts

Access our network of industry-leading software engineers.

Start Now