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

Continuous Analytics. Как мы это поняли, что мы сделали и как это стало экономить нам время

Continuous Analytics. Как мы это поняли, что мы сделали и как это стало экономить нам время

Talk by Vitalii Malakhovskiy.

Тема доклада - “Continuous Analytics. Как мы это поняли, что мы сделали и как это стало экономить нам время”.

В своем выступлении Виталий поделится следующим опытом команды:
- работа с аналитикой и поддержка ее актуальности;
- проблемы, с которыми они столкнулись при ее масштабировании;
- как начать автоматически генерировать часть кода для аналитики;
- что нужно сделать чтобы система типов помогала с аналитикой.

This talk was made for CocoaFriday #1 ( https://cocoaheads.org.ua/cocoafriday/1 ) which took place Mar 1, 2019.

Video is available here: https://youtu.be/rYSXGGfLDak

CocoaHeads Ukraine

March 01, 2019
Tweet

More Decks by CocoaHeads Ukraine

Other Decks in Programming

Transcript

  1. we will talk about • how to manage analytics? •

    how to reduce human mistake? • how to speed up development? vitalii malakhovskyi, betterme 4
  2. continuous analytics is a way to put analytic process into

    agile — wiki vitalii malakhovskyi, betterme 6
  3. enum Profile { case appear case addHeightClick(height: Double) case addWeightClick(weight:

    Double) // other events } vitalii malakhovskyi, betterme 21
  4. protocol EventNameProvider { var name: String { get } }

    protocol EventParametersProvider { var parameters: [String: String] { get } } vitalii malakhovskyi, betterme 22
  5. extension Profile: EventNameProvider { var name: String { switch self

    { case .appear return "profile_appear" // other events } } } vitalii malakhovskyi, betterme 23
  6. extension Profile: EventParametersProvider { var parameters: [String: String] { switch

    self { case .addHeightClick(let value): return [ "height": value, "measurement": "metric", "category": "profile" ] // other events } } } vitalii malakhovskyi, betterme 24
  7. class ViewModel { let analyticsTracker: AnalyticsTracker func handleHeight(with value: Double)

    { analyticsTracker.track(.profile(.addHeightClick(value))) } } vitalii malakhovskyi, betterme 26
  8. Pros • single source of truth • static type check

    • can be tested vitalii malakhovskyi, betterme 28
  9. class ViewModel { let analyticsTracker: AnalyticsTracker func handleHeight(with value: Double)

    { analyticsTracker.track( Event.profile(Profile.addHeightClick(value)) ) } } vitalii malakhovskyi, betterme 30
  10. class ViewModel { let analyticsTracker: AnalyticsTracker func handleHeight(with value: Double)

    { analyticsTracker.track( Event.profile(Profile.addHeightClick(value)) ) } } ! vitalii malakhovskyi, betterme 31
  11. why it is problem: • ! yesterday : rx, viper,

    ml, ci/cd • " today: add few analytics events • ! today: sure, just let me put 80% of time to write some boilerplate code vitalii malakhovskyi, betterme 37
  12. Cons • single page of analytics (no versioning) • human

    factor - incorrect naming/mixed arguments • not scalable • swift type system says ! • analytics everywhere (di) vitalii malakhovskyi, betterme 40
  13. add versioning to analytics google sheets + tabs + clearness

    = versioning vitalii malakhovskyi, betterme 43
  14. event struct Event { let name: String let parameters: [String:

    String] } vitalii malakhovskyi, betterme 57
  15. namespace with enum1 enum A { ... } 1 Because

    you cannot create enum if there is no cases vitalii malakhovskyi, betterme 58
  16. swiftgen produces • L - localization • S - storyboards

    • F - fonts • I - image assets • C - constants • CLR - colors vitalii malakhovskyi, betterme 60
  17. more namespace2 + satisfy swift type system enum Onboarding {

    ... } enum Profile { ... } 2 each category has each namespace vitalii malakhovskyi, betterme 62
  18. static variables3 static let onbGoalsView = Event( name: "onb_goals_view", parameters:

    [ "screen_name": "onb_goals", "category": "onboarding" ] ) 3 if there are no arguments vitalii malakhovskyi, betterme 63
  19. static functions4 static func onbGoalNextTap(goal: String) -> Event { return

    Event( name: "onb_goal_next_tap", parameters: [ "goal": goal, "screen_name": "onb_goals", "category": "onboarding" ] ) } 4 if there are some arguments vitalii malakhovskyi, betterme 64
  20. all-together enum A { enum Onboarding { static let onbGoalsView:

    Event static func onbGoalNextTap(goal: String) -> Event } enum Profile { static let profileView: Event static func demoCardTap(card: Int) -> Event } } vitalii malakhovskyi, betterme 65
  21. swift type system helps now class ViewModel { let analyticsTracker:

    AnalyticsTracker func handleWhatever() { analyticsTracker.track(A.Onboarding.onbGoalsView) } } vitalii malakhovskyi, betterme 66
  22. in functions too class ViewModel { let analyticsTracker: AnalyticsTracker func

    handleHeight(with value: Double) { analyticsTracker.track( A.Onboarding.onbGoalNextTap(goal: value) ) } } vitalii malakhovskyi, betterme 67
  23. Pros • events codegen • swift type system says !

    • single source of analytics (versioned) • scalable vitalii malakhovskyi, betterme 68
  24. Cons • still a lot of di (that's anouther story)

    vitalii malakhovskyi, betterme 70
  25. average analytics ticket time without codegen 3.8 hours with codegen

    2.8 hours codegen + redux middleware 1.1 hour vitalii malakhovskyi, betterme 72
  26. improvements to be made • full cycle in one command

    (export to json, put into project, generate -> generate with one command) • algebraic data types support vitalii malakhovskyi, betterme 74
  27. now: parameter: value: option1 or option2 example: static func someEvent(value:

    String) -> Event vitalii malakhovskyi, betterme 75
  28. desired: enum Option { case option1 case option2 } static

    func someEvent(value: Option) -> Event vitalii malakhovskyi, betterme 76