KMP Gradle Convention: What It Is, How It Works, and Why It Matters



This content originally appeared on Level Up Coding – Medium and was authored by Andrew Malitchuk

Photo by Mikael Kristenson on Unsplash

Gradle files in large Kotlin Multiplatform projects can quickly become a copy-paste nightmare. The more modules there are, the harder it becomes to maintain a consistent style, structure, and avoid chaos.

Solutions that initially seemed temporary and harmless eventually turn into something like:

“don’t touch it — it’s working.”

These code fragments are not easy to change because there’s a high chance something will break. And this doesn’t only apply to business logic but also to infrastructure-related things.

One of the most painful topics in such projects often becomes Gradle:

  • copy-pasting in build.gradle files;
  • complex custom settings that are hard to understand;
  • magical instructions somewhere in README.md;
  • nearly impossible refactoring.

All of this makes project maintenance exhausting. However, this can be fought against.

One proven method is using Gradle Convention Plugins.

They allow us to:

  • centralize common settings in one place;
  • get rid of excessive boilerplate code;
  • create what’s called SSOT (Single Source of Truth).

This significantly simplifies the scaling of the project, especially in the case of Kotlin Multiplatform. Moreover, this approach is effective not only for KMP but also for Android or any other native development.

It helps maintain clean code, improves onboarding for new developers, and makes life easier for the entire team.

In Kotlin Multiplatform, just like in traditional native Android development, Gradle remains the basic tool for building a project.

Interestingly, a significant portion of KMP projects I encounter in open-source is built as a single-module solution. Typically, there is a single shared module where developers add everything — from the data layer to the presentation.

This may seem convenient at the start, but in the long run, this approach complicates the maintenance and development of the project. We’re essentially reverting to the approaches of the 2010s, when projects were single-module and every change required a full rebuild.

This not only slows down the development process but also reduces the overall productivity of the team.

That’s why now, more than ever, the need for:

  • Clean architecture.
  • Proper modularization is taking center stage.

Despite the abundance of articles on Medium and other platforms, the core principles remain unchanged:

  • Separation of concerns.
  • Clear structure.
  • Easy maintainability.

A multi-module structure has a number of advantages:

  • breaking the project into independent parts;
  • faster builds through parallel execution and caching;
  • fewer merge conflicts;
  • clear separation of abstraction from implementation.

All of this creates favorable conditions for building a true Clean Architecture, where each module has its clearly defined role and boundaries of responsibility.

Gradle Convention Plugins — A Key Tool for Standardizing Configuration in Large Projects

Gradle convention plugins are one of the key tools for standardizing configuration in large projects.

They help to:

  • avoid duplicating settings in build.gradle files;
  • make the project cleaner and more understandable;
  • simplify code maintenance;
  • ensure predictable scalability.

Plugins in Gradle

Gradle plugins are an extension mechanism that allows you to:

  • automate repetitive tasks;
  • add specific functionality.

There are two main types of plugins:

  1. Script Plugins
  • created directly in build.gradle(.kts) ;
  • contain Groovy, Java, or Kotlin code;
  • suitable for simple scenarios;
  • convenient at the start but hard to scale;
  • during the build process, they are still transformed into compiled code.

2. Compiled Plugins

  • written in Groovy, Java, Kotlin, or another JVM language;
  • implement the Plugin<Project> interface;
  • allow creating complex build logic;
  • ideal for large teams and projects.

Convention Plugins

Convention plugins are a way to extract shared logic from each module into a single centralized plugin.

Advantages:

  • configure once, reuse many times;
  • no copy-pasting (or minimal);
  • reduce the number of human errors;
  • analogous to OOP: shared logic is in the “superclass,” individual modules are “subclasses”.

What Can Be Extracted into a Convention Plugin?

  • plugin dependencies;
  • dependencies;
  • Android configurations;
  • buildType or flavor configurations;
  • code formatting rules;
  • test configurations;
  • any other configurations.

Examples of Convention Plugin Types

Feature Plugins
For modules that implement UI or are directly tied to the platform.

Library Plugins
For modules with business logic, networking, utilities.
Usually, these are Kotlin/Java modules with minimal dependency on the platform.

Test Plugins
For configuring the testing environment, unit and integration tests.

DI Plugins
For automating dependency injection configuration (e.g., Koin).

In each project, the structure of these plugins may vary — and that’s perfectly fine.

You shouldn’t look for a perfect universal solution. Instead, adapt the approach to fit your team’s needs and architecture.

Gradle in Kotlin Multiplatform vs Android

