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. Vitalii
    Malakhovskyi
    vitalii malakhovskyi, betterme 1

    View Slide

  2. software engineer
    vitalii malakhovskyi, betterme 2

    View Slide

  3. Continuous
    Analytics
    vitalii malakhovskyi, betterme 3

    View Slide

  4. we will talk about
    • how to manage analytics?
    • how to reduce human mistake?
    • how to speed up development?
    vitalii malakhovskyi, betterme 4

    View Slide

  5. ?
    vitalii malakhovskyi, betterme 5

    View Slide

  6. continuous analytics is a
    way to put analytic
    process into agile
    — wiki
    vitalii malakhovskyi, betterme 6

    View Slide

  7. vitalii malakhovskyi, betterme 7

    View Slide

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

    View Slide

  9. vitalii malakhovskyi, betterme 9

    View Slide

  10. not about
    that
    vitalii malakhovskyi, betterme 10

    View Slide

  11. events
    • name
    • category
    • parameters
    • sums
    vitalii malakhovskyi, betterme 11

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  15. where?
    vitalii malakhovskyi, betterme 15

    View Slide

  16. google sheets
    vitalii malakhovskyi, betterme 16

    View Slide

  17. vitalii malakhovskyi, betterme 17

    View Slide

  18. vitalii malakhovskyi, betterme 18

    View Slide

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

    View Slide

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

    View Slide

  21. enum Profile {
    case appear
    case addHeightClick(height: Double)
    case addWeightClick(weight: Double)
    // other events
    }
    vitalii malakhovskyi, betterme 21

    View Slide

  22. protocol EventNameProvider {
    var name: String { get }
    }
    protocol EventParametersProvider {
    var parameters: [String: String] { get }
    }
    vitalii malakhovskyi, betterme 22

    View Slide

  23. extension Profile: EventNameProvider {
    var name: String {
    switch self {
    case .appear
    return "profile_appear"
    // other events
    }
    }
    }
    vitalii malakhovskyi, betterme 23

    View Slide

  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

    View Slide

  25. enum Event {
    case activeTraining(ActiveTraining)
    case profile(Profile)
    case congratulation(Congratulation)
    enum Profile { ... }
    }
    vitalii malakhovskyi, betterme 25

    View Slide

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

    View Slide

  27. first release
    vitalii malakhovskyi, betterme 27

    View Slide

  28. Pros
    • single source of truth
    • static type check
    • can be tested
    vitalii malakhovskyi, betterme 28

    View Slide

  29. Cons
    • analytics tracker everywhere
    • swift type system won't help
    !
    vitalii malakhovskyi, betterme 29

    View Slide

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

    View Slide

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

    View Slide

  32. vitalii malakhovskyi, betterme 32

    View Slide

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

    View Slide

  34. vitalii malakhovskyi, betterme 34

    View Slide

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

    View Slide

  36. vitalii malakhovskyi, betterme 36

    View Slide

  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

    View Slide

  38. vitalii malakhovskyi, betterme 38

    View Slide

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

    View Slide

  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

    View Slide

  41. vitalii malakhovskyi, betterme 41

    View Slide

  42. what to do?
    vitalii malakhovskyi, betterme 42

    View Slide

  43. add versioning to analytics
    google sheets + tabs + clearness = versioning
    vitalii malakhovskyi, betterme 43

    View Slide

  44. vitalii malakhovskyi, betterme 44

    View Slide

  45. what about
    CODEGEN?
    vitalii malakhovskyi, betterme 45

    View Slide

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

    View Slide

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

    View Slide

  48. vitalii malakhovskyi, betterme 48

    View Slide

  49. vitalii malakhovskyi, betterme 49

    View Slide

  50. rasp (file)
    vitalii malakhovskyi, betterme 50

    View Slide

  51. vitalii malakhovskyi, betterme 51

    View Slide

  52. vitalii malakhovskyi, betterme 52

    View Slide

  53. fastlane ruby
    vitalii malakhovskyi, betterme 53

    View Slide

  54. JSON
    !
    Swift
    vitalii malakhovskyi, betterme 54

    View Slide

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

    View Slide

  56. solves issue!
    vitalii malakhovskyi, betterme 56

    View Slide

  57. event
    struct Event {
    let name: String
    let parameters: [String: String]
    }
    vitalii malakhovskyi, betterme 57

    View Slide

  58. namespace with enum1
    enum A {
    ...
    }
    1 Because you cannot create enum if there is no cases
    vitalii malakhovskyi, betterme 58

    View Slide

  59. why "A"?
    vitalii malakhovskyi, betterme 59

    View Slide

  60. swiftgen produces
    • L - localization
    • S - storyboards
    • F - fonts
    • I - image assets
    • C - constants
    • CLR - colors
    vitalii malakhovskyi, betterme 60

    View Slide

  61. A - analytics
    vitalii malakhovskyi, betterme 61

    View Slide

  62. more namespace2 + satisfy swift type
    system
    enum Onboarding {
    ...
    }
    enum Profile {
    ...
    }
    2 each category has each namespace
    vitalii malakhovskyi, betterme 62

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  66. swift type system helps now
    class ViewModel {
    let analyticsTracker: AnalyticsTracker
    func handleWhatever() {
    analyticsTracker.track(A.Onboarding.onbGoalsView)
    }
    }
    vitalii malakhovskyi, betterme 66

    View Slide

  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

    View Slide

  68. Pros
    • events codegen
    • swift type system says
    !
    • single source of analytics (versioned)
    • scalable
    vitalii malakhovskyi, betterme 68

    View Slide

  69. generate
    events for
    vitalii malakhovskyi, betterme 69

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  73. but there still a lot of
    places for
    improvements
    vitalii malakhovskyi, betterme 73

    View Slide

  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

    View Slide

  75. now:
    parameter:
    value: option1 or option2
    example:
    static func someEvent(value: String) -> Event
    vitalii malakhovskyi, betterme 75

    View Slide

  76. desired:
    enum Option {
    case option1
    case option2
    }
    static func someEvent(value: Option) -> Event
    vitalii malakhovskyi, betterme 76

    View Slide

  77. lesson learned:
    analyse and improve your process
    vitalii malakhovskyi, betterme 77

    View Slide

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

    View Slide

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

    View Slide

  80. questions & answers
    vitalii malakhovskyi, betterme 80

    View Slide

  81. vitalii malakhovskyi, betterme 81

    View Slide