Pro Yearly is on sale from $80 to $50! »

Lessons From a Librarian

Lessons From a Librarian

Dccb5651578c54429ec81782a0bf881a?s=128

jbarr21

April 19, 2020
Tweet

Transcript

  1. Photo by Gabriel Sollmann on Unsplash Lessons From a Librarian

    James Barr - @JBarr21 Zac Sweers - @ZacSweers
  2. subscribe()

  3. subscribe(new Consumer<String>() { @Override public void accept(String value) { //

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

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

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

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

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

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

  10. subscribe(new CrashOnErrorConsumer<String>() { @Override public void accept(String value) { //

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

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

    Do stuff } }) subscribe(new CrashOnErrorConsumer<String>() { @Override public void accept(String value) { // Do stuff } }) subscribe(new CrashOnErrorConsumer<String>() { @Override public void accept(String value) { // Do stuff } }) subscribe(new CrashOnErrorConsumer<String>() { @Override public void accept(String value) { // Do stuff } }) subscribe(new CrashOnErrorConsumer<String>() { @Override public void accept(String value) { // Do stuff } }) subscribe(new CrashOnErrorConsumer<String>() { @Override public void accept(String value) { // Do stuff } }) subscribe(new CrashOnErrorConsumer<String>() @Override public void accept(String value) { // Do stuff } }) subscribe(new CrashOnErrorConsumer<String>() { @Override public void accept(String value) { // Do stuff } }) subscribe(new CrashOnErrorConsumer<String>() { @Override public void accept(String value) { // Do stuff } }) subscribe(new CrashOnErrorConsumer<String>() { @Override public void accept(String value) { // Do stuff } }) subscribe(new CrashOnErrorConsumer<String>() { @Override public void accept(String value) { // Do stuff } }) subscribe(new CrashOnErrorConsumer<String>() { @Override public void accept(String value) { // Do stuff } }) subscribe(value -> { // Do stuff }) And thousands more
  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 })
  14. - Anonymous audience member "But how?!"

  15. Lessons from a Librarian

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

    NYC 2018
  17. Writing Code That Lasts Forever

  18. Helping Everyone Write Code That Lasts Forever

  19. Codebase Health ✅ Builds quickly ✅ Test coverage ✅ Well

    documented ❌ Duplicate code ❌ Deprecated APIs ❌ Large modules/classes
  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
  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
  22. How does tech debt happen?

  23. API Design

  24. Limit Surface Area

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

    { // ow } }
  26. Limit Surface Area • Final by default final class Hammer

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

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

    Factories final class Hammer { public static Hammer create() { // Stuff } private Hammer() { ... } }
  29. Limit Surface Area final class Hammer { public void hammer(Nail

    nail) { // ow } } • Final by default • Visibility • Factories
  30. API Design • Limit surface area • Final by default

    • Visibility • Factories • Consume interfaces interface Nail { void onHammer(); int length(); }
  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) { // } }
  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) { // } }
  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
  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
  35. - you, writing libraries "Does this put the user on

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

    the right track?" "Is this going to limit me later?"
  37. API vs Implementation

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

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

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

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

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

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

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

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

    { // ow } } :hammer libA libA App :hammer-api interface Hammer { void hammer(Nail nail) }
  46. BuildConfig

  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; }
  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(); }
  49. BuildConfig final class RealAppBuildConfig implements AppBuildConfig { // ... }

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

    public interface AppBuildConfig { String versionName(); int versionCode(); boolean isDebug(); } :appbuildconfig :realappbuildconfig :app
  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 } } }
  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
  53. Code Review

  54. Code Review Teach, don't tell

  55. Code Review Approve with nits

  56. Code Review

  57. Static Analysis

  58. Static Analysis

  59. Static Analysis

  60. Removing Tech Debt

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

  62. Find/Replace Replace in Path (⌘⇧R) Command line tools > find

    . -name "*.java" | xargs sed -i '' “s/import foo/import barr/g”
  63. Kotlin ReplaceWith class Example { fun foo(s: String) {a/* old

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

    { /* old */ } fun bar(s: String) { /* new */ } fun usage() { foo("hello") } }
  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") } }
  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") } }
  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") } }
  68. import android.util.Pair fun doSomething() { val pair = Pair.create(“a”, “b”)

    } <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <View /> </FrameLayout> </FrameLayout> Android Lint SuggestedFix
  69. import androidx.core.util.Pair fun doSomething() { val pair = Pair.create(“a”, “b”)

    } <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <View /> </FrameLayout> </FrameLayout> Android Lint SuggestedFix
  70. Structural Find & Replace void main( Example ex, Other other)

    { ex.foo() ex.foo(“hi") other.foo() }
  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() }
  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,∞])
  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
  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
  75. Kotlin Script File(args[0]).walkTopDown() .filter { it.extension in setOf("java", "kt") }

    .filter { "/test/" !in it.absolutePath } .forEach {}
  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")) }
  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(-)
  78. De-risking Change

  79. Shadow Jobs

  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
  81. Offensive Programming

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

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

  84. Offensive Programming abstract class Model { abstract List<String> modelList(); }

    ImmutableList
  85. b7cb262 - Migrate lib1 d43aa98 - Migrate lib2 a1dc8d1 -

    Migrate lib3 d843d8f - Migrate lib4 vs a13ab55 - Migrate all libs Safety Without Feature Flags
  86. Photo by Webaroo on Unsplash

  87. Photo by Pankaj Patel on Unsplash

  88. Photo by Margarida CSilva on Unsplash

  89. Photo by Hannes Wolf on Unsplash

  90. Wrap Up

  91. Photo by Gabriel Sollmann on Unsplash Lessons From a Librarian

    James Barr - @JBarr21 Zac Sweers - @ZacSweers