Published on June 7, 2025

Managing dependencies is a critical part of software development, especially when working with modular projects. In this article, we’ll explore how to publish Kotlin libraries locally using Maven and Gradle, and tackle dependency version conflicts with practical examples. Let’s dive in!
Before deploying code to public repositories like Maven Central, developers often test their libraries locally. This allows for rapid iteration without cluttering remote repositories.
Another use case? Note that you can’t directly implement .aar file dependency in your project as your IDE will yell at you. So, the solution? Publish it locally and then implement it. Similarly, you may have your own use cases to do this.
Now, Let’s see how this works with a real-world scenario.
Imagine you’re building three Kotlin modules:
Module A: Provides a utility function.
Module B: Depends on Module A.
Module C: Depends on Module B (and indirectly on A).
Here’s Abc.kt in Module A:
package com.example.test_maven_local.a
class Abc {
fun first(): String {
return "Hello from first"
}
}
To publish it locally, configure build.gradle.kts:
// 1. Apply necessary plugins
plugins {
id("maven-publish") // Adds tasks to publish artifacts to Maven repositories
`java-library` // Provides Java compilation, testing, and packaging capabilities
kotlin("jvm") version "1.9.0" // Kotlin JVM support (ensure version matches your project's Kotlin)
}
// 2. Define Maven coordinates for your library
// - Group: Organizational identifier (e.g., your company domain reversed)
// - Version: Release version (follow Semantic Versioning: MAJOR.MINOR.PATCH)
group = "org.example.first"
version = "1.0.0"
// 3. Configure publishing settings
publishing {
publications {
// Create a Maven publication named "maven"
create<MavenPublication>("maven") {
// Package the JAR built from the Java/Kotlin source code
from(components["java"])
// Optional: Customize the artifact ID (defaults to project name)
// artifactId = "my-library"
}
}
repositories {
// Define where to publish the artifact
maven {
// Publish to a local directory named "repo" in the project root
// For team use, replace with a network path
url = uri(File(projectDir, "repo"))
// Optional: Add credentials for remote repositories
// credentials {
// username = project.findProperty("repoUser") as String?
// password = project.findProperty("repoPassword") as String?
// }
}
}
}
Now run ./gradlew publish. This generates a local Maven repository in the repo directory.
After publishing Module A, you can reference it in Module B:
package com.example.test_maven_local.b
import com.example.test_maven_local.a.Abc // Import from the published JAR
class B {
fun getValueFromA() = Abc().first()
}
In Module B’s build.gradle.kts, add and sync:
dependencies {
implementation("org.example.first:app:1.0.0") // Published version
}
Key Insight: Even if you delete the source code of Abc.kt from Module A, Module B will still work because it now uses the compiled JAR from your local repository.
What happens if you publish a new version of Module A (e.g., 1.0.1) with breaking changes?
class Abc {
// The function `first()` is removed!
fun newFunction(): String {
return "New functionality"
}
}
If Module B still depends on 1.0.0 but Module C wants 1.0.1, conflicts arise:
package com.example.test_maven_local.c
import com.example.test_maven_local.b.B
class C {
fun test(): String {
return B().getValueFromA() // B uses Abc 1.0.0, but C might want 1.0.1
}
}
Gradle’s resolution strategy lets you enforce specific dependency versions. For example:
allprojects {
configurations.configureEach {
resolutionStrategy {
force("org.example.first:app:1.0.0") // Force older version
}
}
}
But this isn’t foolproof! If Module C requires Abc 1.0.1 (which lacks first()), the app will crash at runtime.
- Semantic Versioning: Increment versions correctly: MAJOR for breaking changes,MINOR for backward-compatible features,PATCH for bug fixes.
MINOR for backward-compatible features.
PATCH for bug fixes.
- Avoid Breaking Changes: If Module A must remove first(), create a new method instead of deleting the old one. - Use Dependency Alignment: Ensure all modules depend on the same library version. - Local Publishing Workflow: Test changes locally before pushing to remote repos. Use a structured directory (e.g., repo/) for local artifacts.
The process is similar for Android! Replace java-library with com.android.library in Gradle and publish .aar files instead of .jar.
Local Maven publishing accelerates development but demands careful version management. By understanding resolution strategies and semantic versioning, you can avoid dependency hell.
Try it yourself:
Found this helpful? Consider following, clapping, and sharing your thoughts! Also, Don’t forget to like and comment your thoughts.