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

Lessons From Librarians Makers

jbarr21
April 20, 2020
220

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
  2. 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 } })
  3. 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 })
  4. 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 })
  5. 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
  6. 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 })
  7. Codebase Health ✅ Builds quickly ✅ Test coverage ✅ Well

    documented ❌ Duplicate code ❌ Deprecated APIs ❌ Large modules/classes
  8. 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
  9. 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
  10. Limit Surface Area • Final by default final class Hammer

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

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

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

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

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

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

    the wrong thing?" "Is this going to limit me later?"
  17. API vs Implementation class Hammer { public void hammer(Nail nail)

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

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

    { // ow } } :hammer libA libA App :hammer-api interface Hammer { void hammer(Nail nail) }
  20. 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; }
  21. 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(); }
  22. BuildConfig final class RealAppBuildConfig implements AppBuildConfig { // ... }

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

    public interface AppBuildConfig { String versionName(); int versionCode(); boolean isDebug(); } :appbuildconfig :realappbuildconfig :app
  24. 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 } } }
  25. 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
  26. Find/Replace Replace in Path (⌘⇧R) Command line tools > find

    . -name "*.java" | xargs sed -i '' “s/import foo/import barr/g”
  27. Kotlin ReplaceWith class Example { @Deprecated("Use Example.bar") fun foo(s: String)

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

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

    IntelliJ’s tools • Good for automating multiple refactor operations https://cutt.ly/find-matches-action
  38. 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")) }
  39. 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(-)
  40. 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
  41. b7cb262 - Migrate lib1 d43aa98 - Migrate lib2 a1dc8d1 -

    Migrate lib3 d843d8f - Migrate lib4 vs a13ab55 - Migrate all libs Safety Without Feature Flags
  42. Photo by Gabriel Sollmann on Unsplash Lessons From a Librarian

    James Barr - @JBarr21 Zac Sweers - @ZacSweers