skip to content

Inside commonMain.dependencies { }, when would you choose api(...) over implementation(...), and how does the choice propagate across targets and to consumers of your KMP library?

level: middleimportance: should knowfreq 40%

answer

  1. api = type in public signature
  2. implementation = internal, default
  3. compileOnly / runtimeOnly also available
  4. per source set choice
  5. Module Metadata carries it to consumers

basics

~20 s

Use api when types from the dependency appear in your public API so consumers need it too. Use implementation when the dependency is internal. The choice works the same per source set as in plain Gradle.

solid answer

~40 s

The dependency configurations inside a KMP source set's dependencies { } block are the same Gradle Java/Kotlin configurations: implementation, api, compileOnly, runtimeOnly. api exposes the dependency transitively — if a public function in commonMain returns or accepts a type from that library, declare it with api so downstream consumers of your published KMP library get it on their compile classpath. implementation keeps it internal: it's on your compile/runtime classpath but not leaked to consumers, which speeds incremental builds and reduces coupling. Because dependencies are per source set, you make this choice independently for commonMain, jvmMain, etc. A common mistake is using api everywhere; prefer implementation by default and reserve api for genuine public-API leakage. For KMP libraries this matters across all targets because the published Gradle Module Metadata carries the api/implementation distinction per variant.

go deeper

for a junior

Knows implementation is the default and api exposes a dependency to consumers.

for a middle

Applies the 'type in public signature -> api' rule and knows the choice is per source set.

for a senior

Connects api/implementation to incremental-build cost and encapsulation across targets.

for a principal

Reasons about published Module Metadata variants and sets org-wide conventions to minimize api leakage in a library ecosystem.

## The configurations are standard Gradle Inside any source set's `dependencies { }` you have the usual Java/Kotlin plugin configurations: - `implementation(...)` — on your compile and runtime classpath, **not** exposed to consumers transitively. - `api(...)` — same, **plus** exposed transitively to anyone who depends on your module. - `compileOnly(...)` — compile only, not packaged/at runtime (e.g. annotations). - `runtimeOnly(...)` — runtime only, not on the compile classpath. KMP just lets you set them **per source set**. ## api vs implementation — the rule Declare a dependency with `api` **only if its types appear in your module's public API** — return types, parameters, supertypes, public properties of exported declarations. Otherwise use `implementation`. ```kotlin kotlin { sourceSets { commonMain.dependencies { // kotlinx-datetime types appear in our public function signatures -> api api("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1") // coroutines used only internally -> implementation implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0") } } } ``` ```kotlin // In commonMain source: leaks kotlinx-datetime, so it must be 'api' fun nextSlot(after: kotlinx.datetime.Instant): kotlinx.datetime.Instant = TODO() ``` ## Why it matters - **Build performance:** `implementation` dependencies don't leak, so changing them doesn't recompile consumers; over-using `api` causes excessive recompilation and a bloated public surface. - **Encapsulation:** `implementation` hides your internal choices, letting you swap libraries without breaking consumers. - **Per target:** since you declare deps per source set, a type might be public in `commonMain` (→ `api`) while a JVM-only helper stays `implementation` in `jvmMain`. ## Publishing angle When you publish a KMP library, Gradle Module Metadata records the `api`/`implementation` distinction for each platform variant, so consumers on each target inherit exactly the transitive `api` deps. Getting this wrong silently forces consumers to add missing deps (under-`api`) or pollutes their graph (over-`api`).

  • Does over-using api hurt anything beyond a larger public surface?
    Yes — transitive leakage causes consumers to recompile when those deps change and couples them to your internal choices, hurting incremental build performance.
  • If a datetime type only appears in a jvmMain public function, where do you put api?
    In jvmMain.dependencies as api; the leakage is platform-specific, so the api configuration is scoped to that source set.

api is putting an ingredient on the menu so guests must stock it too; implementation keeps it in your private fridge.

saying these in an interview costs you the question

  • Using api for everything 'to be safe'
  • Claiming KMP has special dependency configurations different from Gradle's
  • Not knowing api leaks transitively to consumers
  • Thinking the api/implementation choice can't differ between source sets

context