# 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

```console jdx-generated
$ 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)
```

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](https://mohammadmd1383.github.io/jdx/docs/design/#77-public-api-diff).

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](https://mohammadmd1383.github.io/jdx/docs/output/#exit-codes): `diff` has no "not found" outcome, so `1` means
"the gate tripped". The verdict is also in the JSON envelope as `result.gate`.

```console jdx-generated
$ 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)
```

In GitHub Actions:

```yaml
- 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
```
