What to do when an IT project goes wrong

19 stycznia 2023
Jakub Rojek Jakub Rojek
Photo by Inzmam Khan from Pexels (https://www.pexels.com/pl-pl/zdjecie/mezczyzna-w-czarnej-koszuli-i-szarych-dzinsowych-spodniach-siedzi-na-szarej-wyscielanej-lawce-1134204/)
Kategorie: Branża, Zarządzanie, Programowanie, Poradniki

Życie jest tak skonstruowane, że ma wzloty i upadki, szczyty i doliny, plusy dodatnie i plusy ujemne. I spokojnie - nie zamierzam rozpisywać się o filozofii czy udzielić Wam 10 rad, które odmienią Wasze życie na lepsze [zobacz jak]. Dążę do tego, że podobnie jak to się dzieje w innych dziedzinach życia, tak tworzenie oprogramowania również nie zawsze przebiega w sposóby, jaki wszyscy byśmy chcieli. Nie mam tutaj na myśli tego, że czasem programista spędzi nad zadaniem ciut więcej czasu, gdyż nie może zlokalizować buga lub przycisk zmienił swój kolor w niepoprawny sposób. Raczej chciałbym nawiązać do sytuacji, w których projekt przez dłuższy czas naprawdę sprawia problemy, zarówno klientowi, jak i wykonawcy.

Nie ukrywajmy tego - nie wszystkie przedsięwzięcia związane z oprogramowaniem (i nie tylko) przebiegają bez problemów i niekiedy trzeba naprostować kurs. Czasem jest to stosunkowo łatwe i wymaga tylko kilku korekt lub udzielenia kilku rad członkom zespołu, ale niekiedy potrzebne są systemowe rozwiązania lub długotrwałe działania naprawcze. Dzisiaj porozmawiamy o paru pomysłach, które można wdrażać w zespołach IT w takich sytuacjach. Niektóre z nich są dosyć proste i oczywiste, inne dużo trudniejsze. Rady przedstawione w dzisiejszym tekście w większości wynikają z naszej obserwacji lub są ogólnie znane, choć nie zawsze szeroko komentowane. To nie jest też tak, że istnieje jakiś złoty środek, który od razu poprawia sytuację - nie zawsze problem da się tak łatwo obejść. Tym niemniej, nie warto siedzieć z założonymi rękami.

Jak poznać, że jest problem w projekcie IT?

Powody zakłóceń realizacji systemów IT są naprawdę różne i tak samo odmienne są sygnały, po których można poznać, że coś jest nie tak. Takim najbardziej wyrazistym papierkiem lakmusowym jest obserwowanie przebiegu sprintów, a dokładniej zwrócenie uwagi na to, czy:

  • zespół zawsze mieści się z wszystkimi zadaniami w terminie,
  • prace przebiegają spokojnie, tzn. zadania nie są kończone w pośpiechu lub po godzinach,
  • nie występują zadania, z którymi nikt nie może sobie poradzić,
  • nie wszystkie prace spadają na barki jednej lub dwóch osób w zespole (rozkład jest jest w miarę równomierny),
  • członkowie zespołu zawsze wiedzą co robić.

Jeśli któryś z powyższych punktów nie zostaje zachowany (lub występują inne, niewymienione tutaj kłopoty), to warto reagować. Oczywiście, nie należy poprzestać na obserwacji pierwszego czy kilku początkowych sprintów lub etapów - problemy z realizacją mają do siebie to, że lubią się nawarstwiać i prawdopodobnie nawet, jeśli rozpoczęcie przebiega bez zakłóceń, to pewne zaburzenia może wypłynąć w późniejszych etapach. Dlatego tak ważna jest rola osób zarządzających projektem, dostępność product ownera oraz - a może i przede wszystkim - komunikacja programistów z kierownictwem (czemu pomaga idea mniejszych zespołów w metodykach zwinnych).

Obserwacja dalszych etapów jest ważna także z tego powodu, że klient ma już dostęp do wyników wcześniejszej pracy, a co za tym idzie - może zgłaszać uwagi. Jeśli jest ich dużo, a zleceniodawca wymaga reakcji w odpowiednim trybie, to może się zdarzyć, że zaburzy to przebieg aktualnego sprintu. Należy na to zwrócić uwagę lub założyć taką sytuację wcześniej i przyjąć konkretną strategię. Pod tym kątem każdy projekt jest inny, gdyż istnieje w innym kontekście biznesowym.

Wreszcie, może być tak, że mimo iż zadania są kończone w terminie, a programiści są w dobrym humorze i kondycji psychicznej, to klient lub testerzy zgłaszają bardzo dużo problemów z funkcjonalnością oraz jakością dostarczonych prac. Jeśli tak jest i rzeczywiście narzekania nie są wyssane z palca, to jasny sygnał, że trzeba wprowadzić poprawki i to nie tylko w oprogramowaniu.

Niezależnie od wszystkiego, najważniejsze to poprawnie zidentyfikować źródło problemu. Powiedzieć, że po prostu występuje kłopot to za mało, gdyż nie można wówczas łatwo znaleźć dla niego rozwiązania - przypomina to strzelanie w ciemności i liczenie na to, że w coś się trafi. A właśnie metody radzenia sobie z kryzysowymi sytuacjami omówimy sobie teraz w kontekście różnych przypadków.

Upewnienie się, że wszyscy znają wymagania

Wbrew pozorom, znajomość szczegółów projektu przez członków zespołu nie zawsze jest oczywista. Dobrze wiemy, że przy ustalaniu wymagań, na spotkaniach z klientami oraz przy planowaniu prac uczestniczy garstka osób, gdyż nie zawsze jest czas i miejsce na angażowanie wszystkich (czy to dobrze, czy źle to już inna kwestia). Efektem takich prac koncepcyjnych są:

Te dokumenty powstają po to, aby z wynikiem analiz mogli zapoznać się wszyscy członkowie zespołu oraz programiści, którzy mają się zajmować zadaniami. Należy upewnić się, że wszystkie osoby mają dostęp do tych dokumentów oraz wiedzą, gdzie czego szukać. Nierzadko bowiem bywa tak, że specyfikacja jest gdzieś zamieszczona, ale niekoniecznie każdy wie, gdzie jej znaleźć. Bywa też tak, że ktoś ma dostęp do notatek, ale nawet nie wie o tym lub nie umie wyszukać w nich potrzebnych informacji. Dlatego warto wyrobić sobie nawyk, aby wszystkie ważniejsze linki i informacje umieszczać np. w firmowym systemie wiki czy w innej bazie wiedzy, aby była do nich prosta droga. To samo, niestety, dotyczy wątków mailowych, w których omawiane są szczegóły różnych funkcji rozwojowych - czasem nie ma siły i trzeba je przekopiować do wspólnej bazy wiedzy lub załączać wszystkich do kopii. W pewien sposób jest w stanie uporządkować to Feedybacky, jeśli wszystkie zainteresowane strony będą przestrzegać pewnych reguł.

Druga część związana jest z sytuacją, w której programista nie do końca wie, za jakie zadanie może się zabrać lub co się z nim wiąże. W pierwszym przypadku oznacza to brak dostarczonego planu sprintu (a także terminu jego realizacji - to też bardzo istotna kwestia, aby każdy członek zespołu znał harmonogram). W drugim - brak jasno postawionych kryteriów lub opisu wymagania. W tym także pomaga odpowiednia baza wiedzy lub stworzenie pełnego opisu zadania w systemie do zarządzania nimi. Bywa jednak też tak, że programista zna kontekst, ale nie wie (lub zapomina) o wszystkich szczegółach danego wymagania - wówczas przydatna jest koncepcja Definition of Done, dzięki której czynimy pracę testera łatwiejszą, a zadanie szybciej wykonywane (mniej poprawek wróci do programisty, więc całość powinna potrwać krócej).

Powyższe porady pomagają w sytuacji, kiedy stwierdzimy, że mamy zasoby czasowe i ludzkie, ale programiści nudzą się lub nie wiedzą, co robić. Krótko mówiąc, jest problem z przepływem informacji.

Zwiększanie zasobów

Teraz przejdziemy do bardzo standardowego planu naprawczego, jaki jest stosowany podczas najczęściej występującej problemowej sytuacji w projekcie - zespół się nie wyrabia i to nie ich wina. Zadań jest tak dużo, a deadline tak bliski, że zwyczajnie jest niemożliwe lub bardzo trudne, aby zmieścić się z tym wszystkim w terminie. Większość osób wówczas przyjmuje ich zdaniem jedyną słuszną drogę - podejście "więcej mocy", czyli przydzielenie większej liczby programistów do projektu, przynajmniej na pewien czas. Czasami to zdaje egzamin, ale nie zawsze. Warunki, aby to zadziałało, są następujące:

  • programiści mają czas na zapoznanie się z projektem lub wcześniejszą wiedzę o nim,
  • projekt jest w technologii podobnej do tej, w jakiej na co dzień pracują "nowi" programiści,
  • zadań jest dużo i są małe lub mogą być podzielone na równolegle realizowane podzadania.

Bez tych warunków (szczególnie ostatniego) wprowadzanie nowych programistów do projektu może tylko pogłębić chaos - te osoby nie będą wiedziały, jak pomóc, ich wsparcie może być dużym ciężarem dla "starych" programistów, a do tego mogą wręcz nie mieć co robić, jeśli zadania są "monolitami". W takich sytuacjach, niestety, należy zwiększać zasoby czasowe, a nie ludzkie. Można wręcz przeznaczyć jeden sprint na dokończenie istniejących zadań i nie rozpoczynać w tym czasie nowych - czasem utrata wpływów z jednego etapu jest lepsza niż ciągłe nadganianie pracy, co może zniszczyć ludzi psychicznie i dodatkowo pogłębić problemy biznesowe.

Dopiero w przypadku, kiedy podobna sytuacja ma miejsce bardzo często, wręcz regularnie, należy rozważyć dodanie kolejnych osób, ale w normalnym trybie, a nie pilnym, natychmiastowym. Warto jednak też pamiętać, że przydzielenie programisty do jakiegoś projektu oznacza odłączenie go od innego - należy uważać, aby w takiej sytuacji nie spowodować dodatkowych problemów w innym miejscu portfolio firmy. Jeśli istnieje ryzyko, że tak się stanie, pozostaje zatrudnienie dodatkowych osób, outsource'owanie prac lub ograniczenie zakresu.

Warto też pamiętać o tym, że nie trzeba od razu dokładać osób - być może warto na początek je zamienić i osobę, która nie do końca radzi sobie z danym projektem przesunąć do mniej "gorącego" przedsięwzięcia, a za niego wstawić weterana, który potencjalnie szybciej upora się z problemami. Przy czym należy zwrócić uwagę na to, aby zapewnić, iż ten weteran faktycznie poradzi sobie z nowym zadaniem, a cała operacja nie będzie wyglądać na "zesłanie" i okazanie braku zaufania wobec pracownika.

Zmniejszenie oczekiwań

Czasem pracy jest za dużo niezależnie od tego, ile osób zostanie przydzielonych do projektu. Pamiętajmy, że projekt IT musi być rentowny dla wszystkich zainteresowanych stron. Jeśli wykonawca, mimo starań, nie jest w stanie realizować zlecenia (z różnych powodów) tak, aby mieć z tego korzyści, to może zwyczajnie powinien powiedzieć o tym wprost zleceniodawcy i porozmawiać o wyjściu z tego impasu raz na zawsze. Natomiast nie zawsze należy uciekać się do tak drastycznych rozwiązań - czasem wystarczy po prostu obniżyć oczekiwania.

Pod tym pojęciem rozumiem zmniejszenie zakresu prac, wynegocjowanie dłuższych dat wykonania lub uproszczenie założeń. Jeśli zadania przydzielane do jednego sprintu mają oszacowanie sięgające 300 godzin i jest regularny problem, aby to "dowozić", to być może należy zmniejszyć ten zakres do 200 godzin. Oczywiście, nie może być to bezmyślne - być może duże sprinty są umotywowane biznesowo (nie chodzi tylko o finanse) czy harmonogramem (np. projekt jest tworzony w ramach wniosku z odgórnie ustalonym deadlinem). Najczęściej ten zabieg jest stosowany czasowo do momentu ustabilizowania sytuacji i sytuacje, w któych się sprawdza, to wspomniane wcześniej skupienie się na nadgonieniu prac (a nie tworzeniu nowych funkcji), miesiące wzmożonych urlopów u pracowników lub trudniejsza sytuacja w innym projekcie.

Czasem też nie trzeba zmniejszać zakresu prac - być może problemem jest nieodpowiednie oszacowanie lub źle ułożone sprinty. W pierwszym przypadku warto korzystać z tego, że w metodykach zwinnych zadania przychodzą faktycznie zwinnie i nawet nie sposób ich wycenić wcześniej, zanim nie odkryje się specyfiki danego przedsięwzięcia (choć zaznaczam, że jest to temat znacznie trudniejszy i często zleceniodawcy chcą znać kwoty z góry). Drugi wątek z kolei zaraz omówimy bardziej szczegółowo.

Należy jeszcze wspomnieć o innym pomyśle - czy zawsze, planując sprint, trzeba deklarować, że wszystkie zadania zostaną zrealizowane? Cóż, zgodnie z regułami powinno się tak robić. Ale można zostawić sobie też furtkę bezpieczeństwa poprzez wewnętrzne priorytetyzowanie zadań, przydzielając im etykietę obowiązkowych i opcjonalnych. W ten sposób wiemy, że powinniśmy zrobić wszystkie, ale w razie trudności mamy też z góry ustalone punkty, które zespół może odpuścić i najczęściej przenieść na następny sprint. Oczywiście, w takim przypadku klient płaci mniej, natomiast ważne jest to, że reguły nadal są jasne, a wykonawca posiada pewne zabezpieczenie niegodzące w reputację.

Lepszy podział zadań

Z poprzednich dwóch sekcji wypływa wniosek, że czasem rozwiązaniem jest lepsze ułożenie zadań. Mamy tutaj do czynienia z dwoma aspektami.

Pierwszym z nich jest fakt, że duże, monolityczne zadania trudniej się przydziela. Z założenia zajmują one więcej czasu, a nierzadko nie da się ich zrównoleglić, aby pracowały nad nim naraz dwie lub więcej osób. To oznacza, że albo trzeba je sztucznie dzielić pomiędzy sprintami, albo jedną osobę całkowicie wyłączyć z innych prac. W ten sposób nie można też pomóc temu programiście w przypadku problemów w inny sposób niż poprzez zastąpienie go lub wynegocjowanie dalszego terminu. Dlatego zawsze warto przyjać zasadę (o ile jest to możliwe), aby zadań było dużo, ale za to były stosunkowo małe i dobrze podzielone. Pomaga w tym ustalenie etapowości wymagań - przykładowo, czy tworząc moduł powiadomień, musisz od razu implementować docelowe rozwiązanie, łącznie z tworzeniem systemu kolejkowania, oznaczaniem wiadomości jako przeczytanych i pełną obsługą kategorii? Może wystarczy na początek zacząć od samego generowania i wyświetlania powiadomień w prostej formie? Oczywiście, trzeba od początku myśleć o wprowadzeniu rozwiąząń, które i tak potem się przydadzą. Tym niemniej, mniejsze zadania zdecydowanie łatwiej się "skalują" oraz pozwalają zrównoleglić prace. Jedynym zagrożeniem może być poczucie, że jest ich tak dużo, że nie damy rady, ale po przejściu przez jeden sprint i ustaleniu swojej "normy" ta obawa zniknie.

Drugi aspekt to podział zadań na sprinty. Co z tego, jeśli uda się odseparować od siebie poszczególnie mniejsze funkcje, skoro i tak do zrobienia jednej wymagana jest realizacja poprzedniej? Przyjmijmy, że mamy w backlogu następujące zadania i ich oszacowania:

  1. Tworzenie produktu: 24 pkt.
  2. Przypomnienie hasła: 10 pkt.
  3. Usunięcie produktu: 8 pkt.
  4. Wysłanie maila do administratora: 4 pkt.
  5. Zarządzanie użytkownikami: 30 pkt.
  6. Wyświetlanie i obsługa kategorii: 10 pkt.

Załóżmy, że każdy sprint pomieści ok. 40 punktów, a klientowi bardzo zależy na jak najszybszym uruchomieniu mechanizmu produktów. Pierwszym pomysłem może być zatem umieszczenie w tym etapie następujących wymagań:

  1. Tworzenie produktu: 24 pkt.
  2. Usunięcie produktu: 8 pkt.
  3. Przypomnienie hasła: 10 pkt.

Razem wychodzi wynik 42 pkt. i powiedzmy, że zespół deklaruje, że da radę. Czy jednak to dobry podział prac? Zauważmy, że aby usuwać produkty, należy je wcześniej móc dodać i też w takiej kolejności powinny być realizowane te wymagania. To oznacza, że o ile za przypomnienie hasła może odpowiadać inna osoba i pracować nad tym zadaniem równolegle, tak start wykonywania usunięcia produktu może nastąpić dopiero po realizacji tworzenia, niezależnie od tego, czy za te pozycje weźmie się ta sama osoba. Co za tym idzie - minimalny czas przejścia całej ściezki to 24 + 8 = 32 punkty. Co więcej, klient oczekujący jak najszybszego uruchomienia mechanizmu produktów może mieć tak naprawdę na myśli tylko ich wprowadzanie, aby zapełnić bazę swoją ofertą. Usuwanie jest sytuacją wyjątkową i prawdopodobnie nic się nie stanie, jeśli użytkownicy nie będą jej mieli do dyspozycji od początku. Może zatem przyjmiemy taki plan.

  1. Tworzenie produktu: 24 pkt.
  2. Przypomnienie hasła: 10 pkt.
  3. Wyświetlanie i obsługa kategorii: 10 pkt.

Plan sprintu to 44 pkt. i niektórzy pomyślą, że wpadliśmy z deszczu pod rynnę - przecież granicą miało być 40 pkt. Ale zauważcie, że każde z tych zadań może być robione niezależnie. Oczywiście, nadal realizacja może być też sekwencyjna, ale dysponujemy większą elastycznością, a minimalny czas przejścia ścieżki wynosi 24 punkty. W wielu przypadkach jest to lepszy i bardziej przyszłościowy zakres sprintu, dopuszczający większe rotowanie zadaniami w zespole.

Oczywiście, sam przykład jest prosty i nie zawsze odpowiednie jest układanie etapów w ten sposób - zależy to od klienta, projektu i momentu w czasie. Czasem sekwencyjności nie da się uniknąć, a niekiedy nie opłaca się tego robić. Dodatkowo, inaczej będziemy podchodzili do zadań w przypadku, kiedy oprogramowanie jest używane wyłącznie testowo i wewnętrznie, a inaczej, kiedy działa już serwer produkcyjny i logują się prawdziwi użytkownicy, mający realne potrzeby biznesowe. Tym niemniej, jest to coś, co warto przemyśleć.

Zwinne podejście do wycen

Wspominałem już o tym w poprzednich sekcjach, natomiast warto pamiętać, że nie zawsze trzeba wyceniać wszystkie zadania od razu. Oczywiście, aby ustalać sprinty, warto mieć wszystkie oszacowania z góry, ale spójrzmy na to inaczej. Zakres sprintów (czy etapów) i tak jest przygotowywany dynamicznie na podstawie realnego rozwoju projektu. Bardzo często oznacza to, że zachodzi potrzeba dołożenia nowych zadań do product backlogu "w locie", a co za tym idzie - ich wyceny. Co więcej, ta wycena uwzględni już nabyte doświadczenie wynikające ze specyfiki projektu i będzie bliższe realnemu czasowi. Może zatem warto wstrzymywać się z wycenianiem wszystkiego na początku i ograniczyć się do części zadań (tych, które mają największą szansę pojawić się w MVP).

Nie da się ukryć, że jest to kuszące, ale pozostaje jeden poważny problem - klient zwykle musi od początku wiedzieć, jaki budżet powinien planować na cały projekt. Co za tym idzie - należy wycenić wszystkie "rdzenne" zadania, aby móc to obliczyć. Trzeba przyznać, że na to nie ma dobrej odpowiedzi - z jednej strony zwinność jest dobra i pozwala szybciej reagować na zmiany oraz robić to, co rzeczywiście jest ważne z korzyścią i opłacalnością dla obu stron, ale z drugiej strony zleceniodawca musi wiedzieć, czy za zaimplementowanie wszystkich planowanych funkcji, wdrożenie itd. zapłaci docelowo X zł, czy 10X zł. Nikt nie chce zaczynać projektu i po paru miesiącach dowiedzieć się, że przyjdzie mu za to zapłacić znacznie więcej niż oczekiwał.

Tutaj powróćmy do tematu podziału zadań i tego, że im bardziej wymaganie zostanie rozbite na podwymagania, tym wycena staje się bardziej realna z punktu widzenia wykonawcy. To kolejny argument za tym, aby to robić. Z drugiej strony, nie wszystko jest takie proste do oszacowania oraz takie dzielenie zajmuje zwykle trochę czasu. Tym niemniej, warto pamiętać o tej technice i stosować ją tam, gdzie estymowana wycena zadania wydaje się szczególnie duża lub niepewna. Oraz o tym, aby w przypadku zagadnień nowo powstałych w trakcie trwania projektu brać pod uwagę już doświadczenie wynikające z realizacji danego przedsięwzięcia.

Zmniejszanie stosu technologicznego

Wspominałem już o zmniejszaniu zakresu, ale nie dotyczy to tylko samych zadań. Dotyka to również tematu stosu technologicznego, a w szczególności tzw. technologicznych pistoletów, którzy chcieliby robić od razu wszystko idealnie, za pomocą docelowej technologii, najlepiej nowej dla siebie, aby poznawać coraz więcej rozwiązań. Cóż, pomysł jest piękny, ale zazwyczaj nierealny.

Nie każdy projekt wymaga nowoczesnych rozwiązań. Co więcej, nawet bardzo duże i rozbudowane oprogramowanie nie zawsze potrzebuje dedykowanych bibliotek do obsługi danego modułu. Wracając do przytoczonego wcześniej przykładu z modułem powiadomień - możemy domyślać się, że w zależności od spodziewanej liczby powiadomień czeka nas tam kolejkowanie. Ale czy to oznacza, że należy od razu wprowadzać rozwiązania typu RabbitMQ czy Apache Kafka? Czy nie warto najpierw spróbować wykorzystać istniejącą bazę danych i zrobić to bardziej standardowo? Oczywiście, nie zawsze jest to wskazane i może się zdarzyć, że jednak uproszczone podejście okazało się błędne, kiedy np. powiadomień w tygodniu generowanych jest nie 1 000, tylko 100 000. Ale zazwyczaj w bardzo restrykcyjnych czasowo projektach opłaca się najpierw sprawdzić prostszą drogę i uprzedzić klienta, że w przypadku problemów warto będzie zrobić dodatkowo to i to. Sytuacja może być inaczej rozpatrywana, kiedy zespół doskonale zna rozwiązania typu RabbitMQ i ich wykorzystanie jest dla nich szybkie oraz naturalne.

W przypadku problemów z realizacją zadań w projekcie, zarządzający powinni zwrócić uwagę, czy kłopoty nie wynikają z tego, że programiści próbują wprowadzić rozwiązanie idealne, które zajmuje więcej czasu. Być może warto ochłonąć i przyjąć prostszą wersję. Nawet jeśli spotka się to z protestami ambitnych koderów, ale za to jest uzasadnione biznesowo, także w dłuższej perspektywie.

Problemy z implementacją

Czasem problemy ze zbyt długim czasem realizacji wynikają z tego, że dany programista "zakopie się" w danym zadaniu i nie umie się w nim odnaleźć. Zwykle dotyczy to większych wymagań lub takich, w których trzeba uwzględnić bardzo specyficzne warunki oraz czynniki jakościowe. Co ciekawe, nie zawsze ten problem spotyka osoby o mniejszym doświadczeniu - także weterani czasem potrzebują pomocy.

Rozwiązania są dwa. Jedno, bardziej tymczasowe i czasami, niestety, tylko odraczające problem, to przydzielenie innego zadania ratunkowego na wyczyszczenie umysłu oraz ochłonięcie. Bywa, że pomaga to spojrzeć na główne zagadnienie z dystansu i pozwala podświadomości przemyśleć niektóre sprawy. Drugie to konsultacje z innymi członkami zespołu lub z kimś z zewnątrz. Zdarzały sie sytuacje (i to nie tak rzadko), że niżej podpisany był zapraszany na spotkania dotyczące zupełnie nieznanych dla niego systemów IT jako zewnętrzny konsultant, który miał wnieść świeże spojrzenie na daną sprawę, nieskażone całą potępieńczą atmosferą, która obejmowała członków zespołu pracującego nad danym oprogramowaniem. A czasem sytuacja jest znacznie prostsza - jeśli nikt z programistów danego projektu nie zajmował się nigdy określoną funkcjonalnością, to może warto porozmawiać z kimś, kto już się z tym mierzył i może udzielić paru wskazówek.

Do tej sekcji wchodzą również rozwiązania znane z programowania ekstremalnego i kodowanie w parach, umożliwiające szybszy przepływ komunikatów i doświadczenia. Nie trzeba być samodzielnym bohaterem w swoim zespole - czasem dopuścić do siebie pomoc innej osoby.

Testy zajmujące zbyt dużo czasu

Są takie projekty, które opierają się na jednym długim procesie lub kilku ekranach, mających wiele rozgałęzień i możliwości ukrycia się błędu. Jako dobrzy programiści wiemy, że każdy przypadek powinien zostać odpowiednio zweryfikowany i "przeklikany", aby upewnić się, że funkcjonalność działa jak należy oraz oprogramowanie nie zyskało nowych błędów. Jednak wiemy też, że nie zawsze sprawdzenie wszystkiego jest możliwe ze względów czasowych lub braku wglądu w myśli użytkownika. Co więcej, testowanie jednej funkcji wielokrotnie, praktycznie po każdej zmianie, potrafi być nużące i demotywujące.

Ponownie możemy wyróżnić dwa rozwiązania. Jednym z nich są dedykowani testerzy. Drugą - testy automatyczne, które pozwolą sprawdzić określoną funkcjonalność w tle, za programistę i od razu pokazać scenariusze, w których pojawiają się błędy. Niestety, obie drogi niosą ze sobą koszty i nawet w przypadku automatyzacji weryfikacji trzeba liczyć się z tym, że samo przygotowywanie warunków testowych potrafi być bardzo czasochłonne, a co więcej - bywa, że jest wrażliwe na zmiany w oprogramowaniu i wówczas należy je pisać od nowa. Dlatego warto przygotowywanie takich testów automatycznych również zawrzeć w wycenie oraz wiedzieć, kiedy je stosować. Nie ma bowiem większego sensu przygotowywać "robotów" weryfikujących zadania, które są proste i napisane raz nie zmieniają się do końca projektu. Choć w tym przypadku niektórzy wspomną o testach regresyjnych, ale to temat na inny tekst.

Bardzo głośnym dzwonkiem alarmowym jest sytuacja, w której po wdrożeniu użytkownicy zgłaszają nagle znacznie więcej krytycznych problemów lub co gorsza - problemy odnotowane wcześniej są ponawiane, gdyż ponownie wystąpiły. Warto wówczas przyjrzeć się temu, w jaki sposób programiści rozwiązują te zgłoszenia i czy faktycznie przykładają się nie tylko do samej realizacji, ale też choćby szczątkowego przetestowania danej funkcji po zmianie. Zdarzało się widzieć nam sytuacje, w których poprawka była wdrażana bez choćby sprawdzenia, czy działa. To karygodne i jeśli zdarza się wielokrotnie, to oznacza, że należy z takim programistą poważnie porozmawiać o jego podejściu do obowiązków. Może też być tak, że dany koder testuje swoje rozwiązania po zmianach, ale albo niedokładnie (jeśli tak jest, to warto wyjaśnić, z czego to wynika), albo nie znając całego kontekstu ("bo ja nie wiedziałem, że klient użyje tego w ten sposób"). W tym ostatnim pomóc mogą wspomniane już wcześniej Definition of Done oraz przeglądy kodu.

Czas obsługi zgłoszeń serwisowych w trakcie realizacji prac

Wcześniej pisałem już o sytuacji, w której realizacja kolejnych sprintów jest zaburzana przez zgłoszenia serwisowe z poprzednich etapów. Jest to naturalne i wynika z tego, że klienci faktycznie powinni testować powstające oprogramowanie oraz z tego tytułu mają pewne uwagi. Czasem są to faktyczne błędy, czasem sugestie, a czasem pytania o nowe funkcje.

Problem w tym, że jeśli zespół pracuje nad aktualnym sprintem, to siłą rzeczy nie może na bieżąco odpowiadać ani nawet przeglądać wszystkich zgłoszeń serwisowych - wówczas ogranicza się tylko do tych o wysokim priorytecie lub wyjątkowo prostych. Z jednej strony to zrozumiałe, a z drugiej - jeśli zespół IT udostępnia pewien fragment oprogramowania za określoną sumę pieniędzy, to klient ma prawo zgłaszać uwagi. Trzeba to wypośrodkować, oczywiście w zależności od tego, ile tych uwag jest.

Każdy sprint może składać się nie tylko z faktycznych zadań programistycznych, ale też tzw. stałych zadań. Mogą to być godziny poświęcone na spotkania, dodatkowe analizy, wdrożenie czy właśnie reagowanie na zgłoszenia. Oczywiście, w przypadku błędów rozwiązywanych w ramach gwarancji ten czas wykonawca bierze na siebie, natomiast bardzo często wszelkie zapytania i sugestie, nawet jeśli są małe, to zajmują "moment" i wybijają z rytmu. Dlatego trzeba je również uwzględnić w harmonogramie.

Z kolei obsługa takich zgłoszeń (która jest znacznie prostsza dzięki Feedybacky'emu) po realizacji głównych prac to już sprawa na umowę serwisową (także obsługiwaną za pośrednictwem FyBy), dzięki której klient ma pewność, że ktoś dysponuje dla niego co najmniej pewną pulą godzin, a wykonawca - zagwarantowanie stałego miesięcznego dochodu. Można też wprowadzić częściową umowę serwisową w trakcie głównych prac, co stanowi motywację do przydzielenia większego zespołu do danego przedsięwzięcia i utworzenie tzw. sekcji wsparcia.

Dług technologiczny

Nie da się ukryć - czasem w projektach dochodzi się do ściany, która wynika z tego, że oprogramowanie ma już kilka lat i programiści je rozpoczynający mieli mniejsze doświadczenie oraz mniejszą wiedzę o wymaganiach niż obecnie. W ten sposób zrobili fundamenty, które teraz się chwieją. Dotyka to każdego systemu, choć nie każdego w równym stopniu. Tym niemniej, narastające zmiany i coraz bardziej "finezyjne" oraz czasem nietrafione rozwiązania (wynikające m.in. ze zbyt ścisłych terminów czasowych) potrafią zniszczyć program i coraz bardziej utrudniać jego rozwój. Nazywamy to długiem technologicznym.

Częściowo można sobie z tym radzić poprzez refaktoryzację i w ten sposób przerabiać pewne moduły, aby stały się bardziej elastyczne. Warto jednak przypomnieć, że refaktoryzacja od razu całego oprogramowania nie ma sensu - zazwyczaj takie zorganizowane modyfikacje dotyczą konkretnych części systemu i dlatego warto, aby były przeprowadzane regularnie, fragmentami, aby stopniowo zmniejszać dług technologiczny, utrzymując go na rozsądnym poziomie. Także tam, gdzie widać od razu możliwość wprowadzenia bardziej długoterminowego rozwiązania, które nie zajmie dużo więcej czasu, warto od razu to zrobić. Jeśli jednak jest ono zdecydowanie bardziej kosztowne, należy porozmawiać z klientem i przedstawić możliwe rozwiązania oraz ich konsekwencje.

Długo odkładana refaktoryzacja lub silna presja czasowa powoduje, że prędzej czy później nadejdzie czas, w którym oprogramowanie powinno zostać przebudowane. Dotyczy to też np. aktualizacji bibliotek do nowszych wersji. To jest bardzo trudny i czasochłonny proces, który tak naprawdę wymaga osobnej wyceny. Co więcej - potrzebuje wcześniejszej analizy i ustalenia planu refaktoryzacji, gdyż należy pamiętać, że bardzo zaburza on sam proces realizacji wymagań lub poprawek. W tym miejscu często wprowadzany jest temat zewnętrznych konsultacji, wspominany już wcześniej.

Nie da się ukryć, że w przypadku niektórych systemów bardzo kusi podejście polegające na napisaniu go od nowa. To też jest możliwe i czasami nawet niekosztujące znacznie więcej niż "wielka refaktoryzacja". Natomiast również może być przeprowadzane z głową i częściowo, np. korzystając przez jakiś czas jednocześnie z dwóch wersji systemu. Na koniec warto też zaznaczyć, że podział frontendu i backendu na mniejsze części znakomicie ułatwia wszelkie modyfikacje i usprawnienia.

Dodatkowe uwagi i podsumowanie

Na koniec chciałbym zaznaczyć jeszcze kilka rzeczy, ale wśród nich jedną szczególnie - nie należy przesadzać z liczbą projektów przypadającą na daną osobę. Nie da się ukryć, że w mniejszych zespołach każdy członek dzieli swój czas pomiędzy kilka systemów, ale zbyt duże rozdrobnienie może wywoływać częstsze zmiany kontekstu, a co za tym idzie - dłuższy czas regeneracji, pogubienie się i zniechęcenie. To już kwestia zarządzania ludźmi, nie tylko pod kątem technicznym, ale też psychologicznym.

Bardzo pomaga również przygotowywanie wszelkich list "do zrobienia" (TODO). Wiadomo, że sam plan danego sprintu często jest obsługiwany w różnych systemach do zarządzania zadaniami, natomiast bardziej mam na myśli "checklisty" rzeczy do zrobienia w danym dniu. To zaskakujące, ale tak proste metody pozwalają podnieść efektywność i uporządkować plan dnia, a jednocześnie odciążyć pamięć.

Podsumowując, zarządzanie firmą IT i realizowanymi projektami jest trudne. Głównie dlatego, że - jak to bywa w życiu - w każdym przedsięwzięciu prędzej czy później pojawią się problemy, z którymi trzeba uporać się jak najwcześniej, aby nie urosły do niespotykanych rozmiarów. Na szczęście, istnieją pewne sposoby, aby sobie poradzić nawet w takiej wymagającej sytuacji, choć wszystko zależy od kontekstu całego projektu, chęci działania ze wszystkich stron i otwartości na zmiany.

Mam nadzieję, że porady przedstawione w tym artykule pomogą komuś w potrzebie lub przynajmniej nakierują myśli na właściwsze rozwiązania. Życzę Wam jak najmniejszej liczby trudności podczas Waszych przygód w IT.

Pozdrawiam i dziękuję - Jakub Rojek.

Lubimy pisać, nawet bardzo, ale na co dzień tworzymy aplikacje webowe i mobilne. Sprawdź niektóre z wykonanych przez programów.

O autorze

Jakub Rojek

Główny programista i współwłaściciel Wilda Software, z wieloletnim doświadczeniem w tworzeniu i rozwoju oprogramowania, ale także w pisaniu tekstów na różnorakich blogach. Zaprawiony w boju analityk i architekt systemów IT. Jednocześnie absolwent Politechniki Poznańskiej i okazjonalny prowadzący zajęcia na tej uczelni. W wolnych chwilach oddaje się graniu w gry wideo (głównie w karcianki), czytaniu książek, oglądaniu futbolu amerykańskiego i e-sportu, odkrywaniu cięższej muzyki oraz wytykaniu innym błędów językowych.

Jakub Rojek