Slide 1

Slide 1 text

Creating A Better Developer Experience By Avoiding Legacy Code Adam McNeilly - @AdamMc331 @AdamMc331 #AndroidSummit 1

Slide 2

Slide 2 text

What Is Legacy Code? @AdamMc331 #AndroidSummit 2

Slide 3

Slide 3 text

What Is Legacy Code? @AdamMc331 #AndroidSummit 3

Slide 4

Slide 4 text

What Is Legacy Code? 1. Code that was written before we knew better. @AdamMc331 #AndroidSummit 3

Slide 5

Slide 5 text

What Is Legacy Code? 1. Code that was written before we knew better. 2. Code that was written by someone else, who is no longer a maintainer. @AdamMc331 #AndroidSummit 3

Slide 6

Slide 6 text

What Is Legacy Code? 1. Code that was written before we knew better. 2. Code that was written by someone else, who is no longer a maintainer. 3. Code that doesn't have tests. @AdamMc331 #AndroidSummit 3

Slide 7

Slide 7 text

What Is Legacy Code? 1. Code that was written before we knew better. 2. Code that was written by someone else, who is no longer a maintainer. 3. Code that doesn't have tests. 4. Code that we're unable to/afraid to change because everything might break. @AdamMc331 #AndroidSummit 3

Slide 8

Slide 8 text

What Is Legacy Code? 1. Code that was written before we knew better. 2. Code that was written by someone else, who is no longer a maintainer. 3. Code that doesn't have tests. 4. Code that we're unable to/afraid to change because everything might break. 5. Code without any type of comments or documentation. @AdamMc331 #AndroidSummit 3

Slide 9

Slide 9 text

What Is Legacy Code? 1. Code that was written before we knew better. 2. Code that was written by someone else, who is no longer a maintainer. 3. Code that doesn't have tests. 4. Code that we're unable to/afraid to change because everything might break. 5. Code without any type of comments or documentation. 6. All of the above! @AdamMc331 #AndroidSummit 3

Slide 10

Slide 10 text

Why Do We Care? @AdamMc331 #AndroidSummit 4

Slide 11

Slide 11 text

Legacy Code Impacts Our Work @AdamMc331 #AndroidSummit 5

Slide 12

Slide 12 text

Legacy Code Impacts Our Work 1. Legacy code can become a blocker for new development. @AdamMc331 #AndroidSummit 5

Slide 13

Slide 13 text

Legacy Code Impacts Our Work 1. Legacy code can become a blocker for new development. 2. Legacy code can prevent us from shipping with confidence. @AdamMc331 #AndroidSummit 5

Slide 14

Slide 14 text

Legacy Code Impacts Our Work 1. Legacy code can become a blocker for new development. 2. Legacy code can prevent us from shipping with confidence. 3. Legacy code can be confusing and difficult to build on top of. @AdamMc331 #AndroidSummit 5

Slide 15

Slide 15 text

Legacy Code Impacts Our Work 1. Legacy code can become a blocker for new development. 2. Legacy code can prevent us from shipping with confidence. 3. Legacy code can be confusing and difficult to build on top of. 4. Legacy code is not fun to work with for these, and many other reasons. @AdamMc331 #AndroidSummit 5

Slide 16

Slide 16 text

Is Legacy Code Inevitable? @AdamMc331 #AndroidSummit 6

Slide 17

Slide 17 text

Is Legacy Code Inevitable? Code will age with time, and new practices will always arise. However, we can examine our own experiences with legacy code to help ensure our future selves and teammates have a better developer experience. @AdamMc331 #AndroidSummit 7

Slide 18

Slide 18 text

Myth: Legacy Code Was Written By Someone Else @AdamMc331 #AndroidSummit 8

Slide 19

Slide 19 text

Legacy Code Was Written By Someone Else @AdamMc331 #AndroidSummit 9

Slide 20

Slide 20 text

Legacy Code Was Written By Someone Else » We can't avoid people moving on to new opportunities. @AdamMc331 #AndroidSummit 9

Slide 21

Slide 21 text

Legacy Code Was Written By Someone Else » We can't avoid people moving on to new opportunities. » One day, YOU will be that someone. @AdamMc331 #AndroidSummit 9

Slide 22

Slide 22 text

Legacy Code Was Written By Someone Else » We can't avoid people moving on to new opportunities. » One day, YOU will be that someone. » Your past self from one year ago also wrote code like a different person. @AdamMc331 #AndroidSummit 9

Slide 23

Slide 23 text

@AdamMc331 #AndroidSummit 10

Slide 24

Slide 24 text

Exploring Causes Of Legacy Code @AdamMc331 #AndroidSummit 11

