A team has a single JVM library. When would you reach for the Kotlin Multiplatform plugin instead of the plain `kotlin("jvm")` plugin, and what does adopting it cost?
answer
- kotlin(jvm) = simple, fast, JVM-only default
- KMP when concrete second platform + shared logic
- costs: build time, host/CI matrix, library availability, publishing
- don't adopt speculatively (YAGNI)
- migration is mechanical: main -> jvmMain, add target
basics
~20 sUse kotlin("jvm") if you only ever need the JVM — it's simpler. Reach for kotlin("multiplatform") when you genuinely need to share code with other platforms (iOS, JS, native). KMP adds build complexity, slower builds, and host constraints, so don't adopt it for a JVM-only library.
solid answer
~50 sThe decision is about *actual* sharing needs, not future-proofing. `kotlin("jvm")` is a single target with the familiar `main`/`test` source sets and fast builds — the right default for JVM-only code. You switch to `kotlin("multiplatform")` when the same logic must run on non-JVM platforms: an iOS app sharing domain/networking code, a JS frontend reusing validation, or a native CLI. KMP's costs are real: the source-set hierarchy and per-target task fan-out, slower full builds (`check` runs every target), Kotlin/Native host constraints that force a multi-OS CI matrix, a smaller pool of multiplatform-ready libraries for `commonMain`, and publishing complexity (root + per-target modules, GMM). For a library that is JVM-only today with no concrete cross-platform consumer, the pragmatic call is to stay on `kotlin("jvm")` and migrate later if a real second target appears — the migration is mechanical (move `main` to `jvmMain`, add targets).
go deeper
Knowing kotlin("jvm") is simpler and KMP is for sharing across platforms is enough.
Contrast the two plugins concretely and name a couple of real KMP use cases.
Give a cost/benefit analysis (build time, CI matrix, library availability, publishing) and a YAGNI-based recommendation with the migration path.
Frame it as an org-level platform decision: shared-logic strategy, CI investment, library ecosystem maturity, team skill, and reversibility versus speculative complexity.
## The two plugins - `org.jetbrains.kotlin.jvm` — one platform (JVM). Standard `main`/`test` source sets, plain jar, fast configuration and builds, full access to every JVM library. This is the default for server/back-end Kotlin. - `org.jetbrains.kotlin.multiplatform` — the `kotlin { }` target DSL, the `commonMain` source-set hierarchy, per-target compilations/tasks, and multiplatform publishing. ## When KMP earns its keep Reach for KMP when there is a **concrete second consumer platform** and meaningful shared logic: - **Mobile**: an iOS + Android product sharing domain models, validation, serialization, and networking, with thin platform UIs. - **Web**: reusing pure logic (validation, calculations) between a JVM backend and a Kotlin/JS frontend. - **Native tooling**: a CLI shipped as a native binary that shares code with a JVM service. The value is *write-once* business logic with platform-specific edges expressed via `expect`/`actual` (a Kotlin-language feature) — eliminating duplicated, drift-prone reimplementations. ## The costs 1. **Build complexity**: the source-set hierarchy and per-target task graph are more to reason about than `main`/`test`. 2. **Build time**: `build`/`check` fan out across every target; a wide matrix is slow without good caching (configuration cache + remote build cache help). 3. **Host constraints**: Apple targets need macOS; you end up with a multi-OS CI matrix and target-prefixed tasks to shard it. 4. **Library availability**: `commonMain` can only use multiplatform-ready libraries; JVM-only libraries are confined to `jvmMain`. This narrows choices for shared code. 5. **Publishing**: root + per-target modules and Gradle Module Metadata, versus a single jar. 6. **Tooling/IDE**: generally good but with more moving parts than single-platform. ## The pragmatic rule Don't adopt KMP speculatively. A JVM-only library should use `kotlin("jvm")`. If a real cross-platform need materializes, **migration is mechanical**: ```kotlin // before plugins { kotlin("jvm") } // after plugins { kotlin("multiplatform") } kotlin { jvm() // existing JVM target iosArm64() // the new platform that justified the switch sourceSets { // old src/main/kotlin moves under jvmMain (or commonMain if shareable) } } ``` You move `src/main/kotlin` into `jvmMain` (or lift the shareable parts to `commonMain`), add the new target, and resolve any JVM-only API usage in the shared layer. Because the switch is cheap when justified and costly when premature, the system-design answer is: **adopt KMP for demonstrated multiplatform sharing, not as insurance.** ## Interview framing A strong answer separates *capability* (KMP can target many platforms) from *cost* (build/CI/library/publishing overhead) and lands on a YAGNI-style recommendation, while noting the migration path keeps the decision reversible.
- If you start with `kotlin("jvm")` and later need iOS, how hard is the migration?Mechanical, not a rewrite: switch the plugin to `kotlin("multiplatform")`, add the targets, move `src/main/kotlin` into `jvmMain` (lifting shareable code to `commonMain`), and fix any JVM-only API used in the shared layer. The reversibility of the decision is why speculative KMP adoption is discouraged.
- Why can adopting KMP restrict your library choices?Code in `commonMain` must compile for every target, so it can only use multiplatform-ready libraries. JVM-only dependencies are still usable but only inside `jvmMain`, not in shared code — which can force you to find or write multiplatform alternatives for the common layer.
Choosing KMP for a JVM-only library is like buying a moving truck for a daily commute — capable of much more, but you pay the size and fuel cost every trip for capacity you don't use.
saying these in an interview costs you the question
- Recommending KMP for a JVM-only library 'just in case'.
- Ignoring the CI/host-matrix and build-time costs.
- Claiming `commonMain` can freely use any JVM library.