Wszyscy się z tym stykamy i to niezależnie od branży - mało która firma może pozwolić sobie na zrealizowanie produktu, przekazanie go klientowi, a następnie zupełne zapomnienie o wytworzonej pracy. Statystyki podają, że niedoceniany często etap utrzymywania oprogramowania zajmuje co najmniej 60 procent czasu całego projektu, a nierzadko ten wskaźnik dochodzi do 80 procent. To szalenie dużo i wydaje się nierealne, ale niestety - wielu z nas potwierdzi te proporcje z własnego doświadczenia. Czasami dość trafnie ocenimy pracochłonność całego systemu IT, jednak mylnie założymy, że po drodze poprawek będzie wręcz pomijalnie mało. Na nasze nieszczęście, sytuacja wygląda zgoła odmiennie - przekroczenie budżetu zwykle odbywa się "dzięki" tym korektom.
Porozmawiajmy dzisiaj o tym, co składa się na etap utrzymania (ang. maintenance) oraz jak mieć w nim trochę łatwiej. Z autopsji możemy wyróżnić kilka rodzajów prac, które mają miejsce podczas tej fazy. Potwierdza to też podlinkowany wyżej artykuł.
1. Zmiany
To temat, który był jednym z powodów powstania ruchu i manifestu agile'owego. Rzadko szczegóły wymagań pozostają niezmienne od początku do końca projektu. Bywa również tak, że zmieniają się kwestie bardzo istotne dla biznesowego aspektu całego przedsięwzięcia i jest to całkowicie naturalne. Każdy pomysł na aplikację ewoluuje - klient, przychodząc do software house'u z ideą na biznes IT, ma w głowie jego kształt, model biznesowy i ogólnie wizję oraz nadzieję związaną ze swoim projektem. Jednak wiadomo, że to, co sprawdza się w teorii, niekoniecznie sprawdzi się w praktyce - pewne funkcje, mimo że wydawały się potrzebne użytkownikom najbardziej na świecie, są nieużywane, a sami zainteresowani domagają się zupełnie czegoś innego, co wcześniej nawet nie było w planach. Pomysł zderza się z rzeczywistością i realnymi wymaganiami biznesowymi. Krótko mówiąc, podczas prac nad oprogramowaniem lub po nich często zachodzi potrzeba wprowadzania poprawek lub nowych możliwości.
Problem, który tutaj dotyka zespoły IT i klienta jest taki, że jeśli te modyfikacje mają być wprowadzane jeszcze w trakcie realizowania pierwotnych założeń, to zwykle zachodzi potrzeba ponownej wyceny i oszacowania pracochłonności. Czasami jednak zmiany są na tyle niepozorne, że w imię dobrych relacji ze zleceniodawcą wprowadzamy je od razu, bez dodatkowych kosztów. Jednak tutaj zapominamy o tym, że o ile mogą wydawać się drobne, o tyle:
- mogą okazać się nie takimi drobnymi i w toku prac wyjdzie, że trzeba się natrudzić, aby je wprowadzić,
- może być ich dużo.
Idąc dalej, czas poświęcony na takie zmiany może być całkiem długi i wydatnie odbić się na budżecie projektu, co zawsze stanowi przyczynek do pewnych nieporozumień, jeśli sytuacja nie zostanie dobrze naświetlona. Na szczęście, istnieje szereg metod, które może zastosować zespół IT, aby poradzić sobie ze zmianami w oprogramowaniu, a jednocześnie nie być stratnym.
- przywołane już wcześniej metodyki zwinne, a dokładniej sprinty. Pisałem o nich już wcześniej na blogu, więc w celu poznania koncepcji zapraszam do lektury tamtego (krótkiego) tekstu. Idea podzielenia pracy na krótkie odcinki czasu, które są niezmienne i tworzone zgodnie z najbliższymi potrzebami klienta, w naturalny sposób pozwala poradzić sobie ze zmianą planów związanymi z większymi modyfikacjami, bowiem takich planów nie ma - w klasycznym Scrumie nie układa się przyszłych sprintów z wyprzedzeniem, a z kolei rozliczenia następują za każdy wykonany. To oznacza, że w przypadku chęci wprowadzania zmian i poprawek, klient nie musi długo czekać do końca sprintu, a zespół IT ma "miejsce" na takie działanie.
- wycenianie z buforem lub wręcz wyszczególnienie buforu na poprawki - technika teoretycznie dobra, choć może być mało skuteczna, gdyż z jednej strony generuje pytania klienta przy analizie wyceny, a z drugiej - i tak nigdy nie doszacujemy odpowiednio potrzebnego czasu. Tym niemniej, warto o tym pamiętać.
- stosowanie prototypów - dotyczy to poprawek, które nie zmieniają diametralnie oprogramowania, ale np. dodają pola lub modyfikują zachowanie w określonych miejscach. W takim wypadku warto wcześniej poświęcić chwilę i "zamakietować" odpowiednie ekrany, próbując je w myślach "przeklikać" z klientem. W ten sposób już wcześniej pojawią się rzeczy, o których nikt wcześniej nie myślał, a będą potrzebne. O prototypowaniu więcej pisałem ostatnio.
- umowa serwisowa - rozsądna droga działania w typowym etapie utrzymania, po zakończeniu zasadniczej części projektu. Wówczas software house świadczy klientowi usługi na określoną liczbę godzin w miesiącu i tym samym jest to przestrzeń na wprowadzanie zmian. O tym temacie, traktujący o nim bardziej szczegółówo, zapewne powstanie osobny artykuł na blogu.
2. Poprawki błędów
Błędy w tworzonym oprogramowaniu się zdarzają. Jest to prawda znana od lat i często obśmiewana przez wiele branż, także samych programistów. Tutaj przy okazji muszę przyznać, że poza pewnymi przypadkami (związanymi szczególnie z fintechem), zespoły IT i tak mają błogo w porównaniu np. do budowlańców - korekty zwykle mogą być zastosowane dość szybko, "w locie" i rzadko powodują zagrożenie dla zdrowia, majątku lub życia ludzkiego (choć i takie sytuacje, niestety, historia informatyki zna).
Wiadomo, że błędy zawinione przez programistów lub np. administratorów muszą zostać poprawione w ramach gwarancji (która zazwyczaj trwa 12 miesięcy od momentu wdrożenia oprogramowania lub zmiany, z której wynika błąd) i nie jest to dodatkowo płatne. Nie zmienia to jednak faktu, że takie korekty zajmują czas i niekiedy wymuszają pilne działania (szczególnie, jeśli oprogramowanie jest już publicznie dostępne), wywracając harmonogram do góry nogami. Od tego nie uciekniemy - rozwiązań musimy zatem szukać w prewencji lub działaniach redukujących wpływ takich błędów na koszty wytwarzania projektu. Warto zauważyć też, że nie mówimy tylko o błędach typowo funkcjonalnych, ale także takich, które wynikają ze złego zrozumienia wymagań klienta.
Co możemy tutaj zaproponować?
- poprawianie ich jak najwcześniej - badania dawno wykazały, że koszt wprowadzenia poprawki rośnie wraz z etapem, na którym został wykryty bądź skorygowany. Można to tłumaczyć m.in. tym, że błąd znajdujący się w oprogramowaniu może zostać powielony w innych jego częściach lub kodu jest po prostu tak dużo, że jakiekolwiek działania mogą nieść za sobą lawinę korekt. Oczywiście, wiąże się to również z utrzymywaniem odpowiedniej architektury itd., ale i tak warto wprowadzać zmiany stosunkowo wcześnie (co nie znaczy, że software house zawsze rzuci wszystko i od razu zacznie diagnozować problem). Tutaj ponownie kłania się prototypowanie, które pozwala zredukować kłopoty wynikające ze złego zrozumienia potrzeb klienta.
- testowanie - ten temat każdy intuicyjnie rozumie i wie, że wszystko wprowadzane do systemu powinno ulegać weryfikacji, możliwie powtarzalnej i z użyciem różnych scenariuszy. Tutaj mamy również pewną walkę (szczególnie w niewielkich zespołach) pomiędzy testami manualnymi i automatycznymi, aczkolwiek jest to trochę inny wątek. Tak czy inaczej, dobry proces weryfikowania kodu i funkcjonalności pozwala uniknąć "wypchnięcia" przynajmniej tych najbardziej zawstydzających błędów do klienta.
- częste wydania wersji (ang. releases) - tutaj znowu wracamy do metodyk zwinnych i sprintów, jednak tym razem skupmy się na tym, co się dzieje po zakończeniu takiego etapu. Klient dostaje pewien fragment produktu i może go zobaczyć, przeklikać, przetestować oraz ocenić. Wówczas wychodzą różnorakie potrzeby zmian, wykrycie błędów, które jednak przecisnęły się na serwer i tym samym - zgłoszenia (oczywiście, za pośrednictwem Feedybacky'ego). Jednak zdarza się, że klienci (zwykle z braku czasu) nie weryfikują tego, co opublikował zespół IT, a to bardzo duży błąd i przepis na zwiększenie kosztów - im wcześniej zauważone zostaną niedoskonałości, tym szybciej mogą zostać skorygowane i tym samym nie wpłyną aż tak na budżet. Dlatego warto jak najczęściej prosić klienta o "przeklikanie" systemu, a nawet usiąść wraz z nim i to zrobić.
3. Działania prewencyjne
Czyli wszelkie modyfikacje wynikające nie tyle ze zgłoszeń klientów, co naszych własnych przewidywań dotyczących tego, gdzie warto się ochronić, zanim stanie się coś niedobrego. Najczęściej ten rodzaj obejmuje dwa rodzaje działań:
- refaktoryzację, która poprawia architekturę oprogramowania (lub przynajmniej jego kod) i uprzyjemnia pracę z nim, tym samym redukując prawdopodobieństwo wprowadzenia błędnych zmian lub powielenia ewentualnych "złych zapachów" (ang. bad smells),
- aktualizację bibliotek - to dużo trudniejszy temat, gdyż nierzadko tego typu "apdejty" potrafią zepsuć oprogramowanie, szczególnie jeśli nowa wersja modułu nie jest kompatybilna wstecz (ang. backward compatibility).
Ten rodzaj poprawek jest szczególnie problematyczny, gdyż dotyczy czegoś, czym klient ma prawo się nie przejmować - jemu chodzi o to, aby aplikacja działała, a to, jak jest zbudowana w środku, leży w kompetencjach software house'u. Z drugiej strony zaniechanie jakichkolwiek działań w tym aspekcie może prowadzić do bardzo brzydkiego kodu i szybszego przejścia do fazy "legacy", ale co więcej - konieczności tłumaczenia po wielu latach klientowi, że niektórych rozwiązań nie jesteśmy w stanie zastosować, gdyż są obsługiwane tylko przez nowsze wersje biblioteki, a teraz ta zmiana będzie wymagała bardzo dużo czasu.
Tutaj rozwiązaniem jest wspomniana wcześniej umowa serwisowa, jednak należy coś zaproponować także na sam etap realizacji projektu. Metodyki zwinne tutaj wprowadzają etap retrospektywy, który pomaga zespołowi dyskutować o tym, co poszło w danym sprincie źle i co warto poprawić bądź utrzymać. To temat znacznie szerszy, jednak to, co nas interesuje, to właśnie propozycje programistów dotyczące umawiania się na tworzenie kodu w odpowiedni sposób. Ponownie - im wcześniej pewne zalecenia zostaną wdrożone w życie, tym dłużej architektura będzie "czysta". Nieocenione są tutaj także regularne przeglądy kodu i tym samym wymiana wiedzy.
Ja od siebie proponuję jednak wprowadzenie jednej zasady, która dotyczy ulepszania kodu i pozwala uniknąć rzeczywiście wysokiego kosztu takiej "pełnej" refaktoryzacji - zasada skautów, głoszone przez Roberta C. Martina, mówiąca o tym, aby zostawiać kod zawsze w lepszym stanie niż się go samemu zastało. Mówiąc inaczej - jeśli podczas rozwijania oprogramowania widzimy coś, co niekoniecznie jest dobrze napisane, a poprawa tego to najwyżej 15 minut pracy (oczywiście w idealnym przypadku) - w większości sytuacji warto to zrobić od razu. Dzięki temu małym kosztem stopniowo poprawiamy strukturę naszej aplikacji. Warto jednak też zaznaczyć, że tą metodą raczej nie naprawimy ogromnych błędów architektonicznych.
4. Zmiany adaptacyjne
Jest to odmiana wyżej opisanego rodzaju zmian w etapie utrzymania, ale tym razem wynikająca z obopólnej decyzji, a często wyboru klienta. Najczęściej dotyczy etapu wdrażania oprogramowania i dyskusji o tym, który serwer będzie najlepszy. Pisałem już na tym blogu wielokrotnie, że warto tego typu plany omawiać na bardzo wczesnym etapie projektu, gdyż mogą determinować one wybór technologii. Może się jednak okazać, że w międzyczasie zmieniła się sytuacja na rynku hostingów lub aplikacja radzi sobie tak dobrze, że wymagane jest np. przeniesienie serwisów do chmury. A to może za sobą nieść zmiany adaptacyjne.
Mam tutaj na myśli wymóg dostosowania oprogramowania do warunków panującej w danej infrastrukturze, innych niż te, na które się przygotowaliśmy. Najczęściej wymienimy tutaj zmiany w backendzie, wprowadzenie bezstanowości, modyfikację zapytań SQL (silniki bazodanowe trochę różnią się od siebie pod tym względem), ale może się też nagle okazać, że np. nasz misternie szykowany serwer websocketowy nie będzie mógł zostać wdrożony i należy inaczej poradzić sobie z powiadamianiem użytkowników na żywo. Ogólnie rzecz biorąc, często są to zmiany bardzo dotkliwe dla projektu.
Pewną receptą jest tutaj wspomniana wcześniej dyskusja o planowanych rozwiązaniach serwerowych, która pozwala nam odpowiednio przygotować architakturę. Zawsze lepiej założyć bardziej pesymistyczny scenariusz i później ewentualnie zmodyfikować aplikację (poprawiając jej wydajność itd.), gdyż to sytuacje "pozytywne", w których projekt odniósł sukces, klient jest zadowolony i z większym zrozumieniem poświęci pieniądze na rozwój architektoniczny. Natomiast prawie nigdy sytuacja nie będzie spokojna, gdy nagle okaże się, że np. na serwerze nie będziemy mieli do dyspozycji Postgresa, tylko dużo popularniejszego MySQL-a i część zapytań będzie wymagała przepisania.
Dlatego warto zakładać pesymistyczny scenariusz, aczkolwiek opłaca się również "rozciągać" architekturę, jeśli istnieje choćby cień szansy, że np. potrzebne będzie API, bezstanowość i inne modyfikacje mocno wpływające na sposób tworzenia kodu. To temat bardzo rozległy i wiążący się z analizą konkretnych przypadków.
Czy mamy jeden etap utrzymania?
Ściśle rzecz biorąc - tak i mówimy o okresie, który rozpoczyna się od momentu opublikowania systemu i udostępniania go użytkownikom do produkcyjnego stosowania. Jednak takich okresów, w których mogą zachodzić zmiany wymienione powyżej, mamy tak naprawdę kilka, po każdej publikacji wersji i wydania. Rzadko pamiętamy o tym, że wypuszczając choćby wynik pierwszego sprintu, klient może zgłosić uwagi, które musimy uwzględnić w sprincie drugim bądź krótko po nim. Dlatego lubię myśleć o tym, że etapów utrzymania mamy tak naprawdę więcej i możemy je traktować jako jedną strefę zaczynającą się znacznie wcześniej niż wydanie produkcyjne.
Podsumowanie
Etap utrzymania to trudny i zawsze niedoceniany temat. Potencjalnie może też być przyczyną większych problemów w każdym projekcie i dlatego warto wiedzieć, w jaki sposób do niego podchodzić. To, co przedstawiłem w powyższym tekście, to jedynie pomysły na "obsługę" tej fazy, które w wielu wypadkach wymagają głębszej analizy oraz planowania, a niekiedy zmianę swoich nawyków. Niezależnie od wszystkiego, warto zawczasu z klientem przedyskutować plan na wspólną weryfikację postępów i procedurę działania w sprawie poprawek - pozwoli to uniknąć nieporozumień oraz może stać się przyczółkiem do dłuższej i owocnej współpracy, z której obie strony będą zadowolone. I aby tak było w przypadku każdego projektu, tego Wam i sobie życzę.
Pozdrawiam i dziękuję - Jakub Rojek