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

Productive Mobile Development for Engineers

Productive Mobile Development for Engineers

Tips and Tricks for Productive Mobile Development. Given in Droidcon Egypt on October 21st, 2022.

Ahmed El-Helw

October 21, 2022
Tweet

More Decks by Ahmed El-Helw

Other Decks in Programming

Transcript

  1. Factors affecting Speed Things to Consider • Distractions • Communication

    Problems • Con fl icting Priorities • Technical Debt
  2. Factors affecting Speed Things to Consider • Distractions • Communication

    Problems • Con fl icting Priorities • Technical Debt • Production Fires
  3. Factors affecting Speed Things to Consider • Distractions • Communication

    Problems • Con fl icting Priorities • Technical Debt • Production Fires • Hardware
  4. Process Bottlenecks Mobile Bottlenecks • Company release process • Application

    store review process • Application store rejections • Adoptions take time
  5. Customer Behavior Understanding and Monitoring Customer Behavior • Dashboards •

    critical business statistics • conversion funnels • Slice by application version • Alerts
  6. Dealing with Problems How to React to Problems • Ignore

    it for now • Hot Fix in Production • Release takes time • Adoption takes time • Risky
  7. Other Considerations Things to Keep in Mind • Be mindful

    about: • The e ff ect of experiments on each other • The e ff ect of feature fl ags on each other • Dead Experiments increase Technical Debt • Can tooling help here?
  8. Data Some odds and ends about Data • Building trust

    with Customers • Compliance • App Stores • Governments
  9. Modularization Structuring Code • Have a set of rules and

    guidelines • Split APIs and Implementation
  10. Modularization Structuring Code / / in profile-api module interface Profile

    { fun isLoggedIn(): Boolean fun currentCustomer(): Customer? fun editCustomerIntent(context: Context): Intent? }
  11. Modularization Structuring Code / / in profile module class ProfileImpl

    constructor( private val profileRepository: ProfileRepository ) : Profile { override fun isLoggedIn(): Boolean = currentCustomer() != null override fun currentCustomer(): Customer? { return profileRepository.provideCustomer() } override fun editCustomerIntent(context: Context): Intent { return Intent(context, EditProfileActivity : : class.java) } }
  12. Modularization Structuring Code / / in profile-noop module class ProfileNoop

    : Profile { override fun isLoggedIn(): Boolean = true override fun currentCustomer(): Customer = Customer(123L, "Test") override fun editCustomerIntent(context: Context) = null }
  13. Modularization Structuring Code fun provideProfile(): Profile { return if (isDebug)

    { ProfileNoop() } else { ProfileImpl(ProfileRepository()) } }
  14. Benefits of Modularization Structuring Code • Better isolation of Features

    • Easier sharing • Easier and faster testing • Ability to stub implementations • Standalone apps
  15. Frameworks that Handle Everything Sharing Code Between Platforms • Pros

    • Potentially easier to hire for • Write once, use everywhere • Faster development (hot reload, etc) • Potential for hot loading code
  16. Frameworks that Handle Everything Sharing Code Between Platforms • Cons

    • Need to explicitly build separate UI for iOS and Android • Doesn’t “feel” native irrespective • New platform features are di ffi cult to adopt - need to wait • Overhead on binary size • Rejections Likely
  17. C/C++ Sharing Code Between Platforms • Cons • Di ffi

    cult • Still have to handle di ff erences between platforms • Custom development environment
  18. Kotlin Multiplatform Sharing Code Between Platforms • Pros • Completely

    native • Easy to learn for engineers writing Swift • Lots of third party libraries • Backed by JetBrains and Google • Supports a plethora of platforms
  19. Kotlin Multiplatform Sharing Code Between Platforms • Cons • iOS

    inter-op with Objective-C instead of Swift • Convincing iOS Engineers is Di ffi cult • Tooling • Still in the process of maturing
  20. Alternatives Sharing Between Platforms • Code generation • Annotation processors

    • Compiler plugins, Gradle plugins • Templating code generators (Stencil, …)
  21. Sharing Code Generating Code data class Car( val make :

    String, val model : String, val vin : String, val vendor : String, )
  22. Sharing Code Generating Code struct Car { let make: String

    let model: String let vin: String let vendor: String }
  23. Sharing Code Generating Code data class {{ component.name }}( {%

    for key, value in component.fields %} val {{ key | lowerFirstLetter }} : {{ value | upperFirstLetter }}, {% endfor %} )
  24. Sharing Code Generating Code struct {{ component.name }} { {%

    for key, value in component.fields %} let {{ key | lowerFirstLetter }}: {{ value | upperFirstLetter }} {% endfor %} }
  25. Things to Share Sharing Between Platforms • Analytics • Business

    Logic • Vocabulary • Architecture • Design System • Versioning • Release Process
  26. First Lines of Defense Catching Issues Early • The build

    process itself • Automated checkers • Lint • Code styling enforcers • Code smell detection • Coverage • Code Review helps catch issues early
  27. Tests Catching Issues Early • Tests • Unit Tests •

    Integration Tests • On-Device Integration Tests (“UI Tests”) • Screenshot Tests • Maestro Tests
  28. Dependency-Diff-Tldr Exposing Hidden Changes Upgraded Dependencies 
 com.squareup.moshi:moshi-adapters:1.14.0, (changed from

    1.13.0) 
 org.jetbrains.kotlin:kotlin-stdlib:1.7.20, (changed from 1.7.10) 
 org.jetbrains.kotlin:kotlin-stdlib-common:1.7.20, (changed from 1.7.10) 
 org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.20, (changed from 1.7.10) 
 org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.20, (changed from 1.7.10)
  29. Diffuse Exposing Hidden Changes │ compressed │ uncompressed ├───────────┬───────────┬──────────┼───────────┬───────────┬────────── APK

    │ old │ new │ diff │ old │ new │ diff ──────────┼───────────┼───────────┼──────────┼───────────┼───────────┼────────── dex │ 11.6 MiB │ 11.6 MiB │ -107 B │ 29.4 MiB │ 29.4 MiB │ -336 B arsc │ 1.8 MiB │ 1.8 MiB │ -992 B │ 1.8 MiB │ 1.8 MiB │ -988 B manifest │ 5.6 KiB │ 5.6 KiB │ +1 B │ 27.6 KiB │ 27.6 KiB │ 0 B res │ 1.1 MiB │ 1.1 MiB │ -3 KiB │ 1.3 MiB │ 1.3 MiB │ -4.8 KiB asset │ 404.2 KiB │ 404.2 KiB │ 0 B │ 678.6 KiB │ 678.6 KiB │ 0 B other │ 173.6 KiB │ 173.1 KiB │ -550 B │ 336.9 KiB │ 335.7 KiB │ -1.3 KiB ──────────┼───────────┼───────────┼──────────┼───────────┼───────────┼────────── total │ 15.1 MiB │ 15.1 MiB │ -4.6 KiB │ 33.5 MiB │ 33.5 MiB │ -7.4 KiB
  30. Expose Hidden Changes Catching Issues Early • Transitive Dependencies •

    Binary Size • Binary compatibility breaking changes
  31. Binary Compatibility Validator Exposing Hidden Changes FAILURE: Build failed with

    an exception. * What went wrong: Execution failed for task ':miniapp:apiCheck'. > API check failed for project miniapp. --- /bitrise/src/miniapp/api/miniapp.api +++ /bitrise/src/miniapp/build/api/miniapp.api @@ -16,6 +16,7 @@ public abstract interface class com/careem/superapp/lib/miniapp/ IntegrationDependencies { public abstract fun provideEventBus ()Lcom/careem/ superapp/lib/eventbus/EventBus; + public abstract fun provideLocationPickerSdk ()Lcom/ careem/globalexp/locations/api/LocationPickerSdk; } You can run :miniapp:apiDump task to overwrite API declarations
  32. Expose Hidden Changes Catching Issues Early • Transitive Dependencies •

    Binary Size • Binary compatibility breaking changes • Permissions
  33. Expose Hidden Changes Tooling • di ff use • dependency-di

    ff -tldr / dependency-guard • metalava-gradle / binary-compatibility-validator
  34. Releases Catching Issues Early • Beta program • Staged rollouts

    to production • Release early, release often • Release should be a train, not a bus
  35. Dashboards and Data Catching Issues Early • Do not hide

    crashes • Handled exceptions and breadcrumbs are useful • Developer and performance metrics • Development Tooling • Chucker • LeakCanary
  36. Engineers are Humans Engineering Cultures • All humans make mistakes

    • Blame is bad • Blame is the enemy of ownership
  37. When Issues Arise Engineering Cultures • How could this have

    been prevented? • Why was this not caught earlier? • Combine fi xes with veri fi cation tests. • Post mortem