Utrzymywalne oprogramowanie – część 1

Jest to pierwszy z serii wpisów o utrzymywalnym oprogramowaniu. Ta część jest poświęcona wyjaśnieniu, co decyduje o stopniu trudności utrzymania software’u oraz umotywowaniu znaczenia tej cechy.

Definicja

Wypada zacząć od określenia, czym jest samo utrzymywanie oprogramowania (ang. software maintenance). W prostych słowach są to wszystkie działania podejmowane w celu zapewnienia ciągłości działania usług. Pożądanymi rezultatami są między innymi poprawienie wydajności, eliminowanie błędów (na przykład zgłoszonych przez użytkowników), a także przystosowanie do uruchomienia w nowym środowisku.

Obrazek ilustrujący ten post przedstawia rzeźbę Atlasa – mitycznego tytana, który utrzymuje na swoich barkach całe niebo. Ten potworny ciężar miał być karą za udział w buncie przeciwko Zeusowi – a przynajmniej tak to sobie wyobrażali starożytni Grecy.

Utrzymywanie oprogramowania też jest ciężarem, tyle że dźwiganym przez ludzi z krwi i kości. Zaczyna się, gdy wersja 1.0 ląduje na produkcji. Trwa przez cały czas, gdy oprogramowanie jest używane i zarabia pieniądze. Kończy się dopiero po całkowitym zamknięciu projektu. Jest więc procesem będącym integralną częścią życia softu. Z tego powodu trochę zaskakującym jest fakt, że trudno znaleźć na ten temat dobre opracowanie. Utrzymanie jest najchętniej traktowane jako zło konieczne, zauważane dopiero gdy produkt przestaje działać albo jego koszt stale rośnie, pożerając kolejne zasoby. Jest to tym bardziej niezrozumiałe, że utrzymanie stanowi świetne źródło informacji zwrotnej o projekcie w warunkach polowych. Chyba nic nie powie więcej o niezawodności i naturze oprogramowania niż obserwacje sposobów, w jaki się ono załamuje – jak i kiedy przestaje działać.

Utrzymywalność jest zatem miarą kosztu utrzymania oprogramowania. Im mniej telefonów po godzinach, ręcznych interwencji jest wymaganych do utrzymania systemu na chodzie, im krótszy czas od wykrycia błędu do wdrożenia poprawki, tym bardziej utrzymywalne jest oprogramowanie.

Znaczenie utrzymywalności

Wytworzenie oprogramowania wiąże się z poniesieniem znacznych kosztów. W 2017 roku oprogramowanie wciąż kosztuje bardzo dużo. Przez to rozumie się głównie czas developmentu, który waha się od kilku miesięcy do lat – wszystko zależnie od projektu. Jeżeli mamy do czynienia z oprogramowaniem na zamówienie, to właściwe utrzymanie zaczyna się często dopiero po oddaniu zakontraktowanego produktu. Czas życia na produkcji vs czas życia w fazie developmentu potrafi mieć się jak 10:1. Utrzymaniem raczej nie będzie się tym zajmować ten sam podmiot, który wytworzył oprogramowanie. Warto byłoby zatem pomyśleć o optymalizacji kosztów oprogramowania, gdy już zejdzie z taśmy produkcyjnej.

