Wiecie czym są fitness functions? A czy jeździliście kiedyś samochodem?
No dobra, każdy kiedyś jeździł. Jako kierowca lub pasażer, ale jeździł. Ja jeździłem niegdyś popularnym w PRLu modelem. Potężna maszyna z rodzimej stajni. Potrafi też zmusić do fitnesu. Szczególnie, gdy odmówi posłuszeństwa w korku.
Ale my nie o takim fitnessie chcieliśmy tutaj.
Jak już ustaliliśmy: każdy kiedyś jeździł. Wielu też brało udział w epickich wyprawach w nieznane. Przed każdą dłuższą trasą standardowy healthcheck:
Mapa - jest.
Paliwo - jest.
Dokumenty i kluczyki - są.
Poziom oleju i ciśnienia w oponach - sprawdzone.
Czas w drogę. Ahoj przygodo!
Jadąc “testujesz” prędkość wskazywaną na prędkościomierzu w stosunku do panujących na drodze ograniczeń. Co jakiś czas rzucasz okiem na poziom paliwa. Częściej jednak Twój wzrok zatrzymuje się na mapie. Oczywiście, robisz to wszystko w nielicznych momentach oderwania oczu od jezdni.
Po godzinie niczym niezmąconej podróży stwierdzasz, że wypicie dwóch kaw tuż przed trasą to nie był jednak dobry pomysł. Twój pęcherz zaczął wyrzucać exception. Czas na postój.
Mapa podpowiada, że stacja paliw jest za 40km.
Organizm podpowiada, że to nie czas na żarty.
Decydujesz się na zjechanie z trasy, by dotrzeć do postoju znajdującego się bliżej.
Sukces.
Powrót na trasę nie wchodzi w grę. Stłuczka na wjeździe na autostradę. Trzeba znaleźć inną trasę. GPS już nad tym pracuje, więc dostosowujesz się do nowych wytycznych. Zmieniasz plan, dzwonisz, że będzie poślizg. Kontrolujesz sytuację, reagujesz na zmiany i ostatecznie docierasz na miejsce tylko z kilkunastominutowym opóźnieniem.
I o tę kontrolę właśnie dzisiaj nam chodzi.
Architektura oprogramowania ma tendencję do starzenia się. Rozpoczynamy projekt z jasnymi założeniami. Pieczołowicie manewrujemy pomiędzy jej atrybutami, jak kierowca tira na rondzie w pobliskiej wsi. Znajdujemy kompromis w gąszczu wymagań technicznych i funkcjonalnych.
Co się stanie, gdy wyjedziemy z domu i zapomnimy o tym wszystkim, co przygotowaliśmy? Spodziewałbym się problemów. Nie inaczej jest z naszą architekturą. Tenedencja do rozpadu jest czymś naturalnym, a naszym zadaniem jest jej zapobiegać. W jaki sposób? Korzystając z przemyślanej kontroli.
Tym właśnie są fitness functions. Zbiorem metryk i testów, które dostarczają informacji o stanie naszej architektury. Dobrze zaprojektowane funkcje pozwalają zobaczyć stan poszczególnych założeń - jak sprawdzanie trasy czy stanu paliwa. Te “testy” są przez nas wywoływane, podczas gdy ciągłym testem jest nieprzerwane patrzenie na jezdnię.
Dodatkową wartością są też testy łączące kilka atrybutów, kwintesencja funkcji dopasowania, sprawdzając jak radzą sobie na przestrzeni czasu. To one dają najwięcej wartości.
Niektóre założenia naszej architektury są jak nasz Exception na trasie, czy stłuczka na wjeździe na autostradę. Wyłania się znikąd. Musimy umieć go zidentyfikować i umiejscowić w kontrakcie architektonicznego kompromisu.
Kod się starzeje. Bity gniją, tak jak zużywa się paliwo czy opony. Nieutrzymywane oprogramowanie się rozpada, bo technologia posuwa się do przodu, nie pytając nas o zgodę.
Projektanci architektury powinni być zaopatrzeni w pokaźny zestaw testów, metryk i logów, by w każdym momencie móc sprawdzić jej stan i założenia.
Jednym z ciekawszych rozwiązań, gdy mamy dobrze skrojone CI, jest uzupełnienie naszych testów integracyjnych o automatyczne testy architektury kodu. Sprawdzenie, czy nasze modularne monolity nie zapożyczają za dużo, czy panujemy nad ich izolacją.
W TypeScript można znaleźć rozwiązania typu ts-arch lub wspomagać się analizą opartą na linterach, jednak zerkając w inną stronę, elegancko ogarnia to Spock, którym możemy kontrolować izolację paczek np. w Kotlinie:
def "setup"() {importedClasses = new ClassFileImporter().withImportOption(new ImportOption.DoNotIncludeTests()).importPackages("my-package")}def "module can be accessed only through events"() {given:def myRule = classes().that().resideInAnyPackage("..my-package.cotrollers..","..my-package.repositories..",).should().onlyBeAccessed().byClassesThat().resideInAnyPackage("..my-package.cotrollers..","..my-package.repositories..","..my-package.events..",)expect:myRule.check(importedClasses)}
Niezależnie od tego, czy nasza architektura jest zastana, czy to greenfield na którym dopiero rozkładamy swoje zabawki, powinniśmy pochylić się nad przygotowaniem jak największej ilości metryk i testów. Czy tego chcemy, czy nie, projekt będzie ewoluować, bo taka jest jego natura, natomiast naszym zadaniem jest pokierowanie nim w odpowiednim kierunku, wprowadzając tzw. guided changes.
Musimy robić wszystko, by nie obudzić się w korku z pustym bakiem, bo wtedy czeka nas już tylko ten drugi fitness.
Zdjęcie: Laura Gariglio