# binary-compatibility-validator: locking down a Kotlin library's public API

> The Kotlin binary compatibility validator is a Gradle plugin that dumps a library's public binary API into a file, commits it, and then fails the build whenever a change would break the people who already compiled against the old version. It is in maintenance mode, because the same idea is being folded into the Kotlin Gradle plugin itself.

**Kotlin/binary-compatibility-validator** — Public API management tool

- Repository: https://github.com/Kotlin/binary-compatibility-validator
- Stars: 967 · Forks: 67
- Language: Kotlin
- License: Apache-2.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/kotlin-binary-compatibility-validator

## What the tool checks, and for whom

The description is compact and it defines the whole product. The tool allows dumping the binary API of the JVM part of a Kotlin library that is public in the sense of Kotlin visibilities, and ensures that the public binary API was not changed in a way that makes the change binary incompatible.

Every word in that sentence is load bearing. The dump is of the binary API, not of the source API, so it describes what is actually in the class files rather than what a reader might infer from the source. The scope is the JVM part of a library, which matters for a multiplatform project where the other targets have their own rules. And the definition of public is Kotlin's visibilities, which is a language-level concept rather than a Java one, so an internal declaration in Kotlin is not part of the dump even though the compiler might emit something for it.

The problem it solves is the classic library one. You publish a version, somebody compiles against it, and then you change a method signature. Their compiled code links against a method that no longer exists, and the failure appears at run time, in their application, not in your build. A tool that catches that at the point of the change is the difference between a version bump and a broken dependency for every consumer.

The repository description is shorter still, calling it a public API management tool, which is the right summary of the workflow it enables.

## Maintenance mode, and where the feature work went

The readme opens with an important notice, and it is the first thing a reader should see.

The plugin is in maintenance mode. Fixes for critical bugs and support for current versions of Kotlin continue to be delivered. The implementation of new features has been stopped, in favour of adding them to the experimental binary compatibility validation in the Kotlin Gradle plugin.

That is a specific and useful kind of deprecation notice. It is not abandonment, because critical fixes keep coming and the newest release in the history is from September 2026, but the feature work is going somewhere else, and that somewhere else is the Kotlin Gradle plugin itself.

The badge at the top of the readme reflects the same status, marking the plugin as alpha, and there is a JetBrains official project badge, so this is a first party tool from the Kotlin organisation rather than a community project.

For anyone deciding what to install, the practical question is which one will still be there. A standalone plugin that is in maintenance mode will keep working and will keep being patched for the Kotlin versions it supports, but a new capability, a new Kotlin target or a new Gradle feature will land in the Kotlin Gradle plugin first. The migration path, when it comes, will presumably be a configuration change and a different task name, and the readme is at least clear about the direction of travel.

## Setup, and the two tasks that do the work

The plugin is added in the ordinary Gradle way, in either the Kotlin or the Groovy build script.

```kotlin
plugins {
    id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.18.2"
}
```

One instruction that is easy to miss follows: it is enough to apply the plugin only to the root project build file, and all sub-projects will be configured automatically. That is a deliberate choice, because the alternative is adding the plugin to every module in a large project and having them disagree about the settings.

The plugin provides two tasks. The dump task builds the project and dumps its public API into a subfolder named api in the project, in a human readable format, overwriting any existing dump. The check task builds the project and checks that the public API matches the golden value in that same subfolder.

The check task is inserted automatically into the check pipeline, so both build and check start validating the public API as soon as you apply the plugin. That is the whole design in one sentence: there is no separate step to remember, and a breaking change fails the build in the same place your tests fail.

For a project with multiple JVM targets, multiple subfolders are created, one per target, with names such as api/jvm and api/android. So a multiplatform library gets one golden file per target, and the check compares the right one to the right build output.

## The DSL, which is mostly about what to leave out

The optional parameters section shows the same configuration in two languages, and the list is dominated by exclusions.

There are packages excluded from the dumps even when they contain public API, and sub-projects excluded from validation entirely. There are fully qualified classes excluded, with the example being a generated build configuration class, which is the right thing to exclude because its contents change every build. And there is a set of annotations that exclude API from being public, typically the various internal API annotations that mark something effectively private for technical reasons when it cannot actually be private.

```kotlin
ignoredPackages.add("kotlinx.coroutines.internal")
```

```groovy
    ignoredPackages += ["kotlinx.coroutines.internal"]
```

The non-public markers are the most interesting of the four, because they are the escape hatch that keeps the plugin usable in a codebase with a real internal API convention. Without it, every library that exposes an internal annotation would have to exclude whole packages.

There are also two switches that are about the mechanism rather than the content. One disables the validator programmatically, and the other sets the path of the subdirectory where the dumps are stored, so a project that does not want a directory called api at the root can point it somewhere else.

```kotlin
apiDumpDirectory = "api"
```

Both languages are documented for everything, which is the kind of attention to detail that saves people from writing the DSL in the wrong file.

## The jar case, where class files and artefacts diverge

There is a section on producing a dump of a jar, and it is the subtlest part of the documentation.

By default the validator analyses the project output class files from the build classes directory when it builds an API dump. If you pack those classes into an output jar in an irregular way, for example by excluding certain classes or by applying the shadow plugin, then the dump built from the original class files may no longer reflect the resulting jar contents accurately.

In that case the readme says it makes sense to use the resulting jar as the input of the build task, and shows how, by pointing the task at the archive file of the jar task, with a comment about the name differing in a multiplatform project and about the shadow plugin creating a second task with a different name.

```kotlin
        inputJar.value(jar.flatMap { it.archiveFile })
```

The point is worth internalising. The compatibility contract of a library is the artefact consumers link against, not the set of source files you happen to compile. A shaded jar that drops or relocates classes has a different binary surface, and validating the wrong one gives you a false all-clear.

This is the kind of problem that only appears once a project adopts the plugin, which is why it is in the documentation rather than in a changelog.

## The workflow, and what it asks of you

The workflow section describes a one-time setup and then a loop, and it is worth following exactly because the first phase has a manual step in it.

The preparation phase is a one-time action. Apply the plugin, configure it, and execute the dump task. Then validate your public API manually, which means reading the generated file and checking that it is what you meant to publish. Then commit the api files to your version control system. From that moment on, the default check task validates the public API along with the test run, and fails the build if the API differs.

The manual validation step is the one people skip, and it is the one that matters. On the first dump, you discover what your library has been exposing without meaning to, and the only moment you can fix that is before the file becomes the golden reference.

The regular workflow is then very short. For code changes that do not imply any changes to the public API, no additional actions should be performed, because the check task on your continuous integration will validate everything. And for changes that do alter the API, the dump is regenerated, the difference is reviewed, and the file is committed.

That last step is the point of the whole arrangement. The api file is a review artefact as much as a test fixture, and a pull request that changes it shows up in the diff as an explicit statement of what changed in the contract.

## Requirements, licence and the state of the tree

The requirements are modest and worth checking before you plan around the tool.

The plugin requires Gradle 6.1.1 or newer, and Kotlin version 1.6.20 or newer. The project is Apache 2.0 licensed, published to Maven Central, and it is a JetBrains official project with an API reference generated from KDoc.

The release history is short and shows the maintenance mode in practice. Version 0.18.0 came in June 2025, 0.18.1 in July, and 0.18.2 on 2026-09-02, with the last push on the same day and the repository not archived. So there is activity, but it is patch level activity on a line that is not going to gain features.

The repository layout is a Gradle project with a wrapper, a build logic directory, a source directory, a documentation directory, and an api directory at the top level, which is where the golden files live for the project itself. There is a module document, a code of conduct and a contributing guide.

So the tool is small, well made, and in the middle of a planned handover. That combination usually means it will keep working for the projects already using it, and that new projects should look at the experimental validation in the Kotlin Gradle plugin first.

## Conclusion

The validator suits a library team that ships binary artefacts to consumers who cannot upgrade in lockstep, because it turns an invisible promise about compatibility into a build failure. It is a poor fit for an application, where the compiler catches everything, and a poor fit for a new project, since there is no previous version to be compatible with. Before you adopt it, read the maintenance notice and look at the experimental validation in the Kotlin Gradle plugin, because the plugin you install today should be the one you will still be able to install in two years, and check how your library is packaged, since the dump of class files and the dump of a shaded jar are not the same thing.

## FAQ

### What does the Kotlin binary compatibility validator do?

It dumps the binary API of the JVM part of a Kotlin library that is public in the sense of Kotlin visibilities, and it checks that the public binary API has not changed in a way that is binary incompatible. The check task is inserted into the check pipeline automatically, so a breaking change fails the build alongside the tests.

### Is the binary compatibility validator plugin still maintained?

It is in maintenance mode. Fixes for critical bugs and support for current versions of Kotlin continue to be delivered, but the implementation of new features has been stopped in favour of adding them to the experimental binary compatibility validation in the Kotlin Gradle plugin.

### How do I set up the binary compatibility validator in a Gradle build?

Apply the plugin in the root project build file, in either the Kotlin or the Groovy script, and it configures all sub-projects automatically. The plugin provides a task that builds the project and dumps its public API into an api subfolder, and a task that checks the current API against that dump, which is wired into the check pipeline.

### What should I do if my library is shaded into a single jar?

Point the build task at the resulting jar instead of letting the validator analyse the class files directory, because a shaded jar that excludes or relocates classes has a different binary surface from the original class files, and validating the wrong one gives a false all-clear.

## Sources

- [Issues](https://github.com/Kotlin/binary-compatibility-validator/issues)
- [Kotlin/binary-compatibility-validator on GitHub](https://github.com/Kotlin/binary-compatibility-validator)
- [License: Apache-2.0](https://github.com/Kotlin/binary-compatibility-validator/blob/master/LICENSE)
- [README](https://github.com/Kotlin/binary-compatibility-validator/blob/master/README.md)
- [Releases](https://github.com/Kotlin/binary-compatibility-validator/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/kotlin-binary-compatibility-validator