Nie zawsze jest to właściwy opis sytuacji. Wszyscy jesteśmy teraz agile, rozumiemy zmienność wymagań, iteracyjne i inkrementalne podejście do wytwarzania i w ogóle 😛 . Innymi słowy utrzymanie może zaczynać się już w trakcie rozwijania produktu. Jeszcze inaczej ma się sprawa z topowymi firmami, które nie produkują softu na zamówienie, ale stale rozwijają własne produkty. Ciągły development zmienia kaliber problemów, z jakimi przychodzi się mierzyć na froncie utrzymania. Wdrożenie nowych wersji to nowe feature’y, ale i zawsze niepewność związana z możliwością wprowadzenia nowych błędów. W takich przypadkach szczególnego znaczenia nabierają praktyki rodzaju Continous Integration. Wracając do tematu – utrzymanie i development interferują ze sobą. Problemy produkcyjne spowalniają rozwój, przez co mogą być traktowane z coraz większą niechęcią jako smutna konieczność, która nie pomaga wytworzyć pożądanej wartości. Tymczasem utrzymanie stanowi świetne źródło informacji o tym, jakie problemy trapią produkt. Jego przetrwanie jest często tożsame z przetrwaniem firmy, która go rozwija i utrzymuje jednocześnie. W takim przypadku dokładanie do własnego produktu trudnych w utrzymaniu elementów (które wcale nie muszą być kosztowe na etapie developmentu!) może w długiej perspektywie przysporzyć więcej strat niż zysków, a jak wiadomo czas utrzymania softu jest zwykle wielokrotnie dłuższy niż wyprodukowania.

Przygotowanie utrzymywalnego produktu daleko wykracza poza standardowe kryteria akceptacyjne, to jest przejście weryfikacji QA. Bardzo trafnie ujął to Michael T. Nygard w Release it:

If you fail to design your system for a production environment, your life after release will be filled with “excitement.”

Tymczasem zaprojektowanie systemu i jego późniejsza ewolucja w kierunku optymalnego działania na produkcji nie może się odbyć bez zbierania feedbacku. Nie da się przecież niczego nauczyć bez kontroli postępów, inaczej wciąż popełnia się te same błędy myśląc, że wszystko jest w porządku. 🙂

Cechy utrzymywalnego oprogramowania

Transparentność

Zdecydowanie najważniejsza cecha. Oznacza możliwość zdobycia informacji o obecnym stanie i kondycji systemu oraz historycznych zmianach tych dwóch. Innymi słowy, można łatwo uzyskać informację o tym ‚co siedzi’ w systemie.

Na tę cechę składają się po pierwsze mechanizmy monitorowania. Poddajemy ciągłej obserwacji środowiska, w których działa aplikacja poprzez parametry niskopoziomowe, takie jak utylizacja CPU czy dysku. Obserwujemy też i powiadamiamy o padach samej aplikacji. Wyciek pamięci potrafi położyć proces, o czym można dowiedzieć się wiele długich minut później bez odpowiednich mechanizmów zabezpieczających i powiadamiających. Jeszcze gorzej, gdy dowiadujemy się o tym od poirytowanych użytkowników. Sprawdzamy także, jak wygląda użycie systemu. Jeżeli jest to jakaś forma sklepu internetowego, a ilość zawartych transakcji kupna – sprzedaży nagle spadła do 0, to coś może być na rzeczy. Niekoniecznie zabójcza promocja u konkurencji. Bardziej wyrafinowany przykład – ilość dodań produktu do koszyka jest wysoka, ale większość z zaczętych tak zakupów kończy się na etapie płatności i nie przechodzi dalej. Widzę dwie możliwości – inwazję Niezdecydowanych lub problem z operatorem płatności.

Kolejną rzeczą jest powiadamianie o błędach w aplikacji – w najprostszej formie wielki try … catch w głównej pętli programu przechwytujący niezłapane wyżej wyjątki i wysyłający o nich powiadomienia dowolnym kanałem komunikacji, chociażby mailowo.

Dobry monitoring i zestaw powiadomień o problemach w aplikacji jest krytycznym aspektem transparentności. Pozwala reagować na problemy zanim dowiemy się od nich od użytkowników. O ile w ogóle się dowiemy, zanim zirytowany klient nie opuści strony/odinstaluje programu i skorzysta z produktu konkurencji. Z tego powodu bardzo podoba mi się motto Sentry:

Stop hoping your users will report errors

Kolejnym sposobem manifestacji transparentności jest obecność dobrych logów. Na ich podstawie powinno się dać odtworzyć stan aplikacji, który doprowadził do wystąpienia błędu. Absolutnym minimum jest zapisywanie informacji o żądaniach zmiany stanu systemu.  Swoista forma access_loga znanego z serwerów HTTP też jest wskazana.