The main difference between Kotlin Multiplatform (KMP) and standard Android development in the context of build.gradle(.kts) is as follows:

  • In KMP, each platform needs to be configured separately — Android, iOS, desktop (if needed).
  • This provides greater control over which dependencies are used on which platform.
  • Besides the usual dependencies from libs.versions.toml, KMP often uses aliases (e.g., composeDeps) to work with Jetpack Compose in shared modules.

Types of plugins in the project

Our project implements three main types of plugins:

Feature plugin
For modules that have UI and direct access to the platform.

Library plugin
For modules where UI is not needed — for example: data, domain, network. These modules are usually platform-independent.

DI plugin
For automating the configuration of Koin — our main DI system.

What is build-logic?

build-logic is a separate subproject in the repository. Its main goal is to:

  • extract the build logic;
  • standardize project configurations;
  • store convention plugins;
  • contain custom configurations;

This approach allows:

  • avoiding duplication;
  • improving Gradle support;
  • providing a single source of truth for configurations.
Before the appearance of version catalogs, build-logic even contained all the dependency versions.

Structure of build-logic

Here is an example of the structure:

It’s worth mentioning buildSrc, although it’s an outdated approach.

The problem with it is that after any changes, you need to refresh the cache, which can be annoying. buildSrc automatically integrates into the project but doesn't provide enough control.

On the other hand, build-logic needs to be manually included in settings.gradle.kts, but it provides more control and flexibility.

How to set up build-logic

Follow simple steps:

1. Create a separate module for build-logic.

2. Add this line to settings.gradle.kts:

include(":build-logic")

3. After that, Gradle will see the module and can use it.

Implementing plugins in build-logic

In build-logic, we implement our plugins. For configuring iOS and Android modules, we need to specify baseName and namespace.

There are two ways to do this:

  • through DSL configuration in the build.gradle file — i.e., using a custom extension;
  • or, more simply, by taking the module name from project.name and automatically forming the namespace.
This is a bit of a “duct-tape” solution, but it works well if the project structure is simple.

Below is the “single-level” project structure where these convention plugins were implemented.

build-logic in settings.gradle.kts:

You can use gradle.properties from the root of the project

build-logic in build.gradle.kts:

Feature plugin

The Feature plugin includes all the necessary plugins:

  • kotlin.multiplatform;
  • android.library;
  • compose;
  • (and anything else required).

It also adds the main dependencies:

  • kotlinx-coroutines;
  • androidx.lifecycle;
  • compose-runtime, foundation, ui, material.

Additionally, the plugin configures:

  • namespace;
  • defaultConfig;
  • Minimum SDK version for Android (minSdk);
  • baseName for iOS;
  • Desktop plugin (if Compose supports desktop).
N.B. For convenience, all Gradle dependencies are hardcoded to make life easier.

Library plugin

The Library plugin is essentially the same but without UI and without CMP.

In native development, a library is a pure Kotlin/Java module, but in Kotlin Multiplatform, even for such modules, you can configure Android/iOS specific parameters.

DI plugin

The DI plugin is simple:

It adds Koin dependencies:

  • koin-core;
  • koin-compose;
  • koin-viewmodel.

This is enough to have a basic DI system in the project.

Registering plugins

After that, all plugins need to be registered, and then they can be used in modules.

Plugin registration example

Example of usage

Here is an example of using the library-convention-pluginin the data module.

Yet another example of using the feature-convention-plugin/di-convention-plugin in the feature module:

Conclusions

For our KMP project, we made a small refactor of its structure — we moved the standard settings into separate Gradle convention plugins:

  • feature;
  • library;
  • di.

Each of them is responsible for its own group of modules, so now everything looks more organized.

This approach seriously reduced the amount of boilerplate code — the project became easier to comprehend and more pleasant to navigate.

Gradle files — that’s a whole different story:

  • less repetition;
  • more readability;
  • and most importantly — it’s clear who is responsible for what.
Yes, I agree — it’s not a silver bullet. It’s more of a palliative or, if you like, duct tape, but it works. Especially if you don’t have a complex module hierarchy — then everything is fine, and it works painlessly.

I plan to either improve or completely rethink this approach. If you already have a better option — feel free to write. Maybe we can make it even better together.

And yes, if there’s an improved version — I will definitely leave a link below. Stay tuned 👇

LinkedIn

GitHub

YACTT

References


KMP Gradle Convention: What It Is, How It Works, and Why It Matters was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding – Medium and was authored by Andrew Malitchuk