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
  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 • Fluent APIs as navigation final class Hammer { public void removeNail(Nail nail) { // } }
  16. API Design • Limit surface area • Final by default

    • Visibility • Factories • Consume interfaces • Fluent APIs as navigation final class Hammer { public RemovedNail removeNail(Nail nail) { // } }
  17. 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
  18. 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
  19. - you, writing libraries "Does this put the user on

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

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

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

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

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

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

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

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

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

    IntelliJ’s tools • Good for automating multiple uses of refactors https://cutt.ly/find-matches-action
  41. 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")) }
  42. 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(-)
  43. 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
  44. b7cb262 - Migrate lib1 d43aa98 - Migrate lib2 a1dc8d1 -

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

    James Barr - @JBarr21 Zac Sweers - @ZacSweers