Quick start
Apply the plugin, run serialkompatCheck, and read the report. It takes about five
minutes.
1. Apply the plugin
serialkompat is on the Gradle Plugin Portal, so no repository setup is needed. Apply
the plugin in the module that holds your @Serializable models, and list the types to
check:
plugins {
kotlin("jvm")
kotlin("plugin.serialization")
id("com.chrisjenx.serialkompat") version "0.2.0"
}
serialkompat {
types.set(listOf("com.example.Order")) // (1)!
}
- Fully-qualified names of the
@Serializableroot types to check. Everything reachable from them, such as nested types and sealed subtypes, is checked too.
2. Run the check
$ ./gradlew serialkompatCheck
serialkompatCheck is wired into check, so ./gradlew build runs it too. It:
- Extracts the current wire schema from your compiled
@Serializabletypes. - Extracts the baseline, the schema you compare against. It checks out
baselineRefin a temporary git worktree and runs the extraction there. If you don't setbaselineRef, it uses your repository's default branch. - Diffs the two schemas, classifies every change against real kotlinx-serialization
behavior, and fails the build on any
BREAKfinding.
Your first run
The baseline is extracted by running your build at baselineRef, so the plugin
must already be applied and configured on that ref. On the change that first adds
serialkompat, the baseline ref doesn't have it yet, so the baseline extraction
fails. Skip the check for that one change (./gradlew build -x serialkompatCheck,
and leave the GitHub Action out of that PR) and merge it. The gate then works on
every later change. See
First-time adoption for the details.
3. Read the report
A breaking change prints a console report like this:
serialkompat: 2 active finding(s) (1 breaking, 1 warning), 0 acknowledged
BREAK PROPERTY_REMOVED com.example.Order (backward)
field 'note' was removed from com.example.Order
fix: Removing a field drops its data for tolerant readers; keep it (or bridge a rename with @JsonNames) until nothing uses it; else bump major.
WARN CONFIG_READER_STRICTNESS Json config (backward)
Json ignoreUnknownKeys changed true -> false
fix: A stricter reader now rejects previously-tolerated unknown keys.
Each finding reads top to bottom:
| Part | Meaning |
|---|---|
BREAK / WARN |
Severity. BREAK fails the gate. WARN means the outcome depends on config, or decoding succeeds but the data silently changes |
PROPERTY_REMOVED / CONFIG_READER_STRICTNESS |
The rule that fired. See Rules |
com.example.Order / Json config |
What the finding is about: a type, or the shared Json config |
(backward) |
The direction that broke. backward means new code reading old data; forward means old code reading new data |
| indented line 1 | What changed, in plain language |
fix: |
A concrete suggestion for resolving or living with the break |
The same report is written as JSON to build/serialkompat/report.json. It is a
versioned document ({schemaVersion, summary, findings: [...]}) that tooling can
depend on. It can also be rendered as SARIF or GitHub annotations; see
Report formats.
Exit codes
Under Gradle, serialkompatCheck passes when there are no active BREAK findings
(WARNs don't fail it). An active BREAK fails the task, so the build exits
non-zero.
The standalone CLI has a finer-grained contract:
| Code | Meaning |
|---|---|
0 |
No breaking findings (there may still be WARNs) |
1 |
At least one active BREAK finding |
2 |
Usage error (bad arguments, unreadable snapshot, etc.), or an empty baseline without --allow-empty-baseline |
Next
- Setup: the CLI and GitHub Action, plus the main
serialkompat { }options. - Rules: every rule, what it detects, and how config changes the verdict.
- Configuration: direction, accepted breaks, renames, and scoping.
- Report formats: the JSON schema, SARIF, and GitHub annotations.