Slide 25

Slide 25 text

Legacy Code Doesn't Have Tests @AdamMc331 #AndroidSummit 12

Slide 26

Slide 26 text

Legacy Code Doesn't Have Tests @AdamMc331 #AndroidSummit 13

Slide 27

Slide 27 text

Legacy Code Doesn't Have Tests There are two reasons code doesn't have tests: @AdamMc331 #AndroidSummit 14

Slide 28

Slide 28 text

Legacy Code Doesn't Have Tests There are two reasons code doesn't have tests: 1. The author chose not to write any. @AdamMc331 #AndroidSummit 14

Slide 29

Slide 29 text

Legacy Code Doesn't Have Tests There are two reasons code doesn't have tests: 1. The author chose not to write any. 2. The code wasn't testable in the first place. @AdamMc331 #AndroidSummit 14

Slide 30

Slide 30 text

Untestable Code @AdamMc331 #AndroidSummit 15

Slide 31

Slide 31 text

Untestable Code 1. Code with a lot of static references. @AdamMc331 #AndroidSummit 15

Slide 32

Slide 32 text

Untestable Code 1. Code with a lot of static references. 2. Code that doesn't leverage proper dependency injection. @AdamMc331 #AndroidSummit 15

Slide 33

Slide 33 text

Untestable Code 1. Code with a lot of static references. 2. Code that doesn't leverage proper dependency injection. These unclear static dependencies can sometimes indicate fragile code that people are afraid to change. @AdamMc331 #AndroidSummit 16

Slide 34

Slide 34 text

Untestable Code class ProfileViewModel : ViewModel() { private fun loadProfile() { try { // ... } catch (e: Throwable) { MyErrorTool.getInstance().logException(e) } } } @AdamMc331 #AndroidSummit 17

Slide 35

Slide 35 text

Untestable Code } catch (e: Throwable) { MyErrorTool.getInstance().logException(e) } @AdamMc331 #AndroidSummit 18

Slide 36

Slide 36 text

Untestable Code } catch (e: Throwable) { MyErrorTool.getInstance().logException(e) } » MyErrorTool is difficult to mock for unit tests. @AdamMc331 #AndroidSummit 18

Slide 37

Slide 37 text

Untestable Code } catch (e: Throwable) { MyErrorTool.getInstance().logException(e) } » MyErrorTool is difficult to mock for unit tests. » We don't want our negative tests to actually log errors to our production tool. @AdamMc331 #AndroidSummit 18

Slide 38

Slide 38 text

Untestable Code } catch (e: Throwable) { MyErrorTool.getInstance().logException(e) } » MyErrorTool is difficult to mock for unit tests. » We don't want our negative tests to actually log errors to our production tool. » This tool could be running setup that crashes our tests. @AdamMc331 #AndroidSummit 18

Slide 39

Slide 39 text

Untestable Code - Quick Solution // Inject error tool with default value. class ProfileViewModel( private val errorTool: MyErrorTool = MyErrorTool.getInstance() ) : ViewModel() { private fun loadProfile() { try { // ... } catch (e: Throwable) { errorTool.logException(e) } } } @AdamMc331 #AndroidSummit 19

Slide 40

Slide 40 text

Tightly Coupled Dependencies Become Legacy Code @AdamMc331 #AndroidSummit 20

Slide 41

Slide 41 text

Tightly Coupled Dependencies Become Legacy Code @AdamMc331 #AndroidSummit 21

Slide 42

Slide 42 text

Tightly Coupled Dependencies Become Legacy Code » Companies change third party vendors (analytics trackers, error reporters). @AdamMc331 #AndroidSummit 21

Slide 43

Slide 43 text

Tightly Coupled Dependencies Become Legacy Code » Companies change third party vendors (analytics trackers, error reporters). » Companies may change tech stacks (REST to GraphQL). @AdamMc331 #AndroidSummit 21

Slide 44

Slide 44 text

Tightly Coupled Dependencies Become Legacy Code » Companies change third party vendors (analytics trackers, error reporters). » Companies may change tech stacks (REST to GraphQL). » New image loading libraries are made every few years. @AdamMc331 #AndroidSummit 21

Slide 45

Slide 45 text

Tightly Coupled Dependencies Become Legacy Code » Companies change third party vendors (analytics trackers, error reporters). » Companies may change tech stacks (REST to GraphQL). » New image loading libraries are made every few years. » Code that doesn't allow these things to be changed is likely to become legacy code. @AdamMc331 #AndroidSummit 21

Slide 46

Slide 46 text

