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

Re-architecture

 Re-architecture

Ragunath Jawahar

February 14, 2020
Tweet

More Decks by Ragunath Jawahar

Other Decks in Programming

Transcript

  1. Re-architecture
    Ragunath Jawahar • @ragunathjawahar
    I assume there are tests edition

    View Slide

  2. ? • Started out as a Minimum Viable Product

    • Workarounds and fighting the framework

    • Overcome limitations

    • Accidental complexity (RxJava)

    • Open-Source (funded by a non-profit)

    • Painful tests (1:8)

    View Slide

  3. Where do we start?

    View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. BOUNDARIES!
    (Large surface area)

    View Slide

  9. System Boundaries
    • Network

    • File System / Database / Persistence

    • Hardware

    • UI

    • 3rd Party Integrations

    View Slide

  10. View Slide

  11. View Slide

  12. Network

    View Slide

  13. Network

    View Slide

  14. Network
    Database

    View Slide

  15. Network
    Database

    View Slide

  16. Network
    Database
    User Interface

    View Slide

  17. +
    -
    Network
    Database
    User Interface

    View Slide

  18. +
    -
    Network
    Database
    User Interface
    Inputs

    View Slide

  19. +
    -

    View Slide

  20. +
    -
    0.0v

    View Slide

  21. +
    -
    0.5v
    Network

    View Slide

  22. +
    -
    1.0v Database

    View Slide

  23. +
    -
    1.5v
    User Interface

    View Slide

  24. +
    -
    2.0v
    Network
    User Interface

    View Slide

  25. +
    -

    View Slide

  26. +
    -
    Re-architect

    View Slide

  27. +
    -
    +
    -
    Re-architect

    View Slide

  28. Approach 1: Cloning Tests

    View Slide

  29. Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt

    View Slide

  30. Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt
    Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt

    View Slide

  31. Different package
    Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt
    Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt

    View Slide

  32. Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt
    Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt

    View Slide

  33. Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt
    Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt

    View Slide

  34. Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt
    Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt

    View Slide

  35. Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt
    Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt

    View Slide

  36. Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt
    Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt

    View Slide

  37. Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt
    Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt

    View Slide

  38. Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt
    Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt

    View Slide

  39. Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt
    Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt

    View Slide

  40. Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt
    Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt

    View Slide

  41. View Slide

  42. ff27cb1

    View Slide

  43. Pros
    • Provides a fallback if things go wrong

    • Little refactoring experience

    • Allows complementing asymmetric/low-confidence tests with manual
    testing

    • “Works” even if you don’t have enough tests

    View Slide

  44. Cons
    • All or nothing

    • Hard to accommodate incoming feature requests

    • Duplicate tests and features

    • Reviewing pull requests can be difficult

    • Easy to miss functionality that is not covered by tests

    • Cleaning up old code after feature stabilisation

    View Slide

  45. Approach 2: Strangulation

    View Slide

  46. +
    -
    +
    -
    Re-architect

    View Slide

  47. View Slide

  48. Network
    Database
    User Interface

    View Slide

  49. +
    -
    Inputs

    View Slide

  50. +
    -

    View Slide

  51. +
    -

    View Slide

  52. +
    -

    View Slide

  53. +
    -

    View Slide

  54. +
    -

    View Slide

  55. +
    -

    View Slide

  56. +
    -

    View Slide

  57. +
    -

    View Slide

  58. Setup
    Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt

    View Slide

  59. Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt
    Setup

    View Slide

  60. Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt
    Setup

    View Slide

  61. Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt
    Setup

    View Slide

  62. Tear down
    Test 1
    Test 2
    Test 3
    Test 4
    LoginControllerTest.kt
    Setup

    View Slide

  63. Peek: Tests

    View Slide

  64. View Slide

  65. View Slide

  66. View Slide

  67. View Slide

  68. Peek: Migration

    View Slide

  69. @Test !// TODO: Migrate to Mobius
    fun `when save is clicked then user input should be validated`() {
    whenever(patientRepository.ongoingEntry()).thenReturn(Single.just(OngoingNewPatientEntry()))
    with(uiEvents) {
    onNext(FullNameChanged(""))
    onNext(PhoneNumberChanged(""))
    onNext(DateOfBirthChanged(""))
    onNext(AgeChanged(""))
    onNext(GenderChanged(None))
    onNext(ColonyOrVillageChanged(""))
    onNext(DistrictChanged(""))
    onNext(StateChanged(""))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(DateOfBirthChanged("33/33/3333"))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(AgeChanged(" "))
    onNext(DateOfBirthChanged(""))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(DateOfBirthChanged("16/07/2018"))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(PhoneNumberChanged("1234"))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(PhoneNumberChanged("1234567890987654"))
    onNext(SaveClicked)
    }
    verify(ui, atLeastOnce()).showEmptyFullNameError(true)
    verify(ui, atLeastOnce()).showEmptyDateOfBirthAndAgeError(true)
    verify(ui, atLeastOnce()).showInvalidDateOfBirthError(true)
    verify(ui, atLeastOnce()).showMissingGenderError(true)
    verify(ui, atLeastOnce()).showEmptyColonyOrVillageError(true)
    verify(ui, atLeastOnce()).showEmptyDistrictError(true)
    verify(ui, atLeastOnce()).showEmptyStateError(true)
    verify(ui, atLeastOnce()).showLengthTooShortPhoneNumberError(true)
    verify(ui, atLeastOnce()).showLengthTooLongPhoneNumberError(true)
    }

    View Slide

  70. View Slide

  71. @Test !// TODO: Migrate to Mobius
    fun `when save is clicked then user input should be validated`() {
    whenever(patientRepository.ongoingEntry()).thenReturn(Single.just(OngoingNewPatientEntry()))
    with(uiEvents) {
    onNext(FullNameChanged(""))
    onNext(PhoneNumberChanged(""))
    onNext(DateOfBirthChanged(""))
    onNext(AgeChanged(""))
    onNext(GenderChanged(None))
    onNext(ColonyOrVillageChanged(""))
    onNext(DistrictChanged(""))
    onNext(StateChanged(""))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(DateOfBirthChanged("33/33/3333"))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(AgeChanged(" "))
    onNext(DateOfBirthChanged(""))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(DateOfBirthChanged("16/07/2018"))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(PhoneNumberChanged("1234"))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(PhoneNumberChanged("1234567890987654"))
    onNext(SaveClicked)
    }
    verify(ui, atLeastOnce()).showEmptyFullNameError(true)
    verify(ui, atLeastOnce()).showEmptyDateOfBirthAndAgeError(true)
    verify(ui, atLeastOnce()).showInvalidDateOfBirthError(true)
    verify(ui, atLeastOnce()).showMissingGenderError(true)
    verify(ui, atLeastOnce()).showEmptyColonyOrVillageError(true)
    verify(ui, atLeastOnce()).showEmptyDistrictError(true)
    verify(ui, atLeastOnce()).showEmptyStateError(true)
    verify(ui, atLeastOnce()).showLengthTooShortPhoneNumberError(true)
    verify(ui, atLeastOnce()).showLengthTooLongPhoneNumberError(true)
    }

    View Slide

  72. @Test !// TODO: Migrate to Mobius
    fun `when save is clicked then user input should be validated`() {
    whenever(patientRepository.ongoingEntry()).thenReturn(Single.just(OngoingNewPatientEntry()))
    with(uiEvents) {
    onNext(FullNameChanged(""))
    onNext(PhoneNumberChanged(""))
    onNext(DateOfBirthChanged(""))
    onNext(AgeChanged(""))
    onNext(GenderChanged(None))
    onNext(ColonyOrVillageChanged(""))
    onNext(DistrictChanged(""))
    onNext(StateChanged(""))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(DateOfBirthChanged("33/33/3333"))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(AgeChanged(" "))
    onNext(DateOfBirthChanged(""))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(DateOfBirthChanged("16/07/2018"))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(PhoneNumberChanged("1234"))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(PhoneNumberChanged("1234567890987654"))
    onNext(SaveClicked)
    }
    verify(ui, atLeastOnce()).showEmptyFullNameError(true)
    verify(ui, atLeastOnce()).showEmptyDateOfBirthAndAgeError(true)
    verify(ui, atLeastOnce()).showInvalidDateOfBirthError(true)
    verify(ui, atLeastOnce()).showMissingGenderError(true)
    verify(ui, atLeastOnce()).showEmptyColonyOrVillageError(true)
    verify(ui, atLeastOnce()).showEmptyDistrictError(true)
    verify(ui, atLeastOnce()).showEmptyStateError(true)
    verify(ui, atLeastOnce()).showLengthTooShortPhoneNumberError(true)
    verify(ui, atLeastOnce()).showLengthTooLongPhoneNumberError(true)
    }

    View Slide

  73. @Test !// TODO: Migrate to Mobius
    fun `when save is clicked then user input should be validated`() {
    whenever(patientRepository.ongoingEntry()).thenReturn(Single.just(OngoingNewPatientEntry()))
    with(uiEvents) {
    onNext(FullNameChanged(""))
    onNext(PhoneNumberChanged(""))
    onNext(DateOfBirthChanged(""))
    onNext(AgeChanged(""))
    onNext(GenderChanged(None))
    onNext(ColonyOrVillageChanged(""))
    onNext(DistrictChanged(""))
    onNext(StateChanged(""))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(DateOfBirthChanged("33/33/3333"))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(AgeChanged(" "))
    onNext(DateOfBirthChanged(""))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(DateOfBirthChanged("16/07/2018"))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(PhoneNumberChanged("1234"))
    onNext(SaveClicked)
    }
    with(uiEvents) {
    onNext(PhoneNumberChanged("1234567890987654"))
    onNext(SaveClicked)
    }
    }

    View Slide

  74. View Slide

  75. View Slide

  76. View Slide

  77. View Slide

  78. View Slide

  79. Move functionality to the new architecture and the test would fail
    indicating a successful port.
    Next, remove the functionality from the new architecture, now the
    current test & all other tests should pass.

    View Slide

  80. View Slide

  81. View Slide

  82. View Slide

  83. View Slide

  84. Rinse and repeat… Delete the test when you’re done!

    View Slide

  85. View Slide

  86. Pros
    • Pretty low risk, micro changes to the system

    • System is green and ready to ship all the time

    • Easy for the reviewer to understand system evolution

    • Fairly easy to accommodate incoming feature requests

    • Untested code attracts attention

    • No duplication

    View Slide

  87. Cons
    • Requires familiarity around testing and refactoring

    • Requires fast feedback cycles

    • Requires experimentation

    • Has the highest pay-off when similar patterns are replicated

    • May inherit some design decisions from the older architecture

    View Slide

  88. References
    https://github.com/simpledotorg/simple-android

    Approach 1: a5c8ad1 .. ff27cb1

    Approach 2: 0c19381 .. df7788b

    View Slide

  89. Questions?

    View Slide