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 full-size 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 full-size slide

  3. Where do we start?

    View full-size slide

  4. BOUNDARIES!
    (Large surface area)

    View full-size slide

  5. System Boundaries
    • Network

    • File System / Database / Persistence

    • Hardware

    • UI

    • 3rd Party Integrations

    View full-size slide

  6. Network
    Database

    View full-size slide

  7. Network
    Database

    View full-size slide

  8. Network
    Database
    User Interface

    View full-size slide

  9. +
    -
    Network
    Database
    User Interface

    View full-size slide

  10. +
    -
    Network
    Database
    User Interface
    Inputs

    View full-size slide

  11. +
    -
    0.5v
    Network

    View full-size slide

  12. +
    -
    1.0v Database

    View full-size slide

  13. +
    -
    1.5v
    User Interface

    View full-size slide

  14. +
    -
    2.0v
    Network
    User Interface

    View full-size slide

  15. +
    -
    Re-architect

    View full-size slide

  16. +
    -
    +
    -
    Re-architect

    View full-size slide

  17. Approach 1: Cloning Tests

    View full-size slide

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

    View full-size slide

  19. 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 full-size slide

  20. 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 full-size slide

  21. 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 full-size slide

  22. 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 full-size slide

  23. 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 full-size slide

  24. 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 full-size slide

  25. 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 full-size slide

  26. 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 full-size slide

  27. 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 full-size slide

  28. 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 full-size slide

  29. 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 full-size slide

  30. 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 full-size slide

  31. 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 full-size slide

  32. Approach 2: Strangulation

    View full-size slide

  33. +
    -
    +
    -
    Re-architect

    View full-size slide

  34. Network
    Database
    User Interface

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  40. Peek: Migration

    View full-size slide

  41. @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 full-size slide

  42. @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 full-size slide

  43. @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 full-size slide

  44. @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 full-size slide

  45. 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 full-size slide

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

    View full-size slide

  47. 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 full-size slide

  48. 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 full-size slide

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

    Approach 1: a5c8ad1 .. ff27cb1

    Approach 2: 0c19381 .. df7788b

    View full-size slide