$30 off During Our Annual Pro Sale. View Details »

Lessons From Librarians Makers

jbarr21
April 20, 2020
130

Lessons From Librarians Makers

jbarr21

April 20, 2020
Tweet

Transcript

  1. Photo by Gabriel Sollmann on Unsplash
    Lessons From a Librarian
    James Barr - @JBarr21
    Zac Sweers - @ZacSweers

    View Slide

  2. cutt.ly/lessons-from-libs

    View Slide

  3. cutt.ly/lessons-from-libs

    View Slide

  4. subscribe()

    View Slide

  5. subscribe(new Consumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })

    View Slide

  6. subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })

    View Slide

  7. subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })

    View Slide

  8. subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer()
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })

    View Slide

  9. subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })

    View Slide

  10. subscribe(value -> {
    // Do stuff
    })

    View Slide

  11. subscribe(value -> {
    // Do stuff
    })
    github.com/uber/rxdogtag

    View Slide

  12. subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer()
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(value -> {
    // Do stuff
    })

    View Slide

  13. subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer()
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(value -> {
    // Do stuff
    })

    View Slide

  14. subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer()
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(new CrashOnErrorConsumer() {
    @Override
    public void accept(String value) {
    // Do stuff
    }
    })
    subscribe(value -> {
    // Do stuff
    })
    And thousands more

    View Slide

  15. subscribe(value -> {
    // Do stuff
    })
    subscribe(value -> {
    // Do stuff
    })
    subscribe(value -> {
    // Do stuff
    })
    subscribe(value -> {
    // Do stuff
    })
    subscribe(value -> {
    // Do stuff
    })
    subscribe(value -> {
    // Do stuff
    })
    subscribe(value -> {
    // Do stuff
    })
    subscribe(value -> {
    // Do stuff
    })
    subscribe(value -> {
    // Do stuff
    })
    subscribe(value -> {
    // Do stuff
    })
    subscribe(value -> {
    // Do stuff
    })
    subscribe(value -> {
    // Do stuff
    })
    subscribe(value -> {
    // Do stuff
    })

    View Slide

  16. - Anonymous audience member
    "But how?!"

    View Slide

  17. Lessons from a Librarian

    View Slide

  18. "Writing Code That Lasts Forever" - Jesse Wilson - Droidcon NYC 2018

    View Slide

  19. Writing Code That Lasts
    Forever

    View Slide

  20. Helping Everyone Write
    Code That Lasts Forever

    View Slide

  21. Codebase Health
    ✅ Builds quickly
    ✅ Test coverage
    ✅ Well documented
    ❌ Duplicate code
    ❌ Deprecated APIs
    ❌ Large modules/classes

    View Slide

  22. Name Build Time
    Undocumented
    Public Methods
    Module Size
    Deprecated
    Method Calls
    Quality Score
    (Overall: 75%)
    app 5m25s 0 10,000 LOC 0 75%
    feature1 4m20s 0 120,000 LOC 0 50%
    feature2 20s 0 4,000 LOC 0 100%
    library1 7s 90 1,000 LOC 0 75%
    library2 5s 0 2,000 LOC 88 75%
    Codebase Health

    View Slide

  23. Name Build Time
    Undocumented
    Public Methods
    Module Size
    Deprecated
    Method Calls
    Quality Score
    (Overall: 75%)
    app 5m25s 0 10,000 LOC 0 75%
    feature1 4m20s 0 120,000 LOC 0 50%
    feature2 20s 0 4,000 LOC 0 100%
    library1 7s 90 1,000 LOC 0 75%
    library2 5s 0 2,000 LOC 88 75%
    Codebase Health

    View Slide

  24. How does tech debt
    happen?

    View Slide

  25. API Design

    View Slide

  26. Limit Surface Area

    View Slide

  27. Limit Surface Area
    class Hammer {
    public void hammer(Nail nail) {
    // ow
    }
    }

    View Slide

  28. Limit Surface Area
    • Final by default
    final class Hammer {
    public void hammer(Nail nail) {
    // ow
    }
    }

    View Slide

  29. Limit Surface Area
    • Final by default
    • Visibility final class Hammer {
    public void hammer(Nail nail) {
    // ow
    }
    }

    View Slide

  30. Limit Surface Area
    • Final by default
    • Visibility
    • Factories
    final class Hammer {
    public static Hammer create() {
    // Stuff
    }
    private Hammer() { ... }
    }

    View Slide

  31. Limit Surface Area
    final class Hammer {
    public void hammer(Nail nail) {
    // ow
    }
    }
    • Final by default
    • Visibility
    • Factories

    View Slide

  32. API Design
    • Limit surface area
    • Final by default
    • Visibility
    • Factories
    • Consume interfaces
    interface Nail {
    void onHammer();
    int length();
    }

    View Slide

  33. API Design
    • Limit surface area
    • Final by default
    • Visibility
    • Factories
    • Consume interfaces
    interface Nail {
    void onHammer();
    int length();
    }

    View Slide

  34. - you, writing libraries
    "How easy is it to do the wrong
    thing?"

    View Slide

  35. - you, writing libraries
    "How easy is it to do the wrong
    thing?"
    "Is this going to limit me
    later?"

    View Slide

  36. API vs Implementation

    View Slide

  37. API vs Implementation
    class Hammer {
    public void hammer(Nail nail) {
    // ow
    }
    }

    View Slide

  38. API vs Implementation
    class Hammer {
    public void hammer(Nail nail) {
    // ow
    }
    }
    :hammer

    View Slide

  39. API vs Implementation
    class Hammer {
    public void hammer(Nail nail) {
    // ow
    }
    }
    :hammer
    libA
    libA
    App

    View Slide

  40. API vs Implementation
    class Hammer {
    public void hammer(Nail nail) {
    // ow
    }
    }
    :hammer
    libA
    libA
    App

    View Slide

  41. API vs Implementation
    class Hammer {
    public void hammer(Nail nail) {
    // ow
    }
    }
    :hammer
    libA
    libA
    App

    View Slide

  42. API vs Implementation
    class Hammer {
    public void hammer(Nail nail) {
    // ow
    }
    }
    :hammer
    libA
    libA
    App
    :hammer-api
    interface Hammer {
    void hammer(Nail nail)
    }

    View Slide

  43. API vs Implementation
    class Hammer {
    public void hammer(Nail nail) {
    // ow
    }
    }
    :hammer
    libA
    libA
    App
    :hammer-api
    interface Hammer {
    void hammer(Nail nail)
    }

    View Slide

  44. API vs Implementation
    class Hammer {
    public void hammer(Nail nail) {
    // ow
    }
    }
    :hammer
    libA
    libA
    App
    :hammer-api
    interface Hammer {
    void hammer(Nail nail)
    }

    View Slide

  45. BuildConfig

    View Slide

  46. BuildConfig
    public class BuildConfig {
    public static final String VERSION_NAME = "1.0";
    public static final int VERSION_CODE = 1;
    public static final boolean DEBUG == false;
    }

    View Slide

  47. BuildConfig
    public class BuildConfig {
    public static final String VERSION_NAME = "1.0";
    public static final int VERSION_CODE = 1;
    public static final boolean DEBUG == false;
    }
    public interface AppBuildConfig {
    String versionName();
    int versionCode();
    boolean isDebug();
    }

    View Slide

  48. BuildConfig
    final class RealAppBuildConfig implements AppBuildConfig {
    // ...
    }
    public interface AppBuildConfig {
    String versionName();
    int versionCode();
    boolean isDebug();
    }
    :appbuildconfig
    :app

    View Slide

  49. BuildConfig
    final class RealAppBuildConfig implements AppBuildConfig {
    // ...
    }
    public interface AppBuildConfig {
    String versionName();
    int versionCode();
    boolean isDebug();
    }
    :appbuildconfig
    :realappbuildconfig
    :app

    View Slide

  50. BuildConfig
    final class RealAppBuildConfig implements AppBuildConfig {
    // ...
    }
    public interface AppBuildConfig {
    String versionName();
    int versionCode();
    boolean isDebug();
    }
    :appbuildconfig
    :realappbuildconfig
    :app
    android {
    // or libraryVariants
    applicationVariants {
    generateBuildConfigProvider {
    enabled = true
    }
    }
    }

    View Slide

  51. More Resources
    • API Design As An Art: youtube.com/watch?v=Ve9HlnJ0wCM
    • Writing Code That Lasts Forever: youtube.com/watch?v=YZstpc2939s
    • Effective Java
    • AppBuildConfig pattern: github.com/ZacSweers/CatchUp/pull/200

    View Slide

  52. Code Review

    View Slide

  53. Static Analysis

    View Slide

  54. Static Analysis

    View Slide

  55. Static Analysis

    View Slide

  56. Removing Tech Debt

    View Slide

  57. Replace in Path (⌘⇧R)
    Find/Replace

    View Slide

  58. Find/Replace
    Replace in Path (⌘⇧R)
    Command line tools
    > find . -name "*.java" | xargs sed -i '' “s/import foo/import barr/g”

    View Slide

  59. Kotlin ReplaceWith
    class Example {
    fun foo(s: String) {a/* old */ }
    fun usage() {
    foo("hello")
    }
    }

    View Slide

  60. Kotlin ReplaceWith
    class Example {
    @Deprecated("Use Example.bar")
    fun foo(s: String) { /* old */ }
    fun bar(s: String) { /* new */ }
    fun usage() {
    foo("hello")
    }
    }

    View Slide

  61. Kotlin ReplaceWith
    class Example {
    @Deprecated("Use Example.bar", replaceWith = ReplaceWith(“bar(s)"))
    fun foo(s: String) { /* old */ }
    fun bar(s: String) { /* new */ }
    fun usage() {
    foo("hello")
    }
    }

    View Slide

  62. Kotlin ReplaceWith
    class Example {
    @Deprecated("Use Example.bar", replaceWith = ReplaceWith(“bar(s)"))
    fun foo(s: String) { /* old */ }
    fun bar(s: String) { /* new */ }
    fun usage() {
    foo("hello")
    }
    }

    View Slide

  63. Kotlin ReplaceWith
    class Example {
    @Deprecated("Use Example.bar", replaceWith = ReplaceWith(“bar(s)"))
    fun foo(s: String) { /* old */ }
    fun bar(s: String) { /* new */ }
    fun usage() {
    bar("hello")
    }
    }

    View Slide

  64. import android.util.Pair
    fun doSomething() {
    val pair = Pair.create(“a”, “b”)
    }
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    android:layout_width="match_parent"
    android:layout_height="match_parent">



    Android Lint SuggestedFix

    View Slide

  65. import androidx.core.util.Pair
    fun doSomething() {
    val pair = Pair.create(“a”, “b”)
    }
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    android:layout_width="match_parent"
    android:layout_height="match_parent">



    Android Lint SuggestedFix

    View Slide

  66. Structural Find & Replace
    void main(
    Example ex,
    Other other) {
    ex.foo()
    ex.foo(“hi")
    other.foo()
    }

    View Slide

  67. Structural Find & Replace
    void main(
    Example ex,
    Other other) {
    ex.foo()
    ex.foo(“hi")
    other.foo()
    }
    void main(
    Example ex,
    Other other) {
    ex.foo()
    ex.foo(“hi")
    other.foo()
    }

    View Slide

  68. Structural Find & Replace
    void main(
    Example ex,
    Other other) {
    ex.foo()
    ex.foo(“hi")
    other.foo()
    }
    $Instance$ type=com.example.Example
    .foo($Parameter$ [0,∞])
    $Instance$ type=com.example.Example
    .bar($Parameter$ [0,∞])

    View Slide

  69. Structural Find & Replace
    void main(
    Example ex,
    Other other) {
    ex.bar()
    ex.bar(“hi")
    other.foo()
    }
    $Instance$ type=com.example.Example
    .foo($Parameter$ [0,∞])
    $Instance$ type=com.example.Example
    .bar($Parameter$ [0,∞])
    youtrack.jetbrains.com/issue/KT-10176

    View Slide

  70. IntelliJ Plugin
    • Add your own refactor options
    • Leverage IntelliJ’s tools
    • Good for automating multiple
    refactor operations
    https://cutt.ly/find-matches-action

    View Slide

  71. Kotlin Script
    File(args[0]).walkTopDown()
    .filter { it.extension in setOf("java", "kt") }
    .filter { "/test/" !in it.absolutePath }
    .forEach {}

    View Slide

  72. Kotlin Script
    File(args[0]).walkTopDown()
    .filter { it.extension in setOf("java", "kt") }
    .filter { "/test/" !in it.absolutePath }
    .forEach { file: File ->
    val fileLines = file.readLines().toMutableList()
    // modify lines of file
    file.writeText(fileLines.joinToString("\n"))
    }

    View Slide

  73. Kotlin Script
    File(args[0]).walkTopDown()
    .filter { it.extension in setOf("java", "kt") }
    .filter { "/test/" !in it.absolutePath }
    .forEach { file: File ->
    val fileLines = file.readLines().toMutableList()
    // modify lines of file
    file.writeText(fileLines.joinToString("\n"))
    }
    $ brew install holgerbrandl/tap/kscript
    $ kscript MigrateToAndroidX.kts ~/project-root
    16,866 files changed, 31,454 insertions(+), 31,415 deletions(-)

    View Slide

  74. De-risking Change

    View Slide

  75. Shadow Jobs

    View Slide

  76. Shadow Jobs
    Android Gradle Plugin 4 beta/4.1 alpha
    Kotlin
    1.4-M1
    Gradle 6.4-rc
    -SNAPSHOT
    Androidx alphas
    JDK
    8-14
    Shadow Jobs

    View Slide

  77. Offensive Programming

    View Slide

  78. b7cb262 - Migrate lib1
    d43aa98 - Migrate lib2
    a1dc8d1 - Migrate lib3
    d843d8f - Migrate lib4
    vs
    a13ab55 - Migrate all libs

    Safety Without Feature Flags

    View Slide

  79. Photo by Webaroo on Unsplash

    View Slide

  80. Photo by Pankaj Patel on Unsplash

    View Slide

  81. Photo by Margarida CSilva on Unsplash

    View Slide

  82. Photo by Hannes Wolf on Unsplash

    View Slide

  83. Wrap Up

    View Slide

  84. Photo by Gabriel Sollmann on Unsplash
    Lessons From a Librarian
    James Barr - @JBarr21
    Zac Sweers - @ZacSweers

    View Slide