Logowanie numerów kart płatniczych i haseł klientów można sobie z czystym sumieniem odpuścić.

Resilience

Niestety nie ma dobrego dosłownego tłumaczenia tego słowa na język polski, które brzmiałoby ładnie w kontekście oprogramowania, więc posłużę się omówieniem:

zdolność do samoczynnego powrotu do poprawnego stanu po szkodliwym incydencie, bez żadnej ingerencji z zewnątrz

Posłużę się trywialnym przykładem, niech będzie to serwer HTTP nginx z PHP uruchomionym przez FastCGI, chociaż z tego obrazu Dockera. Jeżeli jeden z procesów PHP obsługujący żądania zginie (np przekroczy dozwoloną ilość pamięci), to zostanie zastąpiony nowym. Stało się bez jakiejkolwiek interwencji człowieka. W produkcyjnych warunkach taka sytuacja mogłaby wystąpić w trakcie obsługi żądania użytkownika. Wtedy prawdopodobnie zakończyłoby się to 500tką, ale po tym system wróciłby do normalnego stanu i dalej obsługiwał żądania jak gdyby nic się nie stało.

W dobie mikroserwisów i systemów złożonych z wielu usług resilience jest bardziej złożonym zagadnieniem. Przygotowanie do jego osiągnięcia obejmuje między innymi obsługę automatycznego wznawiania połączeń po ich zerwaniu, ponawianie prób żądań, wprowadzanie redundantnych usług, ograniczenie zasięgu lokalnych awarii pojedynczej usługi i zabezpieczenia przed kaskadową propagacją skutków blokady jednego z elementu systemu.

Jak można testować resilience swojego systemu? Spojrzeć na rysunek architektury, zatrzymywać wzrok przy kolejnych elementach i zadać sobie pytanie – czy system sam odzyska stabilność (i ile to zajmie), gdy stanie się jedno z następujących:

  • aplikacja X przestanie działać
  • połączenie sieciowe między serwerami A i B zostanie zerwane

Netflix wypuścił narzędzie zwane Chaos Monkey, które ma testować tę cechę systemów.

Łatwość wdrażania nowych wersji

Ostatnie, ale nie najmniej ważne. Usterka w oprogramowaniu została wykryta i zdiagnozowana, przygotowano odpowiedni patch. To, co się dzieje dalej zależy od sposobu, w jaki release’owane jest oprogramowanie.

Aplikację w chmurze, nad którą ma się kontrolę dużo łatwiej zaktualizować niż zainstalowaną na komputerach użytkowników. Z drugiej strony Microsoft i upierdliwe aktualizacje Windowsa włączające się zawsze wtedy gdy nie potrzeba są przykładem, że można. 😛 Na wzmiankę zasługuje też sposób, w jaki Niantic wymusza update aplikacji klienckiej na smartfonach w swoim Pokemon Go – nie pograsz, dopóki nie ściągniesz najnowszej aktualizacji. Ma to sens – nie muszą wspierać dziesiątek wersji jednocześnie.

W przypadku aplikacji internetowych w chmurze pozostaje też aspekt czysto techniczny związany z samym umieszczeniem kodu. Jeżeli jest to język skryptowy, a aplikacja bezstanowa i można ją restartować bez większych konsekwencji, to patcha można zaaplikować bezpośrednio. W przypadku języków kompilowanych stworzenie nowego wydania to dużo więcej pracy.

W imponujący sposób ten problem został rozwiązany w Erlangu, będącym przecież językiem kompilowanym. Poprzez całkowitą separację danych aplikacji od kodu umożliwia on podmianę implementacji i migracje stanu pomiędzy wersjami bez przerywania obsługi żądań. O tym, jak to jest zrobione napiszę osobnego posta.

 

photo credit: mattk1979 Atlas Sculpture, Rockefeller Center, New York via photopin (license)

3 odpowiedzi do “Utrzymywalne oprogramowanie – część 1”

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *