skip to content

Explicit API mode and the Binary Compatibility Validator both guard a library's API. How do they differ, and why might you use both?

level: seniorimportance: should knowfreq 22%

answer

  1. Explicit API = compile-time, per-declaration discipline
  2. BCV = baseline diff of the public ABI (.api file)
  3. BCV tasks: apiDump (write) / apiCheck (verify)
  4. Together: intentional now + no drift across versions
  5. Neither replaces SemVer / @Deprecated

basics

~20 s

Explicit API mode works while you type — it forces you to declare what's public and its type. Binary Compatibility Validator works at review time — it diffs a saved snapshot of your public API so accidental breaking changes show up. They cover different moments.

solid answer

~40 s

They operate at different stages. Explicit API mode is a compile-time author discipline: it forces explicit visibility and return types on public/protected declarations so the surface is intentional as you write it. It does not track changes over time. Binary Compatibility Validator (BCV, the binary-compatibility-validator Gradle plugin) generates a textual dump of your library's public ABI (an .api file) checked into version control; its apiCheck task fails the build when the current dump diverges from the committed one, and apiDump regenerates it. So BCV catches accidental additions or breaking removals/signature changes between versions, but only as a diff against a baseline. Together: explicit API mode ensures each declaration is deliberate at authoring time; BCV ensures the aggregate surface doesn't drift without an explicit, reviewed update. Neither replaces semantic versioning or deprecation policy.

code

kotlin · 7 lines
kotlin
// authoring discipline
kotlin { explicitApi() }

// drift detection (settings/plugins block)
plugins { id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.x" }
// ./gradlew apiDump   -> regenerate api/*.api
// ./gradlew apiCheck  -> CI fails if live ABI != committed .api

go deeper

for a junior

Knows both relate to the public API but may blur the distinction.

for a middle

States explicit API mode is compile-time and BCV is a baseline diff, names apiDump/apiCheck.

for a senior

Explains the complementary roles, the committed .api review workflow, and that neither owns SemVer.

for a principal

Designs the full API-governance pipeline (explicit API + BCV + deprecation policy + SemVer + CI gates) across many modules and release trains.

## Two tools, two moments | Aspect | **Explicit API mode** | **Binary Compatibility Validator (BCV)** | |--------|----------------------|------------------------------------------| | Stage | **Compile time** | **Build/CI check against a baseline** | | What it enforces | Each public/protected member has explicit **visibility + return type** | The **set** of public ABI symbols matches a committed snapshot | | Form | Compiler mode (`explicitApi()` / `-Xexplicit-api`) | Gradle plugin producing an **`.api` dump** file | | Catches | Accidental exposure / inferred types in a single declaration | Accidental **additions, removals, or signature changes** across versions | | State kept | None — stateless per compile | A **baseline file** in VCS | ## Explicit API mode (recap) Forces you, as you write, to choose `public`/`internal`/`private` and to declare return types. It makes a single declaration intentional but knows nothing about the previous release. ## Binary Compatibility Validator BCV is the `org.jetbrains.kotlinx.binary-compatibility-validator` Gradle plugin. It adds tasks: - **`apiDump`** — writes the current public ABI to `api/<module>.api`. - **`apiCheck`** — fails if the live ABI differs from the committed `.api` file (typically wired into `check`/CI). ```text # api/mylib.api (committed, human-reviewable) public final class com/acme/Parser { public fun parse (Ljava/lang/String;)Lcom/acme/Result; } ``` When a PR changes the public surface, `apiCheck` fails until the author runs `apiDump` and **commits** the new file — making every API change a **reviewed, explicit diff**. ## Why use both - **Explicit API mode** = no symbol is *accidentally* public or *accidentally* a different inferred type **right now**. - **BCV** = the public surface doesn't *drift* between releases without a reviewed `.api` change. They are complementary, not redundant. Example flow: ```kotlin kotlin { explicitApi() } // author-time discipline // + apply binary-compatibility-validator plugin -> apiCheck in CI ``` ## What neither does Neither tool decides **semantic** compatibility, manages **deprecation cycles**, or bumps your version number. They are guardrails that surface change; humans and a versioning policy (SemVer, `@Deprecated` with `DeprecationLevel`) still own the decision.

  • If BCV already diffs the API, why bother with explicit API mode?
    BCV only tells you the surface changed; explicit API mode prevents the accidental change at the source and makes each declaration's visibility/type a deliberate choice as you write.
  • What does a developer do when apiCheck fails legitimately?
    Run apiDump to regenerate the .api file and commit it, so the API change is reviewed in the diff.

Explicit API mode is spell-check as you type; BCV is diffing the whole document against the last approved version.

saying these in an interview costs you the question

  • Saying they're the same tool or redundant
  • Thinking explicit API mode tracks changes across versions
  • Believing BCV enforces explicit return types in source
  • Claiming either tool bumps the version or manages deprecation
  • Not knowing BCV stores a committed .api baseline

context