Upgrade to Pro — share decks privately, control downloads, hide ads and more …

There is 1 Broken API Among Us

Nicola Corti
November 29, 2020

There is 1 Broken API Among Us

As a library user, you probably know that versions don't always tell the truth. Have you ever experienced a broken build after doing a minor bump of a library?

As a library author, maintaining a clean API is a challenge. There is always the risk that a breaking change sneaks in by accident.

Wouldn't it be nice to get notified if you're accidentally breaking your users' code? A tool to inspect the binary API of your Kotlin/Java code can help you exactly with that. In this talk, we will see some of those tools and how that simplifies the life of library & SDK developers.

Nicola Corti

November 29, 2020
Tweet

More Decks by Nicola Corti

Other Decks in Technology

Transcript

  1. THERE IS 1 BROKEN API AMONG US
    @CORTINICO

    View Slide

  2. NICOLA CORTI
    Kotlin GDE
    Android Infra Eng. @ Spotify
    twitter.com/cortinico
    github.com/cortinico
    ncorti.com

    View Slide

  3. WHO IS THIS TALK FOR?

    View Slide

  4. WHO IS THIS TALK FOR?
    • Library Devs
    • SDK Devs
    • Open Source Devs
    • Mobile Devs in a Modularized world

    View Slide

  5. GOOD API CITIZENS

    View Slide

  6. GOOD API CITIZENS @Deprecate

    View Slide

  7. GOOD API CITIZENS @Deprecate
    fun replace(with: String)

    View Slide

  8. GOOD API CITIZENS @Deprecate
    @Deprecated
    fun replace(with: String)

    View Slide

  9. GOOD API CITIZENS @Deprecate
    @Deprecated(
    message = "This method is affected by a bug"
    )
    fun replace(with: String)

    View Slide

  10. GOOD API CITIZENS @Deprecate
    @Deprecated(
    message = "This method is affected by a bug",
    replaceWith = ReplaceWith("newReplace(with)")
    )
    fun replace(with: String)

    View Slide

  11. GOOD API CITIZENS @Deprecate
    @Deprecated(
    message = "This method is affected by a bug",
    replaceWith = ReplaceWith("newReplace(with)"),
    level = DeprecationLevel.ERROR
    )
    fun replace(with: String)

    View Slide

  12. GOOD API CITIZENS @OptIn

    View Slide

  13. GOOD API CITIZENS @OptIn
    fun getAnswer() = 42

    View Slide

  14. GOOD API CITIZENS @OptIn
    @MyExperimentalApi
    fun getAnswer() = 42

    View Slide

  15. GOOD API CITIZENS @OptIn
    @RequiresOptIn(message = "This API is experimental")
    annotation class MyExperimentalApi
    @MyExperimentalApi
    fun getAnswer() = 42

    View Slide

  16. GOOD API CITIZENS @OptIn
    @RequiresOptIn(message = "This API is experimental")
    annotation class MyExperimentalApi
    @MyExperimentalApi
    fun getAnswer() = 42
    fun main() {
    println(getAnswer())
    }

    View Slide

  17. GOOD API CITIZENS @OptIn
    @RequiresOptIn(message = "This API is experimental")
    annotation class MyExperimentalApi
    @MyExperimentalApi
    fun getAnswer() = 42
    @MyExperimentalApi
    fun main() {
    println(getAnswer())
    }

    View Slide

  18. GOOD API CITIZENS @OptIn
    @RequiresOptIn(message = "This API is experimental")
    annotation class MyExperimentalApi
    @MyExperimentalApi
    fun getAnswer() = 42
    @OptIn(MyExperimentalApi::class)
    fun main() {
    println(getAnswer())
    }

    View Slide

  19. GOOD API CITIZENS Explicit API Mode
    kotlin {
    explicitApiWarning()
    }

    View Slide

  20. GOOD API CITIZENS Explicit API Mode
    kotlin {
    explicitApi()
    }

    View Slide

  21. GOOD API CITIZENS Explicit API Mode
    kotlin {
    explicitApi()
    }
    fun getAnswer() = 42

    View Slide

  22. GOOD API CITIZENS Explicit API Mode
    kotlin {
    explicitApi()
    }
    fun getAnswer() = 42.0

    View Slide

  23. GOOD API CITIZENS Explicit API Mode
    kotlin {
    explicitApi()
    }
    public fun getAnswer() : Int = 42

    View Slide

  24. GOOD API CITIZENS @SemVer

    View Slide

  25. GOOD API CITIZENS @SemVer
    MAJOR.

    View Slide

  26. GOOD API CITIZENS @SemVer
    MAJOR.MINOR.

    View Slide

  27. GOOD API CITIZENS @SemVer
    MAJOR.MINOR.PATCH

    View Slide

  28. GOOD API CITIZENS @SemVer
    MAJOR.MINOR.PATCH

    View Slide

  29. GOOD API CITIZENS @SemVer
    MAJOR.MINOR.PATCH

    View Slide

  30. GOOD API CITIZENS @SemVer
    MAJOR.MINOR.PATCH

    View Slide

  31. BINARY COMPATIBILITY VALIDATOR

    View Slide

  32. BINARY COMPATIBILITY VALIDATOR

    View Slide

  33. BINARY COMPATIBILITY VALIDATOR

    View Slide

  34. BINARY COMPATIBILITY VALIDATOR Setup

    View Slide

  35. BINARY COMPATIBILITY VALIDATOR
    buildscript {
    dependencies {
    classpath("org.jetbrains.kotlinx:binary-compatibility-validator:0.2.4")
    }
    }
    apply plugin: "binary-compatibility-validator"
    Setup

    View Slide

  36. BINARY COMPATIBILITY VALIDATOR
    buildscript {
    dependencies {
    classpath("org.jetbrains.kotlinx:binary-compatibility-validator:0.2.4")
    }
    }
    apply plugin: "binary-compatibility-validator"
    Setup
    apiValidation {
    }

    View Slide

  37. BINARY COMPATIBILITY VALIDATOR
    buildscript {
    dependencies {
    classpath("org.jetbrains.kotlinx:binary-compatibility-validator:0.2.4")
    }
    }
    apply plugin: "binary-compatibility-validator"
    Setup
    apiValidation {
    ignoredProjects += ["sample"]
    }

    View Slide

  38. BINARY COMPATIBILITY VALIDATOR
    buildscript {
    dependencies {
    classpath("org.jetbrains.kotlinx:binary-compatibility-validator:0.2.4")
    }
    }
    apply plugin: "binary-compatibility-validator"
    Setup
    apiValidation {
    ignoredProjects += ["sample"]
    ignoredPackages += ["com.chuckerteam.chucker.internal"]
    }

    View Slide

  39. BINARY COMPATIBILITY VALIDATOR
    buildscript {
    dependencies {
    classpath("org.jetbrains.kotlinx:binary-compatibility-validator:0.2.4")
    }
    }
    apply plugin: "binary-compatibility-validator"
    Setup
    apiValidation {
    ignoredProjects += ["sample"]
    ignoredPackages += ["com.chuckerteam.chucker.internal"]
    nonPublicMarkers += ["com.chuckerteam.chucker.InternalApiAnnotation"]
    }

    View Slide

  40. BINARY COMPATIBILITY VALIDATOR
    buildscript {
    dependencies {
    classpath("org.jetbrains.kotlinx:binary-compatibility-validator:0.2.4")
    }
    }
    apply plugin: "binary-compatibility-validator"
    Setup
    apiValidation {
    ignoredProjects += ["sample"]
    ignoredPackages += ["com.chuckerteam.chucker.internal"]
    nonPublicMarkers += ["com.chuckerteam.chucker.InternalApiAnnotation"]
    }
    $ ./gradlew apiDump

    View Slide

  41. BINARY COMPATIBILITY VALIDATOR .api File

    View Slide

  42. BINARY COMPATIBILITY VALIDATOR .api File
    public final class com/chuckerteam/chucker/BuildConfig {
    public static final field BUILD_TYPE Ljava/lang/String;
    public static final field DEBUG Z
    public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String;
    public fun ()V
    }
    public final class com/chuckerteam/chucker/api/Chucker {
    public static final field INSTANCE Lcom/chuckerteam/chucker/api/Chucker;
    public static final fun dismissNotifications (Landroid/content/Context;)V
    public static final fun getLaunchIntent (Landroid/content/Context;)Landroid/content/Intent;
    public final fun isOp ()Z
    }
    public final class com/chuckerteam/chucker/api/ChuckerCollector {
    public fun (Landroid/content/Context;)V
    public fun (Landroid/content/Context;Z)V
    public fun (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/RetentionManager$Period;)V
    public synthetic fun (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/
    RetentionManager$Period;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
    public final fun getShowNotification ()Z
    public final fun setShowNotification (Z)V
    }
    public final class com/chuckerteam/chucker/api/ChuckerInterceptor : okhttp3/Interceptor {

    View Slide

  43. BINARY COMPATIBILITY VALIDATOR .api File
    public final class com/chuckerteam/chucker/BuildConfig {
    public static final field BUILD_TYPE Ljava/lang/String;
    public static final field DEBUG Z
    public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String;
    public fun ()V
    }
    public final class com/chuckerteam/chucker/api/Chucker {
    public static final field INSTANCE Lcom/chuckerteam/chucker/api/Chucker;
    public static final fun dismissNotifications (Landroid/content/Context;)V
    public static final fun getLaunchIntent (Landroid/content/Context;)Landroid/content/Intent;
    public final fun isOp ()Z
    }
    public final class com/chuckerteam/chucker/api/ChuckerCollector {
    public fun (Landroid/content/Context;)V
    public fun (Landroid/content/Context;Z)V
    public fun (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/RetentionManager$Period;)V
    public synthetic fun (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/
    RetentionManager$Period;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
    public final fun getShowNotification ()Z
    public final fun setShowNotification (Z)V
    }
    public final class com/chuckerteam/chucker/api/ChuckerInterceptor : okhttp3/Interceptor {

    View Slide

  44. BINARY COMPATIBILITY VALIDATOR .api File
    public final class com/chuckerteam/chucker/BuildConfig {
    public static final field BUILD_TYPE Ljava/lang/String;
    public static final field DEBUG Z
    public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String;
    public fun ()V
    }
    public final class com/chuckerteam/chucker/api/Chucker {
    public static final field INSTANCE Lcom/chuckerteam/chucker/api/Chucker;
    public static final fun dismissNotifications (Landroid/content/Context;)V
    public static final fun getLaunchIntent (Landroid/content/Context;)Landroid/content/Intent;
    public final fun isOp ()Z
    }
    public final class com/chuckerteam/chucker/api/ChuckerCollector {
    public fun (Landroid/content/Context;)V
    public fun (Landroid/content/Context;Z)V
    public fun (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/RetentionManager$Period;)V
    public synthetic fun (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/
    RetentionManager$Period;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
    public final fun getShowNotification ()Z
    public final fun setShowNotification (Z)V
    }
    public final class com/chuckerteam/chucker/api/ChuckerInterceptor : okhttp3/Interceptor {

    View Slide

  45. BINARY COMPATIBILITY VALIDATOR .api File
    public final class com/chuckerteam/chucker/BuildConfig {
    public static final field BUILD_TYPE Ljava/lang/String;
    public static final field DEBUG Z
    public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String;
    public fun ()V
    }
    public final class com/chuckerteam/chucker/api/Chucker {
    public static final field INSTANCE Lcom/chuckerteam/chucker/api/Chucker;
    public static final fun dismissNotifications (Landroid/content/Context;)V
    public static final fun getLaunchIntent (Landroid/content/Context;)Landroid/content/Intent;
    public final fun isOp ()Z
    }
    public final class com/chuckerteam/chucker/api/ChuckerCollector {
    public fun (Landroid/content/Context;)V
    public fun (Landroid/content/Context;Z)V
    public fun (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/RetentionManager$Period;)V
    public synthetic fun (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/
    RetentionManager$Period;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
    public final fun getShowNotification ()Z
    public final fun setShowNotification (Z)V
    }
    public final class com/chuckerteam/chucker/api/ChuckerInterceptor : okhttp3/Interceptor {

    View Slide

  46. BINARY COMPATIBILITY VALIDATOR .api File
    public final class com/chuckerteam/chucker/BuildConfig {
    public static final field BUILD_TYPE Ljava/lang/String;
    public static final field DEBUG Z
    public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String;
    public fun ()V
    }
    public final class com/chuckerteam/chucker/api/Chucker {
    public static final field INSTANCE Lcom/chuckerteam/chucker/api/Chucker;
    public static final fun dismissNotifications (Landroid/content/Context;)V
    public static final fun getLaunchIntent (Landroid/content/Context;)Landroid/content/Intent;
    public final fun isOp ()Z
    }
    public final class com/chuckerteam/chucker/api/ChuckerCollector {
    public fun (Landroid/content/Context;)V
    public fun (Landroid/content/Context;Z)V
    public fun (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/RetentionManager$Period;)V
    public synthetic fun (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/
    RetentionManager$Period;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
    public final fun getShowNotification ()Z
    public final fun setShowNotification (Z)V
    }
    public final class com/chuckerteam/chucker/api/ChuckerInterceptor : okhttp3/Interceptor {

    View Slide

  47. BINARY COMPATIBILITY VALIDATOR .api File
    public final class com/chuckerteam/chucker/BuildConfig {
    public static final field BUILD_TYPE Ljava/lang/String;
    public static final field DEBUG Z
    public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String;
    public fun ()V
    }
    public final class com/chuckerteam/chucker/api/Chucker {
    public static final field INSTANCE Lcom/chuckerteam/chucker/api/Chucker;
    public static final fun dismissNotifications (Landroid/content/Context;)V
    public static final fun getLaunchIntent (Landroid/content/Context;)Landroid/content/Intent;
    public final fun isOp ()Z
    }
    public final class com/chuckerteam/chucker/api/ChuckerCollector {
    public fun (Landroid/content/Context;)V
    public fun (Landroid/content/Context;Z)V
    public fun (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/RetentionManager$Period;)V
    public synthetic fun (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/
    RetentionManager$Period;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
    public final fun getShowNotification ()Z
    public final fun setShowNotification (Z)V
    }
    public final class com/chuckerteam/chucker/api/ChuckerInterceptor : okhttp3/Interceptor {

    View Slide

  48. BINARY COMPATIBILITY VALIDATOR .api File
    public final class com/chuckerteam/chucker/BuildConfig {
    public static final field BUILD_TYPE Ljava/lang/String;
    public static final field DEBUG Z
    public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String;
    public fun ()V
    }
    public final class com/chuckerteam/chucker/api/Chucker {
    public static final field INSTANCE Lcom/chuckerteam/chucker/api/Chucker;
    public static final fun dismissNotifications (Landroid/content/Context;)V
    public static final fun getLaunchIntent (Landroid/content/Context;)Landroid/content/Intent;
    public final fun isOp ()Z
    }
    public final class com/chuckerteam/chucker/api/ChuckerCollector {
    public fun (Landroid/content/Context;)V
    public fun (Landroid/content/Context;Z)V
    public fun (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/RetentionManager$Period;)V
    public synthetic fun (Landroid/content/Context;ZLcom/chuckerteam/chucker/api/
    RetentionManager$Period;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
    public final fun getShowNotification ()Z
    public final fun setShowNotification (Z)V
    }
    public final class com/chuckerteam/chucker/api/ChuckerInterceptor : okhttp3/Interceptor {

    View Slide

  49. BINARY COMPATIBILITY VALIDATOR Normal Workflow
    $ ./gradlew apiCheck

    View Slide

  50. BINARY COMPATIBILITY VALIDATOR Normal Workflow
    $ ./gradlew apiCheck
    # OR
    $ ./gradlew check

    View Slide

  51. BINARY COMPATIBILITY VALIDATOR Normal Workflow
    $ ./gradlew apiCheck
    # OR
    $ ./gradlew check
    # Computed API has not changed
    BUILD SUCCESSFUL

    View Slide

  52. BINARY COMPATIBILITY VALIDATOR Normal Workflow
    $ ./gradlew apiCheck
    # OR
    $ ./gradlew check
    # Computed API has not changed
    BUILD SUCCESSFUL
    # Computed API has changed
    BUILD FAILED

    View Slide

  53. BINARY COMPATIBILITY VALIDATOR Non-Breaking

    View Slide

  54. BINARY COMPATIBILITY VALIDATOR Breaking

    View Slide

  55. JAPICMP

    View Slide

  56. JAPICMP

    View Slide

  57. JAPICMP

    View Slide

  58. JAPICMP
    search.maven.org/artifact/com.github.siom79.japicmp/japicmp
    Download

    View Slide

  59. JAPICMP CLI
    $ java -jar japicmp-0.14.4-jar-with-dependencies.jar -h
    SYNOPSIS
    java -jar japicmp.jar [-a ] [(-b | --only-incompatible)]
    [(-e | --exclude )] [--exclude-exclusively]
    [(-h | --help)] [--html-file ]
    [--html-stylesheet ]
    [(-i | --include )] [--ignore-missing-classes]
    [--ignore-missing-classes-by-regex ...]
    [--include-exclusively] [--include-synthetic] [(-m | --only-modified)]
    [(-n | --new )]
    [--new-classpath ] [--no-annotations]
    [(-o | --old )]
    [--old-classpath ] [--report-only-filename]
    [(-s | --semantic-versioning)]
    [(-x | --xml-file )]
    [--error-on-binary-incompatibility]
    [--error-on-source-incompatibility]
    [--error-on-modifications]
    [--no-error-on-exclusion-incompatibility]

    View Slide

  60. JAPICMP CLI
    $ java -jar japicmp-0.14.4-jar-with-dependencies.jar -h
    SYNOPSIS
    java -jar japicmp.jar [-a ] [(-b | --only-incompatible)]
    [(-e | --exclude )] [--exclude-exclusively]
    [(-h | --help)] [--html-file ]
    [--html-stylesheet ]
    [(-i | --include )] [--ignore-missing-classes]
    [--ignore-missing-classes-by-regex ...]
    [--include-exclusively] [--include-synthetic] [(-m | --only-modified)]
    [(-n | --new )]
    [--new-classpath ] [--no-annotations]
    [(-o | --old )]
    [--old-classpath ] [--report-only-filename]
    [(-s | --semantic-versioning)]
    [(-x | --xml-file )]
    [--error-on-binary-incompatibility]
    [--error-on-source-incompatibility]
    [--error-on-modifications]
    [--no-error-on-exclusion-incompatibility]

    View Slide

  61. JAPICMP CLI
    $ java -jar japicmp-0.14.4-jar-with-dependencies.jar -h
    SYNOPSIS
    java -jar japicmp.jar [-a ] [(-b | --only-incompatible)]
    [(-e | --exclude )] [--exclude-exclusively]
    [(-h | --help)] [--html-file ]
    [--html-stylesheet ]
    [(-i | --include )] [--ignore-missing-classes]
    [--ignore-missing-classes-by-regex ...]
    [--include-exclusively] [--include-synthetic] [(-m | --only-modified)]
    [(-n | --new )]
    [--new-classpath ] [--no-annotations]
    [(-o | --old )]
    [--old-classpath ] [--report-only-filename]
    [(-s | --semantic-versioning)]
    [(-x | --xml-file )]
    [--error-on-binary-incompatibility]
    [--error-on-source-incompatibility]
    [--error-on-modifications]
    [--no-error-on-exclusion-incompatibility]

    View Slide

  62. JAPICMP CLI
    $ java -jar japicmp-0.14.4-jar-with-dependencies.jar -h
    SYNOPSIS
    java -jar japicmp.jar [-a ] [(-b | --only-incompatible)]
    [(-e | --exclude )] [--exclude-exclusively]
    [(-h | --help)] [--html-file ]
    [--html-stylesheet ]
    [(-i | --include )] [--ignore-missing-classes]
    [--ignore-missing-classes-by-regex ...]
    [--include-exclusively] [--include-synthetic] [(-m | --only-modified)]
    [(-n | --new )]
    [--new-classpath ] [--no-annotations]
    [(-o | --old )]
    [--old-classpath ] [--report-only-filename]
    [(-s | --semantic-versioning)]
    [(-x | --xml-file )]
    [--error-on-binary-incompatibility]
    [--error-on-source-incompatibility]
    [--error-on-modifications]
    [--no-error-on-exclusion-incompatibility]

    View Slide

  63. JAPICMP CLI
    $ java -jar japicmp-0.14.4-jar-with-dependencies.jar -h
    SYNOPSIS
    java -jar japicmp.jar [-a ] [(-b | --only-incompatible)]
    [(-e | --exclude )] [--exclude-exclusively]
    [(-h | --help)] [--html-file ]
    [--html-stylesheet ]
    [(-i | --include )] [--ignore-missing-classes]
    [--ignore-missing-classes-by-regex ...]
    [--include-exclusively] [--include-synthetic] [(-m | --only-modified)]
    [(-n | --new )]
    [--new-classpath ] [--no-annotations]
    [(-o | --old )]
    [--old-classpath ] [--report-only-filename]
    [(-s | --semantic-versioning)]
    [(-x | --xml-file )]
    [--error-on-binary-incompatibility]
    [--error-on-source-incompatibility]
    [--error-on-modifications]
    [--no-error-on-exclusion-incompatibility]

    View Slide

  64. JAPICMP CLI
    $ java -jar japicmp-0.14.4-jar-with-dependencies.jar -h
    SYNOPSIS
    java -jar japicmp.jar [-a ] [(-b | --only-incompatible)]
    [(-e | --exclude )] [--exclude-exclusively]
    [(-h | --help)] [--html-file ]
    [--html-stylesheet ]
    [(-i | --include )] [--ignore-missing-classes]
    [--ignore-missing-classes-by-regex ...]
    [--include-exclusively] [--include-synthetic] [(-m | --only-modified)]
    [(-n | --new )]
    [--new-classpath ] [--no-annotations]
    [(-o | --old )]
    [--old-classpath ] [--report-only-filename]
    [(-s | --semantic-versioning)]
    [(-x | --xml-file )]
    [--error-on-binary-incompatibility]
    [--error-on-source-incompatibility]
    [--error-on-modifications]
    [--no-error-on-exclusion-incompatibility]
    [--error-on-semantic-incompatibility]

    View Slide

  65. JAPICMP CLI
    $ java -jar japicmp-0.14.4-jar-with-dependencies.jar -h
    SYNOPSIS
    java -jar japicmp.jar [-a ] [(-b | --only-incompatible)]
    [(-e | --exclude )] [--exclude-exclusively]
    [(-h | --help)] [--html-file ]
    [--html-stylesheet ]
    [(-i | --include )] [--ignore-missing-classes]
    [--ignore-missing-classes-by-regex ...]
    [--include-exclusively] [--include-synthetic] [(-m | --only-modified)]
    [(-n | --new )]
    [--new-classpath ] [--no-annotations]
    [(-o | --old )]
    [--old-classpath ] [--report-only-filename]
    [(-s | --semantic-versioning)]
    [(-x | --xml-file )]
    [--error-on-binary-incompatibility]
    [--error-on-source-incompatibility]
    [--error-on-modifications]
    [--no-error-on-exclusion-incompatibility]

    View Slide

  66. JAPICMP CLI
    java -jar japicmp-0.14.4-jar-with-dependencies.jar \

    View Slide

  67. JAPICMP CLI
    java -jar japicmp-0.14.4-jar-with-dependencies.jar \
    --old library-3.3.0/classes.jar \

    View Slide

  68. JAPICMP CLI
    java -jar japicmp-0.14.4-jar-with-dependencies.jar \
    --old library-3.3.0/classes.jar \
    --new library-3.4.0/classes.jar \

    View Slide

  69. JAPICMP CLI
    java -jar japicmp-0.14.4-jar-with-dependencies.jar \
    --old library-3.3.0/classes.jar \
    --new library-3.4.0/classes.jar \
    --html-file index.html \

    View Slide

  70. JAPICMP CLI
    java -jar japicmp-0.14.4-jar-with-dependencies.jar \
    --old library-3.3.0/classes.jar \
    --new library-3.4.0/classes.jar \
    --html-file index.html \
    --include “com.chuckerteam.chucker.api” \
    --include-exclusively

    View Slide

  71. JAPICMP CLI
    java -jar japicmp-0.14.4-jar-with-dependencies.jar \
    --old library-3.3.0/classes.jar \
    --new library-3.4.0/classes.jar \
    --html-file index.html \
    --include “com.chuckerteam.chucker.api” \
    --include-exclusively
    java -jar japicmp-0.14.4-jar-with-dependencies.jar \
    --old library-3.3.0/classes.jar \
    --new library-3.4.0/classes.jar \
    --html-file index.html \

    View Slide

  72. JAPICMP CLI
    java -jar japicmp-0.14.4-jar-with-dependencies.jar \
    --old library-3.3.0/classes.jar \
    --new library-3.4.0/classes.jar \
    --html-file index.html \
    --include “com.chuckerteam.chucker.api” \
    --include-exclusively
    java -jar japicmp-0.14.4-jar-with-dependencies.jar \
    --old library-3.3.0/classes.jar \
    --new library-3.4.0/classes.jar \
    --html-file index.html \
    --ignore-missing-classes \

    View Slide

  73. JAPICMP CLI
    java -jar japicmp-0.14.4-jar-with-dependencies.jar \
    --old library-3.3.0/classes.jar \
    --new library-3.4.0/classes.jar \
    --html-file index.html \
    --include “com.chuckerteam.chucker.api” \
    --include-exclusively
    java -jar japicmp-0.14.4-jar-with-dependencies.jar \
    --old library-3.3.0/classes.jar \
    --new library-3.4.0/classes.jar \
    --html-file index.html \
    --ignore-missing-classes \
    --no-annotations \

    View Slide

  74. JAPICMP CLI
    java -jar japicmp-0.14.4-jar-with-dependencies.jar \
    --old library-3.3.0/classes.jar \
    --new library-3.4.0/classes.jar \
    --html-file index.html \
    --include “com.chuckerteam.chucker.api” \
    --include-exclusively
    java -jar japicmp-0.14.4-jar-with-dependencies.jar \
    --old library-3.3.0/classes.jar \
    --new library-3.4.0/classes.jar \
    --html-file index.html \
    --ignore-missing-classes \
    --no-annotations \
    --exclude “com.chuckerteam.chucker.databinding;
    com.chuckerteam.chucker.internal"

    View Slide

  75. JAPICMP HTML REPORT

    View Slide

  76. JAPICMP HTML REPORT

    View Slide

  77. JAPICMP HTML REPORT

    View Slide

  78. JAPICMP HTML REPORT

    View Slide

  79. JAPICMP HTML REPORT

    View Slide

  80. JAPICMP HTML REPORT

    View Slide

  81. JAPICMP HTML REPORT

    View Slide

  82. JAPICMP HTML REPORT

    View Slide

  83. JAPICMP HTML REPORT

    View Slide

  84. JAPICMP HTML REPORT

    View Slide

  85. JAPICMP GRADLE SETUP
    apply plugin: "me.champeau.gradle.japicmp"

    View Slide

  86. JAPICMP GRADLE SETUP
    apply plugin: "me.champeau.gradle.japicmp"
    configurations {
    latest
    baseline
    }

    View Slide

  87. JAPICMP GRADLE SETUP
    apply plugin: "me.champeau.gradle.japicmp"
    configurations {
    latest
    baseline
    }
    dependencies {
    }

    View Slide

  88. JAPICMP GRADLE SETUP
    apply plugin: "me.champeau.gradle.japicmp"
    configurations {
    latest
    baseline
    }
    dependencies {
    baseline project(path: ":library", configuration: "default")
    }

    View Slide

  89. JAPICMP GRADLE SETUP
    apply plugin: "me.champeau.gradle.japicmp"
    configurations {
    latest
    baseline
    }
    dependencies {
    baseline project(path: ":library", configuration: "default")
    latest("com.github.chuckerteam.chucker:library:3.4.0")
    }

    View Slide

  90. JAPICMP GRADLE SETUP
    apply plugin: "me.champeau.gradle.japicmp"
    configurations {
    latest
    baseline
    }
    dependencies {
    baseline project(path: ":library", configuration: "default")
    latest("com.github.chuckerteam.chucker:library:3.4.0") {
    transitive = false
    force = true
    }
    }

    View Slide

  91. apply plugin: "me.champeau.gradle.japicmp"
    configurations {
    latest
    baseline
    }
    dependencies {
    baseline project(path: ":library", configuration: "default")
    latest("com.github.chuckerteam.chucker:library:3.4.0") {
    transitive = false
    force = true
    }
    }
    tasks.register("japiCmp", JapicmpTask) {
    oldClasspath = configurations.latest
    newClasspath = configurations.baseline
    }
    JAPICMP GRADLE SETUP

    View Slide

  92. apply plugin: "me.champeau.gradle.japicmp"
    configurations {
    latest
    baseline
    }
    dependencies {
    baseline project(path: ":library", configuration: "default")
    latest("com.github.chuckerteam.chucker:library:3.4.0") {
    transitive = false
    force = true
    }
    }
    tasks.register("japiCmp", JapicmpTask) {
    oldClasspath = configurations.latest
    newClasspath = configurations.baseline
    htmlOutputFile = file("$buildDir/reports/japiCmp.html")
    }
    JAPICMP GRADLE SETUP

    View Slide

  93. apply plugin: "me.champeau.gradle.japicmp"
    configurations {
    latest
    baseline
    }
    dependencies {
    baseline project(path: ":library", configuration: "default")
    latest("com.github.chuckerteam.chucker:library:3.4.0") {
    transitive = false
    force = true
    }
    }
    tasks.register("japiCmp", JapicmpTask) {
    oldClasspath = configurations.latest
    newClasspath = configurations.baseline
    htmlOutputFile = file("$buildDir/reports/japiCmp.html")
    failOnModification = true
    }
    JAPICMP GRADLE SETUP

    View Slide

  94. apply plugin: "me.champeau.gradle.japicmp"
    configurations {
    latest
    baseline
    }
    dependencies {
    baseline project(path: ":library", configuration: "default")
    latest("com.github.chuckerteam.chucker:library:3.4.0") {
    transitive = false
    force = true
    }
    }
    tasks.register("japiCmp", JapicmpTask) {
    oldClasspath = configurations.latest
    newClasspath = configurations.baseline
    htmlOutputFile = file("$buildDir/reports/japiCmp.html")
    failOnModification = true
    ignoreMissingClasses = true
    }
    JAPICMP GRADLE SETUP

    View Slide

  95. configurations {
    latest
    baseline
    }
    dependencies {
    baseline project(path: ":library", configuration: "default")
    latest("com.github.chuckerteam.chucker:library:3.4.0") {
    transitive = false
    force = true
    }
    }
    tasks.register("japiCmp", JapicmpTask) {
    oldClasspath = configurations.latest
    newClasspath = configurations.baseline
    htmlOutputFile = file("$buildDir/reports/japiCmp.html")
    failOnModification = true
    ignoreMissingClasses = true
    packageIncludes = ["com.chuckerteam.chucker.api"]
    }
    JAPICMP GRADLE SETUP

    View Slide

  96. configurations {
    latest
    baseline
    }
    dependencies {
    baseline project(path: ":library", configuration: "default")
    latest("com.github.chuckerteam.chucker:library:3.4.0") {
    transitive = false
    force = true
    }
    }
    tasks.register("japiCmp", JapicmpTask) {
    oldClasspath = getJarClasses(project, configurations.latest)
    newClasspath = getJarClasses(project, configurations.baseline)
    htmlOutputFile = file("$buildDir/reports/japiCmp.html")
    failOnModification = true
    ignoreMissingClasses = true
    packageIncludes = ["com.chuckerteam.chucker.api"]
    }
    JAPICMP GRADLE SETUP

    View Slide

  97. latest("com.github.chuckerteam.chucker:library:3.4.0") {
    transitive = false
    force = true
    }
    }
    tasks.register("japiCmp", JapicmpTask) {
    oldClasspath = getJarClasses(project, configurations.latest)
    newClasspath = getJarClasses(project, configurations.baseline)
    htmlOutputFile = file("$buildDir/reports/japiCmp.html")
    failOnModification = true
    ignoreMissingClasses = true
    packageIncludes = ["com.chuckerteam.chucker.api"]
    }
    def getJarClasses(project, configuration) {
    return project.files(
    project.zipTree(configuration.files.first())
    .matching {include "classes.jar"}
    .first()
    )
    }
    JAPICMP GRADLE SETUP

    View Slide

  98. TO RECAP

    View Slide

  99. TO RECAP Evolving APIs is HARD

    View Slide

  100. TO RECAP Evolving APIs is HARD

    View Slide

  101. TO RECAP Evolving APIs is HARD

    View Slide

  102. TO RECAP Evolving APIs is HARD

    View Slide

  103. TO RECAP
    • binary-compatibility-validator
    • Safeguard your API during your workflow
    • Easier to integrate for Kotlin/Gradle projects
    • japicmp
    • Fine grain control over your API surface
    • Useful when making a new release
    • Be a good API citizen

    View Slide

  104. THANKS
    @CORTINICO

    View Slide