Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Lessons From a Librarian
Search
jbarr21
April 19, 2020
Programming
480
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Lessons From a Librarian
jbarr21
April 19, 2020
More Decks by jbarr21
See All by jbarr21
Lessons From Librarians Makers
jbarr21
0
320
Scaling UI
jbarr21
2
350
It's Time to Develop a Watch Face
jbarr21
1
310
Other Decks in Programming
See All in Programming
ここ半年くらいでAIに作らせたR用ツール
eitsupi
0
380
5分で問診!Composer セキュリティ健康診断
codmoninc
0
1k
Claude Code全社展開のためにやったことn選~プラグイン302個・コミッター271人を支えるために~
kenchan
5
1.5k
Lean は証明の正しさを確認するためだけのツールって思ってませんか?
inoueasei
1
160
琵琶湖の水は止められてもNet--HTTPのリトライは止められない / You might be able to stop the water flow of Lake Biwa but you can't stop Net::HTTP retries
luccafort
PRO
0
740
メールのエイリアス機能を履き違えない
isshinfunada
0
240
「人を評価する AI」の設計と実装
ryoyanara
0
210
React本体のコードリーディング
high_g_engineer
1
140
生成AI導入の「期待外れ」を乗り越える ー 開発フロー改革が目指す、真の組織変革
starfish719
0
4.6k
【QA Test Talk Vol.8】AI-DLC による Whole Team Approach の加速
pkshadeck
PRO
0
190
ソフトウェア設計に溶けるインフラ ― AWS CDK のインフラ認識論
konokenj
3
800
為什麼你並不需要ViewModel / No, you don't need a ViewModel
lovee
1
500
Featured
See All Featured
RailsConf 2023
tenderlove
30
1.5k
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
63
45k
Raft: Consensus for Rubyists
vanstee
141
7.6k
Making the Leap to Tech Lead
cromwellryan
135
10k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
The agentic SEO stack - context over prompts
schlessera
0
860
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
250
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
490
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
650
My Coaching Mixtape
mlcsv
0
240
The World Runs on Bad Software
bkeepers
PRO
72
12k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.6k
Transcript
Photo by Gabriel Sollmann on Unsplash Lessons From a Librarian
James Barr - @JBarr21 Zac Sweers - @ZacSweers
subscribe()
subscribe(new Consumer<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 } }) 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 })
subscribe(value -> { // Do stuff }) github.com/uber/rxdogtag
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 })
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 })
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
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 })
- Anonymous audience member "But how?!"
Lessons from a Librarian
"Writing Code That Lasts Forever" - Jesse Wilson - Droidcon
NYC 2018
Writing Code That Lasts Forever
Helping Everyone Write Code That Lasts Forever
Codebase Health ✅ Builds quickly ✅ Test coverage ✅ Well
documented ❌ Duplicate code ❌ Deprecated APIs ❌ Large modules/classes
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
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
How does tech debt happen?
API Design
Limit Surface Area
Limit Surface Area class Hammer { public void hammer(Nail nail)
{ // ow } }
Limit Surface Area • Final by default final class Hammer
{ public void hammer(Nail nail) { // ow } }
Limit Surface Area • Final by default • Visibility final
class Hammer { public void hammer(Nail nail) { // ow } }
Limit Surface Area • Final by default • Visibility •
Factories final class Hammer { public static Hammer create() { // Stuff } private Hammer() { ... } }
Limit Surface Area final class Hammer { public void hammer(Nail
nail) { // ow } } • Final by default • Visibility • Factories
API Design • Limit surface area • Final by default
• Visibility • Factories • Consume interfaces interface Nail { void onHammer(); int length(); }
API Design • Limit surface area • Final by default
• Visibility • Factories • Consume interfaces • Fluent APIs as navigation final class Hammer { public void removeNail(Nail nail) { // } }
API Design • Limit surface area • Final by default
• Visibility • Factories • Consume interfaces • Fluent APIs as navigation final class Hammer { public RemovedNail removeNail(Nail nail) { // } }
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
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
- you, writing libraries "Does this put the user on
the right track?"
- you, writing libraries "Does this put the user on
the right track?" "Is this going to limit me later?"
API vs Implementation
API vs Implementation class Hammer { public void hammer(Nail nail)
{ // ow } }
API vs Implementation class Hammer { public void hammer(Nail nail)
{ // ow } } :hammer
API vs Implementation class Hammer { public void hammer(Nail nail)
{ // ow } } :hammer libA libA App
API vs Implementation class Hammer { public void hammer(Nail nail)
{ // ow } } :hammer libA libA App
API vs Implementation class Hammer { public void hammer(Nail nail)
{ // ow } } :hammer libA libA App
API vs Implementation class Hammer { public void hammer(Nail nail)
{ // ow } } :hammer libA libA App :hammer-api interface Hammer { void hammer(Nail nail) }
API vs Implementation class Hammer { public void hammer(Nail nail)
{ // ow } } :hammer libA libA App :hammer-api interface Hammer { void hammer(Nail nail) }
API vs Implementation class Hammer { public void hammer(Nail nail)
{ // ow } } :hammer libA libA App :hammer-api interface Hammer { void hammer(Nail nail) }
BuildConfig
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; }
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(); }
BuildConfig final class RealAppBuildConfig implements AppBuildConfig { // ... }
public interface AppBuildConfig { String versionName(); int versionCode(); boolean isDebug(); } :appbuildconfig :app
BuildConfig final class RealAppBuildConfig implements AppBuildConfig { // ... }
public interface AppBuildConfig { String versionName(); int versionCode(); boolean isDebug(); } :appbuildconfig :realappbuildconfig :app
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 } } }
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
Code Review
Code Review Teach, don't tell
Code Review Approve with nits
Code Review
Static Analysis
Static Analysis
Static Analysis
Removing Tech Debt
Replace in Path (⌘⇧R) Find/Replace
Find/Replace Replace in Path (⌘⇧R) Command line tools > find
. -name "*.java" | xargs sed -i '' “s/import foo/import barr/g”
Kotlin ReplaceWith class Example { fun foo(s: String) {a/* old
*/ } fun usage() { foo("hello") } }
Kotlin ReplaceWith class Example { @Deprecated("Use Example.bar") fun foo(s: String)
{ /* old */ } fun bar(s: String) { /* new */ } fun usage() { foo("hello") } }
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") } }
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") } }
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") } }
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
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
Structural Find & Replace void main( Example ex, Other other)
{ ex.foo() ex.foo(“hi") other.foo() }
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() }
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,∞])
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
IntelliJ Plugin • Add your own refactor options • Leverage
IntelliJ’s tools • Good for automating multiple uses of refactors https://cutt.ly/find-matches-action
Kotlin Script File(args[0]).walkTopDown() .filter { it.extension in setOf("java", "kt") }
.filter { "/test/" !in it.absolutePath } .forEach {}
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")) }
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(-)
De-risking Change
Shadow Jobs
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
Offensive Programming
Offensive Programming abstract class Model { abstract List<String> modelList(); }
Offensive Programming abstract class Model { abstract ImmutableList<String> modelList(); }
Offensive Programming abstract class Model { abstract List<String> modelList(); }
ImmutableList
b7cb262 - Migrate lib1 d43aa98 - Migrate lib2 a1dc8d1 -
Migrate lib3 d843d8f - Migrate lib4 vs a13ab55 - Migrate all libs Safety Without Feature Flags
Photo by Webaroo on Unsplash
Photo by Pankaj Patel on Unsplash
Photo by Margarida CSilva on Unsplash
Photo by Hannes Wolf on Unsplash
Wrap Up
Photo by Gabriel Sollmann on Unsplash Lessons From a Librarian
James Barr - @JBarr21 Zac Sweers - @ZacSweers