Gdy kończyłem studia, byłem przekonany, że efektywność mojej pracy będzie zależała wyłącznie od tego, jak szybko wpadnę na rozwiązanie problemu i jak szybko zaimplementuję rozwiązanie. Założenie wyglądało na słuszne, z racji tego, że większość projektów studenckich odbywało się zgodnie z programistyczną metodą 3 Z: zaimplementuj, zalicz, zapomnij.

Projekty komercyjne nie dość, że przeważnie trwają dłużej niż semestr, to dodatkowo angażują więcej osób – w takim wypadku jakość i zrozumienie kodu przez zespół są dużo ważniejsze.
W Kainosie miałem okazję przećwiczyć kilka praktyk, które pozwoliły na stworzenie czytelniejszego, bardziej zrozumiałego i lepiej przetestowanego kodu. Poniższa lista to zbiór metod pracy które zrobiły na mnie największe wrażenie, jeśli chodzi o efektywność.

1. TDD

Oczywiście na studiach słyszałem o hipsterskich programistach, którzy najpierw piszą testy, a dopiero później kod właściwy. Następnie przeczytałem “TDD. Sztuka tworzenia dobrego kodu” Kenta Becka i sam zacząłem eksperymentować. Autor podchodzi do tematu bardzo rygorystycznie – sugeruje on, aby każdą najmniejszą zmianę w kodzie zaczynać od napisania nowego nieprzechodzącego unit testu (sam jednak później przyznaje, że przy pewnej wprawie można pominąć kilka etapów).
Zdecydowanie polecam popracować kilka dni w schemacie red-green-refactor, aby docenić jego zalety. W skrócie wygląda to tak:

a) red – napisz jeden nieprzechodzący test który sprawdza czy warunek wymagania biznesowego został spełniony. Na początek niech będzie to coś trywialnego. Na przykład dla wymagania “niech funkcja zwraca najmniejszy element z tablicy” możemy przetestować przypadek tablicy jednoelementowej, wtedy poprawną odpowiedzią będzie pierwszy element z tablicy. Brzmi jak przekombinowanie? Być może, ale ten trywialny przypadek pozwoli nam zbudować odpowiednią strukturę w kodzie bez zagłębiania się w logikę. Bardziej skomplikowane przypadki testowe wprowadzimy w kolejnym cyklu.

b) green – popraw kod tak, aby test przechodził. Standardowym problemem jest myślenie za bardzo na przód i pisanie kodu pod testy które jeszcze nie istnieją, ale są już w głowie programisty. Przestrzega przed tym Kent Beck a i ja polecam dla eksperymentu pisać kod krok po kroku. Dość łatwo wpaść w pułapkę pisania kodu zbyt wielkimi partiami – grozi to bugami, których wykrycie i poprawa trwa dużo dłużej niż pisanie zgodnie z duchem TDD ;)

c) refactor – w momencie gdy mamy przechodzące testy, które pokrywają kod z logiką biznesową, możemy bez obawy modyfikować źródło. Etap refaktoryzacji jest najlepszym momentem na zastanowienie się, czy w pośpiechu dobrze nazwaliśmy zmienne, czy nie warto wydzielić fragmentu logiki do innej funkcji/klasy, albo czy nie ma konieczności całkowitego przepisania któregoś fragmentu. Gotowe testy pozwalają nam na swobodne zadbanie o jakość kodu.

Powtarzamy powyższy schemat aż do zaimplementowania całej funkcjonalności. Ostatecznie uzyskujemy czysty, dobrze pokryty testami kod.

Czemu warto najpierw napisać test a potem kod? Po pierwsze nie zawsze napisany kod nadaje się do przetestowania, więc TDD wymusza na nas specyficzny sposób kodowania, po drugie jeśli napiszemy kod, następnie test (przechodzący!) to skąd mamy pewność że test się zaczerwieni w przypadku zepsucia kodu? W teorii musielibyśmy ponownie zepsuć fragment kodu żeby przetestować sam test! TDD omija nam ten problem.

2. Pair Programming

Koszmar managerów – dwie osoby przy jednym komputerze: jedna pisze, druga się przypatruje :) Statystyki mówią, że wydajność pary programistów pracujących razem jest ok 15% mniejsza niż gdyby pracowali oddzielnie. Jest to liczba i tak sporo mniejsza niż intuicyjne 50%, jednak mimo to mało osób się decyduje na taką formę programowania. Zysk jest jednak znaczący, przede wszystkim w niemiarodajnych wartościach, takich jak zrozumienie technologii i projektu, lepsza jakość kodu no i budowanie relacji w zespole.