Strict Dependencies // Codebase strongly relies on `MyErrorTool` class ProfileViewModel( private val errorTool: MyErrorTool = MyErrorTool.getInstance() ) : ViewModel() @AdamMc331 #AndroidSummit 22

Slide 47

Slide 47 text

Strict Dependencies // Codebase strongly relies on `MyErrorTool` class ProfileViewModel( private val errorTool: MyErrorTool = MyErrorTool.getInstance() ) : ViewModel() » This class is strictly tied to MyErrorTool method contracts. @AdamMc331 #AndroidSummit 22

Slide 48

Slide 48 text

Strict Dependencies // Codebase strongly relies on `MyErrorTool` class ProfileViewModel( private val errorTool: MyErrorTool = MyErrorTool.getInstance() ) : ViewModel() » This class is strictly tied to MyErrorTool method contracts. » Despite dependency injection, this will make swapping our error tool difficult. @AdamMc331 #AndroidSummit 22

Slide 49

Slide 49 text

Wrapping Dependencies interface ErrorReporter { fun reportError(error: Throwable) } class FirebaseErrorReporter : ErrorReporter { override fun reportError(error: Throwable) { Firebase.getInstance().logException(error) } } @AdamMc331 #AndroidSummit 23

Slide 50

Slide 50 text

Wrapping Dependencies // Firebase is default, but we have the ability // to pass anything we want here. class ProfileViewModel( private val errorReporter: ErrorReporter = FirebaseErrorReporter() ) : ViewModel() @AdamMc331 #AndroidSummit 24

Slide 51

Slide 51 text

Wrapping Dependencies @AdamMc331 #AndroidSummit 25

Slide 52

Slide 52 text

Wrapping Dependencies » Easier to provide fake/mock implementations in tests. @AdamMc331 #AndroidSummit 25

Slide 53

Slide 53 text

Wrapping Dependencies » Easier to provide fake/mock implementations in tests. » Easier to entirely swap third party tools. @AdamMc331 #AndroidSummit 25

Slide 54

Slide 54 text

Wrapping Dependencies » Easier to provide fake/mock implementations in tests. » Easier to entirely swap third party tools. » Easier to update tools. @AdamMc331 #AndroidSummit 25

Slide 55

Slide 55 text

