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

Db84cf61fdada06b63f43f310b68b462?s=128

CocoaHeads Ukraine

March 01, 2019
Tweet

More Decks by CocoaHeads Ukraine

Other Decks in Programming

Transcript

  1. Vitalii Malakhovskyi vitalii malakhovskyi, betterme 1

  2. software engineer vitalii malakhovskyi, betterme 2

  3. Continuous Analytics vitalii malakhovskyi, betterme 3

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

    how to reduce human mistake? • how to speed up development? vitalii malakhovskyi, betterme 4
  5. ? vitalii malakhovskyi, betterme 5

  6. continuous analytics is a way to put analytic process into

    agile — wiki vitalii malakhovskyi, betterme 6
  7. vitalii malakhovskyi, betterme 7

  8. so what is analytics for us, developers? vitalii malakhovskyi, betterme

    8
  9. vitalii malakhovskyi, betterme 9

  10. not about that vitalii malakhovskyi, betterme 10

  11. events • name • category • parameters • sums vitalii

    malakhovskyi, betterme 11
  12. !"#$% !"#$ !"# !" ! tracking systems vitalii malakhovskyi, betterme

    12
  13. class AnalyticsTracker { func track(event: Event) } vitalii malakhovskyi, betterme

    13
  14. let's focus on events vitalii malakhovskyi, betterme 14

  15. where? vitalii malakhovskyi, betterme 15

  16. google sheets vitalii malakhovskyi, betterme 16

  17. vitalii malakhovskyi, betterme 17

  18. vitalii malakhovskyi, betterme 18

  19. what about code??? vitalii malakhovskyi, betterme 19

  20. preventing errors (by making another errors) vitalii malakhovskyi, betterme 20

  21. enum Profile { case appear case addHeightClick(height: Double) case addWeightClick(weight:

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

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

    { case .appear return "profile_appear" // other events } } } vitalii malakhovskyi, betterme 23
  24. 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
  25. enum Event { case activeTraining(ActiveTraining) case profile(Profile) case congratulation(Congratulation) enum

    Profile { ... } } vitalii malakhovskyi, betterme 25
  26. class ViewModel { let analyticsTracker: AnalyticsTracker func handleHeight(with value: Double)

    { analyticsTracker.track(.profile(.addHeightClick(value))) } } vitalii malakhovskyi, betterme 26
  27. first release vitalii malakhovskyi, betterme 27

  28. Pros • single source of truth • static type check

    • can be tested vitalii malakhovskyi, betterme 28
  29. Cons • analytics tracker everywhere • swift type system won't

    help ! vitalii malakhovskyi, betterme 29
  30. class ViewModel { let analyticsTracker: AnalyticsTracker func handleHeight(with value: Double)

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

    { analyticsTracker.track( Event.profile(Profile.addHeightClick(value)) ) } } ! vitalii malakhovskyi, betterme 31
  32. vitalii malakhovskyi, betterme 32

  33. single page of truth - is it really good? vitalii

    malakhovskyi, betterme 33
  34. vitalii malakhovskyi, betterme 34

  35. problem, it`s when something small and cute becomes into vitalii

    malakhovskyi, betterme 35
  36. vitalii malakhovskyi, betterme 36

  37. 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
  38. vitalii malakhovskyi, betterme 38

  39. Pros • static type check vitalii malakhovskyi, betterme 39

  40. 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
  41. vitalii malakhovskyi, betterme 41

  42. what to do? vitalii malakhovskyi, betterme 42

  43. add versioning to analytics google sheets + tabs + clearness

    = versioning vitalii malakhovskyi, betterme 43
  44. vitalii malakhovskyi, betterme 44

  45. what about CODEGEN? vitalii malakhovskyi, betterme 45

  46. ! " ! " ! ! ! " vitalii malakhovskyi,

    betterme 46
  47. google: hot export sheets to json? http://blog.pamelafox.org/2013/06/exporting-google- spreadsheet-as-json.html vitalii malakhovskyi,

    betterme 47
  48. vitalii malakhovskyi, betterme 48

  49. vitalii malakhovskyi, betterme 49

  50. rasp (file) vitalii malakhovskyi, betterme 50

  51. vitalii malakhovskyi, betterme 51

  52. vitalii malakhovskyi, betterme 52

  53. fastlane ruby vitalii malakhovskyi, betterme 53

  54. JSON ! Swift vitalii malakhovskyi, betterme 54

  55. ugliest, unsupportable, unreadable vitalii malakhovskyi, betterme 55

  56. solves issue! vitalii malakhovskyi, betterme 56

  57. event struct Event { let name: String let parameters: [String:

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

    you cannot create enum if there is no cases vitalii malakhovskyi, betterme 58
  59. why "A"? vitalii malakhovskyi, betterme 59

  60. swiftgen produces • L - localization • S - storyboards

    • F - fonts • I - image assets • C - constants • CLR - colors vitalii malakhovskyi, betterme 60
  61. A - analytics vitalii malakhovskyi, betterme 61

  62. more namespace2 + satisfy swift type system enum Onboarding {

    ... } enum Profile { ... } 2 each category has each namespace vitalii malakhovskyi, betterme 62
  63. 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
  64. 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
  65. 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
  66. swift type system helps now class ViewModel { let analyticsTracker:

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

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

    • single source of analytics (versioned) • scalable vitalii malakhovskyi, betterme 68
  69. generate events for vitalii malakhovskyi, betterme 69

  70. Cons • still a lot of di (that's anouther story)

    vitalii malakhovskyi, betterme 70
  71. a little bit of analytics in analytics vitalii malakhovskyi, betterme

    71
  72. average analytics ticket time without codegen 3.8 hours with codegen

    2.8 hours codegen + redux middleware 1.1 hour vitalii malakhovskyi, betterme 72
  73. but there still a lot of places for improvements vitalii

    malakhovskyi, betterme 73
  74. 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
  75. now: parameter: value: option1 or option2 example: static func someEvent(value:

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

    func someEvent(value: Option) -> Event vitalii malakhovskyi, betterme 76
  77. lesson learned: analyse and improve your process vitalii malakhovskyi, betterme

    77
  78. !!!!!! vitalii malakhovskyi, betterme 78

  79. purpleshirted Vitalii Malakhovskyi VitaliyMal vmalakhovskiy vitalii malakhovskyi, betterme 79

  80. questions & answers vitalii malakhovskyi, betterme 80

  81. vitalii malakhovskyi, betterme 81