API diff for upgrades
Before bumping a dependency, ask what the new version breaks. jdx diff compares the public API of two artifacts — jars, class directories or Maven coordinates — and names every difference as a rule with a severity, with a gate for CI.
Compare two versions#
jdx diff com.google.code.gson:gson:2.11.0 com.google.code.gson:gson:2.14.0 --fetch
api diff com.google.code.gson:gson:2.11.0 -> com.google.code.gson:gson:2.14.0 (public surface)
info TYPE_ADDED com.google.gson.internal.GsonTypes
info TYPE_ADDED com.google.gson.internal.bind.JsonElementTypeAdapter
info MEMBER_ADDED com.google.gson.FieldNamingStrategy#alternateNames(java.lang.reflect.Field)
info MEMBER_ADDED com.google.gson.internal.ConstructorConstructor#get(com.google.gson.reflect.TypeToken, boolean)
info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#BIG_DECIMAL_FACTORY
info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#BIG_INTEGER_FACTORY
info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#DOUBLE_STRICT
info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#FLOAT_STRICT
info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#LAZILY_PARSED_NUMBER_FACTORY
info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#LONG_AS_STRING
info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#atomicLongAdapter(com.google.gson.TypeAdapter)
info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#atomicLongArrayAdapter(com.google.gson.TypeAdapter)
info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#javaTimeTypeAdapterFactory()
info MEMBER_ADDED com.google.gson.internal.sql.SqlTypesSupport#SQL_TYPE_FACTORIES
info MEMBER_ADDED com.google.gson.stream.JsonReader#getNestingLimit()
info MEMBER_ADDED com.google.gson.stream.JsonReader#setNestingLimit(int)
info FIELD_CONSTANT_VALUE_CHANGED com.google.gson.internal.GsonBuildConfig#VERSION: "2.11.0" -> "2.14.0"
0 breaking · 0 suspicious · 17 informational · 2 types added · 0 types removed
warning MULTI_RELEASE_VARIANT: in com.google.code.gson:gson:2.11.0: multi-release gson-2.11.0.jar serves version-specific variants for this JDK (21)
… 9 more lines (run it yourself to see them all)Real output — this example ran against the current jdx when the page was built.
Each side can be a jar file, a class directory, a glob or a group:artifact:version
coordinate (with --fetch to download it). diff reads bytecode only — no sources, no
decompilation, no workspace.
Rules and severities#
Every finding names exactly one rule, so tools can filter on the rule rather than the prose:
- breaking — an already-compiled caller can fail to link or run:
MEMBER_REMOVED,RETURN_TYPE_CHANGED,PARAMETER_TYPE_CHANGED,MEMBER_VISIBILITY_NARROWED,STATIC_TO_INSTANCE,INTERFACE_METHOD_ADDED,TYPE_REMOVED, … - suspicious — binary-compatible by the letter of the JLS, but recompiling, the Kotlin
view or behaviour can still break:
CHECKED_EXCEPTION_ADDED,GENERIC_SIGNATURE_CHANGED,MEMBER_MOVED_TO_SUPERTYPE,KOTLIN_NULLABILITY_CHANGED,KOTLIN_SUSPEND_CHANGED, … - info — additive or cosmetic:
MEMBER_ADDED,TYPE_ADDED,DEPRECATED_ADDED,FIELD_CONSTANT_VALUE_CHANGED, …
A removed member paired with a matching added one is reported once as a retyped signature, not as a removal plus an addition — and an ambiguous pairing is never guessed.
The full rule set is in the design specification.
Useful flags:
| Flag | Effect |
|---|---|
--severity breaking|suspicious|all |
how much to print (the tally always covers everything) |
--visibility public|all |
public + protected surface (default), or every declared member |
--include-synthetic |
also compare bridge and synthetic members |
--limit N |
cap the findings shown (default 200; the cut tail is the least severe) |
Gate a pull request#
--fail-on breaking (or --fail-on any) turns the report into an exit status: 1 when the
gate trips, 0 otherwise. This is the one documented exception to the
exit-code table: diff has no "not found" outcome, so 1 means
"the gate tripped". The verdict is also in the JSON envelope as result.gate.
jdx diff com.google.code.gson:gson:2.11.0 com.google.code.gson:gson:2.14.0 --severity breaking --fail-on breaking
api diff com.google.code.gson:gson:2.11.0 -> com.google.code.gson:gson:2.14.0 (public surface)
17 findings below --severity breaking
0 breaking · 0 suspicious · 17 informational · 2 types added · 0 types removed
fail-on breaking: no breaking changes (exit 0)
warning MULTI_RELEASE_VARIANT: in com.google.code.gson:gson:2.11.0: multi-release gson-2.11.0.jar serves version-specific variants for this JDK (21)
warning MULTI_RELEASE_VARIANT: in com.google.code.gson:gson:2.14.0: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21)
… 7 more lines (run it yourself to see them all)Real output — this example ran against the current jdx when the page was built.
In GitHub Actions:
- name: Public API must not break
run: jdx diff "com.example:mylib:$(git describe --tags --abbrev=0 | tr -d v)" build/libs/mylib.jar --fetch --fail-on breaking