Skip to content
Go back

🎄 A Christmas Checklist for Kotlin Multiplatform Projects

🔒 Exclusive on KMP Bits
by KMP Bits

KMP Bits Cover

What Is Really Worth Sharing


🎅 Introduction

End of year always has a very specific feeling. Projects slow down a bit, pull requests get smaller, and many of us start reviewing what we built over the past months. It is also the time where we look at our Kotlin Multiplatform modules and ask ourselves a very honest question, did we actually share the right things, or did we just share more things.

Kotlin Multiplatform is about sharing, but sharing blindly is like buying random Christmas gifts at the last minute. Some will be useful, some will not, and a few will quietly become technical debt before the new year even starts.

This article is a practical checklist. No theory, no evangelism. Just a clear guide to what usually belongs in the shared module, what might belong there, and what almost never should.


🎁 1. The Gift List, What Should Live in the Shared Module

These are the gifts that almost always fit. They are platform agnostic by nature and benefit the most from being shared.

Business logic and use cases

Anything that represents a decision, a rule, or a flow of data belongs here. Use cases are usually the first thing I move to the shared module.

class LoadUserProfileUseCase(
    private val repository: UserRepository
) {
    suspend operator fun invoke(userId: String): UserProfile {
        return repository.loadProfile(userId)
    }
}

This code does not care if it runs on Android or iOS. That is exactly why it is a perfect gift.

Domain models and validation rules

Data models, sealed states, and validation logic are another strong candidate. They define what your app is, not how it renders.

data class Email(val value: String) {
    init {
        require(value.contains("@")) { "Invalid email" }
    }
}

Networking and repositories

Shared networking logic keeps behavior consistent and avoids subtle differences between platforms.

This is where libraries like Ktor or your own abstractions shine, especially when combined with Flows or suspend functions.

Shared state holders

State machines, reducers, or shared ViewModels that expose immutable state are excellent candidates, as long as they do not depend on platform lifecycles.


🤔 2. The “Maybe” Gifts, Think Twice Before Sharing

These are the gifts that look great in the store but require a bit more thought.

Date and time handling

Date handling usually fails because platforms disagree on locales or time zone databases. To fix this, you have to be disciplined about where the “math” happens versus where the “formatting” happens.

Serialization details

Serialization is a “maybe” because a small change in a JSON key can crash one platform while the other handles it fine.

Concurrency assumptions

Kotlin Coroutines are great, but the way Swift consumes them can be messy. It’s a “maybe” because of the friction it creates for iOS developers.

Platform APIs behind interfaces

Over-abstracting a platform API (like Bluetooth or Camera) is a recipe for a maintenance nightmare.


🚫 3. The Naughty List, What You Should Not Share

This is where many KMP projects go wrong.

UI code

Compose, SwiftUI, UIKit, XML layouts. None of this belongs in shared code when you are using Kotlin Multiplatform with native UIs.

If you are using Compose Multiplatform to share UI across platforms, this rule obviously changes. In that case, UI is part of the shared strategy by design.

The key distinction is intent. When platforms are expected to express themselves independently, UI should remain platform specific. The UI is where platforms should be free to express themselves.

Navigation is deeply tied to platform conventions. Trying to unify it usually creates more friction than value.

Lifecycle handling

Android lifecycles and iOS lifecycles are fundamentally different. Abstracting them rarely pays off.

Over abstracted platform wrappers

If you need five interfaces and three layers just to call a system API, it is probably not worth sharing.


🎀 4. Wrapping Presents Properly, Using expect and actual

expect and actual are powerful, but they should be used with restraint.

A good use case is when the shared code needs a capability, not an implementation.

expect class PlatformLogger {
    fun log(message: String)
}
actual class PlatformLogger {
    actual fun log(message: String) {
        println(message)
    }
}

The key rule is simple. Keep the expected surface small and focused. If the abstraction starts leaking platform details, it is time to reconsider.


🧹 5. The End of Year Cleanup, Reviewing Your Shared Code

End of year is the perfect moment to audit your shared module.

Ask yourself:

Removing code from shared can be just as valuable as adding new shared code.


⭐ 6. A Simple Rule of Thumb for the Next Year

If a piece of code represents a business rule, a decision, or a state, it probably belongs in shared.

If it represents a user interaction, a visual concern, or a platform behavior, it probably does not.

This rule is not perfect, but it will save you from many painful refactors.


🎄 Conclusion

Kotlin Multiplatform is not about sharing everything. It is about sharing the right things.

A small, boring, predictable shared module is usually a sign of a healthy KMP project. As the year ends, take the time to clean it up, remove what no longer belongs there, and prepare it for the next cycle.

Do that, and your future self will probably thank you, sometime after the holidays, when things start moving fast again.


📱 New: The KMP Bits App

If you want an easier way to read articles like this, I just released the KMP Bits mobile app, built entirely with Kotlin Multiplatform.
This kind of more personal, experience-based content is exactly what I like to share first on KMP Bits.

The app makes reading and navigating content much faster, and it is the best way to stay up to date with new posts.

You can follow along directly in the app:

➡️ App Store
➡️ Google Play

If you enjoyed this article, thank you for reading. I hope this checklist helps you make better KMP decisions in your own projects.

If you want to keep following along:


Share this post on:

Next Post
Testing Jetpack Compose UI on the JVM: The Discovery That Changed My Workflow