skip to content

commonMain & commonTest

commonMain holds the code compiled for every target and commonTest the shared tests written against kotlin.test. Both can only depend on multiplatform-safe libraries, which is the constraint that shapes your architecture.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

In a Kotlin Multiplatform project, what is the `commonMain` source set and what kind of code belongs in it?

level: juniorimportance: must knowfreq 70%

answer

  1. Root production source set, compiled per target
  2. Only stdlib + multiplatform-safe deps
  3. No java.*/android.*/platform.* directly
  4. expect/actual or interfaces for platform code
  5. src/commonMain/kotlin

basics

~10 s

commonMain holds shared code that compiles to every target (JVM, iOS, JS, etc.). Put platform-independent logic there. It can only use APIs available on all targets.

solid answer

~40 s

`commonMain` is the root production source set of a Kotlin Multiplatform module. Code placed there is compiled separately for every configured target, so it must only reference APIs and libraries available on all of them — the Kotlin standard library plus multiplatform-safe dependencies. You write shared business logic, data models, networking/serialization wiring, and `expect` declarations here. Platform-specific pieces live in target source sets like `jvmMain` or `iosMain` (often as `actual` implementations). Because `commonMain` cannot see `java.*`, `android.*`, or `platform.Foundation.*`, anything platform-bound must be abstracted behind an interface or an `expect`/`actual` pair. It is configured in `build.gradle.kts` via the `kotlin { sourceSets { commonMain { ... } } }` block.

code

kotlin · 6 lines
kotlin
// src/commonMain/kotlin/Greeting.kt
expect fun platformName(): String

class Greeting {
    fun greet(): String = "Hello from ${platformName()}"
}

go deeper

for a junior

Knows commonMain holds shared code and only multiplatform APIs are allowed.

for a middle

Explains compilation per target and the expect/actual escape hatch with correct directory layout.

for a senior

Discusses dependency safety (artifacts must exist for every target) and abstraction strategies for platform code.

for a principal

Frames common-code design as an architecture decision: maximizing shared surface while isolating platform concerns behind stable interfaces.

## What `commonMain` is A Kotlin Multiplatform (KMP) module organizes code into **source sets**. `commonMain` is the root *production* source set: every Kotlin file under `src/commonMain/kotlin` is compiled **once per target** that the module declares (e.g. `jvm()`, `iosArm64()`, `js()`, `linuxX64()`). The same source therefore produces a JVM `.class`, an iOS Kotlin/Native binary, a JS module, and so on. ## The hard constraint: lowest common denominator Because that one file must compile for *all* targets, `commonMain` may only reference: - The **Kotlin standard library** (`kotlin.*`, `kotlin.collections.*`, `kotlin.time.*`, `kotlin.coroutines` if added, etc.) — these are multiplatform. - **Multiplatform-safe libraries** — ones that publish artifacts for every target you use (kotlinx.coroutines, kotlinx.serialization, Ktor client, etc.). It **cannot** reference platform-only APIs: no `java.io.File`, no `android.content.Context`, no `platform.UIKit.*`. Those only exist in their respective target source sets. ## Where platform-specific code goes Two escape hatches let common code reach platform APIs: - **Interfaces / abstractions** — declare an interface in `commonMain`, implement it per platform. - **`expect`/`actual`** — declare `expect fun currentTimeMillis(): Long` in `commonMain`; provide an `actual` in each target source set. ```kotlin // src/commonMain/kotlin/Platform.kt expect fun platformName(): String class Greeting { fun greet(): String = "Hello, ${platformName()}!" } ``` ```kotlin // src/jvmMain/kotlin/Platform.kt actual fun platformName(): String = "JVM ${System.getProperty("java.version")}" ``` ## Gradle configuration `commonMain` and its dependencies are declared in the `kotlin {}` DSL: ```kotlin kotlin { jvm() iosArm64() sourceSets { commonMain.dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0") } } } ``` ## Key takeaways - `commonMain` = shared, compiled-to-every-target code. - Only stdlib + multiplatform-safe deps allowed. - Reach platform APIs via interfaces or `expect`/`actual`, never directly.

  • Why can't you use `java.io.File` directly in `commonMain`?
    `java.io.File` is part of the JVM/Android platform only; iOS, JS, and Native targets have no JVM, so the code wouldn't compile for them. Common code is limited to APIs present on every target.
  • How does common code call platform-specific functionality?
    Via an `expect` declaration with per-target `actual` implementations, or by declaring an interface in common and injecting a platform implementation.

commonMain is like writing in a language every team member speaks — you can only use words (APIs) everyone understands; specialized jargon goes in per-team handbooks (target source sets).

saying these in an interview costs you the question

  • Thinking `commonMain` can use Java standard library classes
  • Believing common code is compiled only once for all targets
  • Confusing `commonMain` with a separate compiled JAR shared at runtime
  • Not knowing about `expect`/`actual` as the escape hatch
  • Claiming any Maven dependency can be added to `commonMain`

context

open as a page

How do you write shared tests for a KMP module, and what role does `commonTest` and the `kotlin.test` library play?

level: middleimportance: must knowfreq 55%

basics

~10 s

Put shared tests in commonTest and use the kotlin.test library. Those tests run once per target, so a single test verifies behavior on JVM, iOS, JS, etc.

open as a page

What makes a dependency "multiplatform-safe" so it can be added to `commonMain`, and what happens if you add one that isn't?

level: middleimportance: should knowfreq 45%

basics

~10 s

A multiplatform-safe library publishes versions for every target your module uses. If you add a JVM-only library to commonMain, the build fails for non-JVM targets because there's nothing to compile against.

open as a page

How do `expect` declarations in `commonMain` relate to `actual` implementations, and what are the rules and pitfalls?

level: seniorimportance: should knowfreq 40%

basics

~10 s

In commonMain you write expect declarations — promises of an API. Each target supplies a matching actual. The compiler checks every target provides one; common code calls the expected API without knowing the platform.

open as a page

When designing a KMP library, how do you decide what belongs in `commonMain` versus platform source sets, and what are the trade-offs of pushing more code into common?

level: principalimportance: nice to knowfreq 25%

basics

~10 s

Put as much platform-independent logic in commonMain as possible to maximize reuse and shared tests, but keep platform APIs out. Over-sharing forces awkward abstractions; under-sharing duplicates logic.

open as a page