Wariantów PP jest całkiem sporo, czasem jedna osoba dyktuje kod drugiej, czasem jeden programista pisze sam, drugi odpoczywa. Tracimy jednak wtedy najważniejszy element PP czyli dyskusję i interakcję. Celem PP jest omówienie fragmentu funkcjonalności tak, aby implementacja była oczywista dla obu osób. Wtedy nie ma znaczenia kto napisze kod.
Moim ulubionym wariantem PP jest połączenie go z TDD – jedna osoba pisze test, druga implementuje rozwiązanie, następnie obie osoby zastanawiają się nad refaktoryzacją. W kolejnym cyklu role się odwracają.

W przypadku programistów na podobnym poziomie, pracujących razem od jakiegoś czasu, wszystko odbywa się niemal intuicyjne, współpraca przebiega szybko i gładko. W przypadku programistów o zróżnicowanym poziomie, np. z osobą świeżą w projekcie korzyść jest jeszcze większa. Mamy pewność, że kod został przejrzany przez osobę doświadczoną, a przy okazji osoba świeża uzyska łatwe wprowadzenie do projektu czy zespołu.

Patrząc szerzej na obraz pracy w parach dostrzeżemy jeszcze więcej zalet. Pracując z testerem będziemy mogli wyeliminować wiele błędów już na etapie implementacji rozwiązania, dodatkowo będziemy mogli nauczyć się pisać testy automatyczne. Pracując z analitykiem biznesowym być może uda nam się uniknąć masy dokumentacji a i będziemy mieli okazję dopytać o szczególne przypadki.

Niewątpliwym problemem PP jest fakt, że jest bardzo męczące. Prawidłowa praca w parach odbywa się przy pełnym skupieniu obu osób, więc warto pamiętać aby robić przerwy i nie pracować razem dłużej niż 4-5h dziennie.

Oczywiście – wielu programistów woli pracować z maszyną niż z człowiekiem i nie każdy członek zespołu będzie chciał pracować z każdym. Warto jednak próbować, może się okazać że taka współpraca zakopie niejeden topór wojenny.

Dla bardziej odważnych polecam pair programming wg. Atlassiana: youtube ;)

IMG_5152

3. Code Review

Sam nie miałem okazji uczestniczyć w “waterfallowym” podejściu do code review – spotkaniu całego zespołu deweloperów w sali konferencyjnej, gdzie wyświetlane są poszczególne fragmenty kodu projektu i prowadzona jest dyskusja o funkcjach i klasach. Niektórzy chwalą sobie takie podejście, jednak mi najbardziej pasuje forma analizowania kodu na bieżąco, po każdym zrealizowanym user storie. Narzędzia typu gitlab czy crucible pozwalają na stworzenie merge-requesta w którym wylistowane są wszystkie zmiany w projekcie. Wystarczy podesłać linka do zespołu, tak aby każdy członek mógł skomentować dowolną linijkę w kodzie. Ważna jest zasada jednego komentarza + jednej odpowiedzi do pojedynczej linijki, jeśli wątpliwości dalej występują należy wyjaśnić je ustnie tak, aby nie powstawały wielodniowe wymiany tekstu. Code review powinno trwać krótko, najlepiej do jednego dnia – wtedy zachodzi mniejsze ryzyko, że w developie powstały zmiany zmiany wpływające na nasz kod, dodatkowo ograniczamy context switching.

Czasami zespoły decydują się na dwuetapowe CR. Najpierw kod sprawdza kolega z zespołu, potem po wprowadzeniu zmian CR wędruje do tech leada/architekta. W przypadku bardziej newralgicznych funkcjonalności na pewno warto przeprowadzić taki proces, ale należy liczyć się z ryzykiem większych opóźnień.

Można zastanowić się nad pytaniem czy warto robić CR w przypadku gdy funkcjonalność jest pisana razem, przez dwóch deweloperów w ramach Pair Programming. W końcu każdy fragment kodu był już przejrzany przez dwie osoby. Prawda jest taka, że wszystko zależy od projektu, funkcjonalności i dojrzałości zespołu. Na pewno warto przetestować każde rozwiązanie i omówić je na kolejnej retrospekcji :)