Wrapping Dependencies val coreModule = module { /** * Change the reporter supplied by your DI manager, * the entire app just updates! */ single { FirebaseErrorReporter() // SentryErrorReporter() // EmbraceErrorReporter() } } @AdamMc331 #AndroidSummit 26

Slide 56

Slide 56 text

Wrapping Dependencies class FirebaseErrorReporter : ErrorReporter { /** * Wrapping this method makes it easy to update our project * if the library changes its method signature. */ override fun reportError(error: Throwable) { // Firebase.getInstance().logException(error) Firebase.getInstance().logError(error) } } @AdamMc331 #AndroidSummit 27

Slide 57

Slide 57 text

These Steps Avoid A Lot Of Legacy Code Pain Points @AdamMc331 #AndroidSummit 28

Slide 58

Slide 58 text

These Steps Avoid A Lot Of Legacy Code Pain Points » We have testable code that we can ship with confidence. @AdamMc331 #AndroidSummit 28

Slide 59

Slide 59 text

These Steps Avoid A Lot Of Legacy Code Pain Points » We have testable code that we can ship with confidence. » We have decoupled third party tools, giving us freedom to swap vendors with ease. @AdamMc331 #AndroidSummit 28

Slide 60

Slide 60 text

These Steps Avoid A Lot Of Legacy Code Pain Points » We have testable code that we can ship with confidence. » We have decoupled third party tools, giving us freedom to swap vendors with ease. » We can confidently upgrade our third party tools and limit the code changes required. @AdamMc331 #AndroidSummit 28

Slide 61

Slide 61 text

Legacy Code Is Code No One On The Team Understands @AdamMc331 #AndroidSummit 29

Slide 62

Slide 62 text

Legacy Code Is Code No One On The Team Understands Doesn't Have Documentation @AdamMc331 #AndroidSummit 30

Slide 63

Slide 63 text

How Undocumented Code Becomes Legacy Code @AdamMc331 #AndroidSummit 31

Slide 64

Slide 64 text

How Undocumented Code Becomes Legacy Code 1. The author of the code understood what they were writing. @AdamMc331 #AndroidSummit 31

Slide 65

Slide 65 text

How Undocumented Code Becomes Legacy Code 1. The author of the code understood what they were writing. 2. The author felt that the code was self documenting. @AdamMc331 #AndroidSummit 31

Slide 66

Slide 66 text

How Undocumented Code Becomes Legacy Code 1. The author of the code understood what they were writing. 2. The author felt that the code was self documenting. 3. The author didn't provide additional context. @AdamMc331 #AndroidSummit 31

Slide 67

Slide 67 text

How Undocumented Code Becomes Legacy Code 1. The author of the code understood what they were writing. 2. The author felt that the code was self documenting. 3. The author didn't provide additional context. 4. The author leaves the team, only for someone else to find this class much later. @AdamMc331 #AndroidSummit 31

Slide 68

Slide 68 text

How Undocumented Code Becomes Legacy Code 1. The author of the code understood what they were writing. 2. The author felt that the code was self documenting. 3. The author didn't provide additional context. 4. The author leaves the team, only for someone else to find this class much later. 5. The new team has no idea how this code works, or if it can be changed. @AdamMc331 #AndroidSummit 31

Slide 69

Slide 69 text

Looking After Future Developers With Documentation @AdamMc331 #AndroidSummit 32

Slide 70

Slide 70 text

Document When You Rely External Code Samples /** * Feature XYZ requires a ViewPager like experience, but * without the user being able to control each step. We've * implemented a ViewPager that rejects any user interraction. * * Source: https://stackoverflow.com/a/9650884/3131147 */ class NonSwipeableViewPager( context: Context, attrs: AttributeSet? = null ) : ViewPager(context, attrs) { } @AdamMc331 #AndroidSummit 33

Slide 71

Slide 71 text

Document When You Rely External Code Samples /** * Feature XYZ requires a ViewPager like experience, but * without the user being able to control each step. We've * implemented a ViewPager that rejects any user interraction. * * Source: https://stackoverflow.com/a/9650884/3131147 */ class NonSwipeableViewPager( context: Context, attrs: AttributeSet? = null ) : ViewPager(context, attrs) { } » This allows future devs to understand why this class was added, and explore if there are new first-party solutions. @AdamMc331 #AndroidSummit 33

Slide 72

Slide 72 text

Document When You Work Around Library Bugs /** * Due to github.com/company/library/issues/1234 we needed * to implement this work around to prevent a crash on * Android devices running API 21. */ private fun doLibraryWorkAround() { // ... } @AdamMc331 #AndroidSummit 34

Slide 73

Slide 73 text

Document When You Work Around Library Bugs /** * Due to github.com/company/library/issues/1234 we needed * to implement this work around to prevent a crash on * Android devices running API 21. */ private fun doLibraryWorkAround() { // ... } » This allows future devs to understand what this block of code is doing, and explore if the bug has been fixed in a subsequent library update. @AdamMc331 #AndroidSummit 34

Slide 74

Slide 74 text

Document When You Use Libraries For Specific Use Cases implementation("androidx.security:security-crypto:$rootProject.ext.versions.security") { because("This dependency allows us to use EncryptedSharedPreferences to store sensitive information.") } @AdamMc331 #AndroidSummit 35

Slide 75

Slide 75 text

Document When You Use Libraries For Specific Use Cases implementation("androidx.security:security-crypto:$rootProject.ext.versions.security") { because("This dependency allows us to use EncryptedSharedPreferences to store sensitive information.") } » This allows future devs to update version numbers with confidence, as well as understanding what to test, and why this library was chosen. @AdamMc331 #AndroidSummit 35

Slide 76

Slide 76 text

Why This Documentation Matters @AdamMc331 #AndroidSummit 36

Slide 77

Slide 77 text

Why This Documentation Matters We're not trying to prevent code from becoming obsolete and outdated. We're trying to help the future maintainers respond accordingly. @AdamMc331 #AndroidSummit 37

Slide 78

Slide 78 text

Recap @AdamMc331 #AndroidSummit 38

Slide 79

Slide 79 text

Recap » Writing testable code with decent coverage helps future developers refactor with confidence. @AdamMc331 #AndroidSummit 38

Slide 80

Slide 80 text

Recap » Writing testable code with decent coverage helps future developers refactor with confidence. » Wrapping dependencies allows future developers to update and replace tooling easily. @AdamMc331 #AndroidSummit 38

Slide 81

Slide 81 text

Recap » Writing testable code with decent coverage helps future developers refactor with confidence. » Wrapping dependencies allows future developers to update and replace tooling easily. » Documenting certain decisions allows future developers to confidently understand, refactor, and replace code written by someone else. @AdamMc331 #AndroidSummit 38

Slide 82

Slide 82 text

Remember The goal isn't to completely eradicate legacy code. It's to look out for our future teammates by not leaving behind rigid, confusing, and unmaintainable code. @AdamMc331 #AndroidSummit 39

Slide 83

Slide 83 text

Thanks! @AdamMc331 #AndroidSummit 40