What does the @ExperimentalSerializationApi status of ProtoBuf/CBOR mean in practice, and how do you handle it in production code?
answer
- @ExperimentalSerializationApi = no binary-compat guarantee + opt-in
- Opt in via @OptIn or compiler -opt-in/optIn
- Prefer local opt-in over blanket suppression
- Pin version, isolate behind adapter, golden byte tests
- @Serializable & JSON are stable; binary surface is not
basics
~20 sExperimental means the API can change between versions and the compiler warns unless you opt in. In production you opt in deliberately, pin the library version, and wrap usage so a future change touches one place.
solid answer
~40 sThe binary formats (and several APIs like `@ProtoNumber`) are marked `@ExperimentalSerializationApi`. Kotlin's opt-in mechanism makes the compiler emit a warning/error at every call site until you opt in — either with `@OptIn(ExperimentalSerializationApi::class)` on the using declaration, or project-wide via the `-opt-in` compiler flag / Gradle `optIn` setting. Experimental means **no binary-compatibility guarantee**: signatures or wire details can change across minor versions. In production the discipline is: opt in consciously (not blanket-suppress everywhere), pin the kotlinx-serialization version, isolate format calls behind a thin adapter/boundary so an upgrade changes one file, keep round-trip tests with golden byte fixtures to catch wire changes, and read release notes before bumping. None of this affects the stable `@Serializable` model — only the format API surface is experimental.
go deeper
Knows the compiler warns and you must opt in with @OptIn.
Explains opt-in mechanics and that experimental means the API can change between versions.
Adds production discipline: pinning, adapter isolation, golden tests, deliberate local opt-in.
Sets org policy for adopting experimental APIs — risk assessment, upgrade gates, and containment so a wire change can't silently break persisted data.
## What 'experimental' means Kotlin has an **opt-in** system (`@RequiresOptIn`). kotlinx.serialization marks parts of its API with `@ExperimentalSerializationApi`, including the binary formats and annotations like `@ProtoNumber`. This signals: the API may change, and there is **no binary-compatibility guarantee** across versions. ## How the compiler reacts Touching an experimental API without opting in produces a warning (or error, depending on config). You resolve it explicitly: ```kotlin @OptIn(ExperimentalSerializationApi::class) fun encode(u: User): ByteArray = ProtoBuf.encodeToByteArray(u) ``` Or project-wide: ```kotlin // build.gradle.kts kotlin { compilerOptions { optIn.add("kotlinx.serialization.ExperimentalSerializationApi") } } ``` Prefer **local** `@OptIn` so you stay aware of every experimental touch point rather than silencing the whole project. ## Production discipline - **Opt in deliberately**, ideally close to the call, not via a blanket suppression that hides future surprises. - **Pin the version** of `kotlinx-serialization` and bump intentionally. - **Isolate** format access behind a thin adapter (one `encode`/`decode` boundary) so a breaking change is a one-file fix. - **Golden/round-trip tests**: assert `decode(encode(x)) == x` and keep byte-level fixtures so a wire change is caught by CI before it reaches consumers. - **Read release notes** when upgrading; experimental APIs are exactly where changes land. ## What is NOT experimental The `@Serializable` annotation and the core JSON format are stable. Only the binary format surface (and some advanced APIs) carry the experimental marker, so the risk is contained to that boundary.
- Is the data written by an experimental format guaranteed stable on disk?No hard guarantee across versions — that is why you keep golden byte fixtures and read release notes, so a wire-format change is caught before it corrupts stored data.
- Why prefer @OptIn over a project-wide flag?Local opt-in keeps every experimental usage visible and reviewable; a project-wide flag hides them and makes future breaking changes harder to locate.
saying these in an interview costs you the question
- Thinks experimental just means 'unfinished/buggy' with no API/compat meaning
- Suppresses opt-in globally without awareness
- Assumes wire format is frozen across versions
- No round-trip or fixture tests around format calls
- Believes @Serializable itself is experimental