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

The intrepid adventure of scaling a mobile app

The intrepid adventure of scaling a mobile app

Slides from the talk that I gave in T3chfest 2018 about my experience helping to scale mobile apps codebases

Pedro Piñera Buendía

February 25, 2018
Tweet

More Decks by Pedro Piñera Buendía

Other Decks in Technology

Transcript

  1. About me — Production Engineer at Shopify. — Building tools

    for mobile developers. — Born and raised in the ❤ Murcia. — Suffering the ☔ in Berlin. — # ppinera.es — $ @pepibumur — % [email protected] 2
  2. Scaling a mobile codebase "Scalability is the capability of a

    system, network, or process to handle a growing amount of work, or its potential to be enlarged to accommodate that growth" - Wikipedia @pepibumur 3
  3. The codebase doesn't scale when... — 20 engineers produce the

    same as 10 — You spend more time fighting the tools — You can prepare & drink a coffee while CI runs — Random issues arise in a daily basis — There are conflicts very often @pepibumur 4
  4. If you are not experimenting scalability issues, they will come

    up sooner or later (don't build for scale, think for scale) @pepibumur 6
  5. Changing environment — Larger products — More APIs — More

    platforms to support — New programming languages — People joining/leaving the team @pepibumur 7
  6. ! SoundCloud — Compilation times. — Inconsistencies in settings. !

    Shopify — Multiple mobile apps. — Historically no shared code. — Compilation times. @pepibumur 9
  7. Key points 1. Make features independent (from UI to Data)

    — Reduce friction 2. Reduce dependencies between features (atomicity) — Allow autonomy 3. Share as much code as possible — Save time @pepibumur 12
  8. Storing data — Collections ➡ SQLite / Core Data /

    Realm — Secure ➡ Keychain / AccountManager — Other ➡ UserDefaults / SharedPreferences @pepibumur 13
  9. Example — Feature A: Deletes a token from the Keychain

    — Feature B: Expects the token to be there to work. — Result: Feature B behaving unexpectedly @pepibumur 15
  10. Make your stores observable And let other features know about

    the state changes SecureStore.instance.observe(.token) { [weak self] token in if token == nil { self?.deleteData() } } @pepibumur 16
  11. Example — Feature A: Saves /Documents/orders.json. — Feature B: Cleans

    the folder /Documents. — Feature A: Tries to access /Documents/orders.json. — Result: Unexpected behaviour from feature A @pepibumur 18
  12. Be!er approach // A store to save orders related data

    let store = StoreManager.instance.store(for: .orders) // We interact with our own disk space try! store.save(json, name: "orders.json") try! store.cleanAll() store.observe("orders.json") { orders in // Orders changed } @pepibumur 19
  13. Relationships class Product: NSManagedObject {} class Order: NSManagedObject { @dynamic

    var products: [Product] } class Client: NSManagedObject { @dynamic var orders: [Order] } — Can we safely delete an order? — Do I have to update the order delivery address if the client address changes? — Do I have to update an order if a product gets removed? @pepibumur 22
  14. Explicit APIs — If you use SQL, don't use relationships

    — Aim for APIs that other features can consume. let order = ordersRepository.new(clientId: clientId) let token = ordersRepository.observe(clientId: clientId) { orders in // Client orders changed } @pepibumur 24
  15. Maybe you don't need a SQL store ! . Just

    serialize objects into disk @pepibumur 25
  16. // Module Core public class Client { public fund execute(request:

    Request) -> Response private fund privateMethod() { /* some code */ } } // Module App class ProfileInteractor { let client: Client func sync() { client.privateMethod() // Compiler error! let response = client.execute(request: User.me) // All right } } @pepibumur 27
  17. Other programming paradigms might have benefits, but its usage in

    large teams is questionable @pepibumur 30
  18. Use case: Reactive programming — Everyone in your team needs

    to learn it. — They need to learn it properly. — You'll most likely rely on a third party dependency (RxSwift, RxJava...) @pepibumur 31
  19. Because... — !" know about the system patterns. — #

    You get updates/improvements from the platform. — $ Building an abstraction that scale is hard in practice. — % The fewer dependencies, the better. @pepibumur 33
  20. Minimize dependencies — Dependencies save time initially, but need to

    be maintained. — Do you need dependency X: — Yes: Use it but with an abstraction layer. — No: Then don't use it or build just what you need. @pepibumur 34
  21. 1. I start defining my layout 2. I add this

    button here. 3. I add this list of images there. 4. I introduce this new button between these two elements 5. ... 6. ... 7. ... 8. No one can modify the XML/XIB/storyboard anymore @pepibumur 37
  22. Use your intuition if the layout is growing considerably then

    split it in small layouts @pepibumur 38
  23. Don't spend your developers' time There are great tools already

    built to help you with automation (e.g. Fastlane) @pepibumur 42
  24. Build times The more code you have, the more it

    takes to compile (unless you use React Native) @pepibumur 43
  25. Gradle & Xcode Build system build incrementally Until you clean

    the build ! And I'm sure it's the first thing you do when your project doesn't compile. @pepibumur 44
  26. Let's do some maths — Clean build: 10 minutes —

    Cleans per day per developer: 10 — Developers in the team: 30 Build time spent per month - 1000 hours - 6.25 developers - 21% of your team @pepibumur 45
  27. Make your app modular — Cleaning should only affect the

    current module. — Developers should only compile the module they are working on (and its dependencies). — Reference: Microfeatures @pepibumur 46
  28. You can go further And try other build systems that

    build incrementally across developers in a team Buck from Facebook Bazel from Google @pepibumur 47
  29. If clean builds are slow... So they are on CI

    Continuous integration services/platforms Build from a clean environment @pepibumur 48
  30. Can we speed up clean builds on CI? — Selective

    build/run tests based on the files that were modified. — Parallelize work. — Cache build artifacts across builds (e.g. Pods, Bundler dependencies) — Use Buck/Bazel. @pepibumur 49
  31. What to measure? ! — Compilation time of each of

    the modules. — App startup time. — Flaky tests consistently failing. — Failing builds and the reason. @pepibumur 51
  32. Reproducible builds "My build is failing on CI but it

    doesn't fail locally" @pepibumur 53
  33. How to make your builds reproducible — Avoid custom logic

    that only runs on CI. — Pin your dependencies versions. — Bundle your dependencies into the repository and avoid access the shared system environment. — Use Nix @pepibumur 54
  34. Feature teams / Squads ! They are independent. " They

    might duplicate code. " Patterns/style might diverge a lot. @pepibumur 57
  35. Be consistent — Build settings. — Tools. — Processes. —

    Patterns. — Programming languages. @pepibumur 58
  36. Mobops team !" — Has an overview of the project

    as a whole. — Identifies patterns across teams and extracts reusable components. — Builds tools to make teams productive. — Provides teams with communication tools/channels. — Removes any friction between teams making them as atomic as possible. @pepibumur 59
  37. Collective meetings Where all the iOS/Android developers from different teams

    come together to talk about the vision of the project @pepibumur 60
  38. — If you are not facing the challenges I talked

    about, you'll do sooner or later. — Don't let yourself be influenced by hype technologies. — Automate as much as you can. — Don't expect Apple/Google to optimise the tools for scale. — An awesome product is the result of motivated developers. @pepibumur 62