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

Lessons From Librarians Makers

Dccb5651578c54429ec81782a0bf881a?s=47 jbarr21
April 20, 2020
30

Lessons From Librarians Makers

Dccb5651578c54429ec81782a0bf881a?s=128

jbarr21

April 20, 2020
Tweet

Transcript

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

    James Barr - @JBarr21 Zac Sweers - @ZacSweers
  2. cutt.ly/lessons-from-libs

  3. cutt.ly/lessons-from-libs

  4. subscribe()

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

    Do stuff } })
  6. 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(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 } })
  9. subscribe(new CrashOnErrorConsumer<String>() { @Override public void accept(String value) { //

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

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

  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 })
  13. 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 })
  14. 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
  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 })
  16. - Anonymous audience member "But how?!"

  17. Lessons from a Librarian

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

    NYC 2018
  19. Writing Code That Lasts Forever

  20. Helping Everyone Write Code That Lasts Forever

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

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

  25. API Design

  26. Limit Surface Area

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

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

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

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

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

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

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

    • Visibility • Factories • Consume interfaces interface Nail { void onHammer(); int length(); }
  34. - you, writing libraries "How easy is it to do

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

    the wrong thing?" "Is this going to limit me later?"
  36. API vs Implementation

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

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

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

    { // ow } } :hammer libA libA App
  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 :hammer-api interface Hammer { void hammer(Nail nail) }
  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. BuildConfig

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

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

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

  53. Static Analysis

  54. Static Analysis

  55. Static Analysis

  56. Removing Tech Debt

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

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

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

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

    { /* old */ } fun bar(s: String) { /* new */ } fun usage() { foo("hello") } }
  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") } }
  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") } }
  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") } }
  64. 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
  65. 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
  66. Structural Find & Replace void main( Example ex, Other other)

    { ex.foo() ex.foo(“hi") other.foo() }
  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() }
  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,∞])
  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
  70. IntelliJ Plugin • Add your own refactor options • Leverage

    IntelliJ’s tools • Good for automating multiple refactor operations https://cutt.ly/find-matches-action
  71. Kotlin Script File(args[0]).walkTopDown() .filter { it.extension in setOf("java", "kt") }

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

  75. Shadow Jobs

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

  78. b7cb262 - Migrate lib1 d43aa98 - Migrate lib2 a1dc8d1 -

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

  80. Photo by Pankaj Patel on Unsplash

  81. Photo by Margarida CSilva on Unsplash

  82. Photo by Hannes Wolf on Unsplash

  83. Wrap Up

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

    James Barr - @JBarr21 Zac Sweers - @ZacSweers