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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
Wrapping Dependencies » Easier to provide fake/mock implementations in tests. » Easier to entirely swap third party tools. @AdamMc331 #AndroidSummit 25
Wrapping Dependencies » Easier to provide fake/mock implementations in tests. » Easier to entirely swap third party tools. » Easier to update tools. @AdamMc331 #AndroidSummit 25
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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