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

Lessons From a Librarian

jbarr21
April 19, 2020

Lessons From a Librarian

jbarr21

April 19, 2020
Tweet

More Decks by jbarr21

Other Decks in Programming

Transcript

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

    View Slide

  2. subscribe()

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  6. 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

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

    View Slide

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

    View Slide

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

    View Slide

  10. 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

  11. 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

  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
    })
    And thousands more

    View Slide

  13. 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

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

    View Slide

  15. Lessons from a Librarian

    View Slide

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

    View Slide

  17. Writing Code That Lasts
    Forever

    View Slide

  18. Helping Everyone Write
    Code That Lasts Forever

    View Slide

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

    View Slide

  20. 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

  21. 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

  22. How does tech debt
    happen?

    View Slide

  23. API Design

    View Slide

  24. Limit Surface Area

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  31. API Design
    • Limit surface area
    • Final by default
    • Visibility
    • Factories
    • Consume interfaces
    • Fluent APIs as
    navigation
    final class Hammer {
    public void removeNail(Nail nail) {
    //
    }
    }

    View Slide

  32. API Design
    • Limit surface area
    • Final by default
    • Visibility
    • Factories
    • Consume interfaces
    • Fluent APIs as
    navigation
    final class Hammer {
    public RemovedNail removeNail(Nail nail) {
    //
    }
    }

    View Slide

  33. API Design
    final class Hammer {
    public RemovedNail removeNail(Nail nail) {
    //
    }
    }
    hammer.removeNail()
    .dispose()
    • Limit surface area
    • Final by default
    • Visibility
    • Factories
    • Consume interfaces
    • Fluent APIs as
    navigation

    View Slide

  34. API Design
    final class Hammer {
    public RemovedNail removeNail(Nail nail) {
    //
    }
    }
    hammer.removeNail()
    .dispose()
    • Limit surface area
    • Final by default
    • Visibility
    • Factories
    • Consume interfaces
    • Fluent APIs as
    navigation

    View Slide

  35. - you, writing libraries
    "Does this put the user on the
    right track?"

    View Slide

  36. - you, writing libraries
    "Does this put the user on the
    right track?"
    "Is this going to limit me
    later?"

    View Slide

  37. API vs Implementation

    View Slide

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

    View Slide

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

    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

    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. 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

  46. BuildConfig

    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;
    }

    View Slide

  48. 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

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

    View Slide

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

    View Slide

  51. 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

  52. 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

  53. Code Review

    View Slide

  54. Code Review
    Teach, don't tell

    View Slide

  55. Code Review
    Approve with nits

    View Slide

  56. Code Review

    View Slide

  57. Static Analysis

    View Slide

  58. Static Analysis

    View Slide

  59. Static Analysis

    View Slide

  60. Removing Tech Debt

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  65. 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

  66. 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

  67. 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

  68. 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

  69. 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

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

    View Slide

  71. 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

  72. 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

  73. 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

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

    View Slide

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

    View Slide

  76. 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

  77. 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

  78. De-risking Change

    View Slide

  79. Shadow Jobs

    View Slide

  80. 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

  81. Offensive Programming

    View Slide

  82. Offensive Programming
    abstract class Model {
    abstract List modelList();
    }

    View Slide

  83. Offensive Programming
    abstract class Model {
    abstract ImmutableList modelList();
    }

    View Slide

  84. Offensive Programming
    abstract class Model {
    abstract List modelList();
    }
    ImmutableList

    View Slide

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

    Safety Without Feature Flags

    View Slide

  86. Photo by Webaroo on Unsplash

    View Slide

  87. Photo by Pankaj Patel on Unsplash

    View Slide

  88. Photo by Margarida CSilva on Unsplash

    View Slide

  89. Photo by Hannes Wolf on Unsplash

    View Slide

  90. Wrap Up

    View Slide

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

    View Slide