DGDependency Graph
Fieldnotes

ENGINEERING NOTE / 2026-08-18

Keep Kotlin Multiplatform bindings at the platform boundary

Shared code should describe the capability it needs while each target supplies the platform implementation.

Kotlin Multiplatform makes source-set boundaries visible, but a dependency container can blur them again. A shared module should state a capability such as secure storage, HTTP transport, or local time. The platform source set should supply the implementation.

Put contracts where consumers can see them

Define the smallest useful interface in common code. Avoid copying a platform API into that interface. A storage contract might expose read, write, and remove; it does not need to expose Android preferences or an Apple keychain type.

Then keep the platform binding near the platform implementation. This makes three facts easy to review: which targets support the capability, which lifecycle owns it, and which test substitute is available.

JetBrains documents that Multiplatform projects can use common and platform-specific dependencies in separate source sets. The dependency guide is the right source for current Gradle structure. Framework documentation should answer how to bind the implementations; the source-set model should decide where those bindings live.

Avoid a universal module

A single module containing every definition looks convenient until it needs target checks, optional imports, or large test fixtures. Prefer small modules aligned with capabilities and product features. Compose UI definitions, persistence definitions, and remote-service definitions should be includable without starting the complete application.

Test each target’s boundary

Common tests can validate consumer behavior against a fake contract. Platform tests must also prove that the real binding can be constructed and that its lifetime matches the target. Run graph verification for each supported target configuration, not only the JVM configuration that is easiest to execute.

A clean boundary does not require every implementation to be identical. It requires the shared consumer to depend on a stable capability and the build to make each target’s choice explicit.