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

Lessons From Librarians Makers

Avatar for jbarr21 jbarr21
April 20, 2020
240

Lessons From Librarians Makers

Avatar for jbarr21

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