Wspominaliśmy na blogu już o tym, że istnieją tematy, które są uznawane za klasykę inżynierii oprogramowania. Są to przykładowo diagramy UML (nie tylko klas, ale i stanów), zapobieganie brzydkim zapachom, refaktoryzacja i wiele innych obszarów. Należą do nich także techniki weryfikacji nie tylko funkcjonalności, ale też jakości samego kodu źródłowego, a jedną z takich metod są przeglądy kodu (ang. code reviews).
Co to są przeglądy kodu?
Zapewne intuicyjnie każdy z Was czuje, że takie przeglądy to przeczytanie i analiza kodu związanego z danym fragmentem oprogramowania i znalezienie miejsc, które:
- potencjalnie mogą powodować błędy, nawet jeśli te nie zostały wykryte podczas testów lub po prostu jeszcze nie wystąpiły,
- nie pozwalają lub utrudniają dalszy rozwój oprogramowania (np. dodanie nowych ścieżek przetwarzania będzie wymagać znacznej przebudowy),
- są niezgodne ze standardem kodu obowiązującym w zespole,
- kod generuje luki bezpieczeństwa,
- zastosowane rozwiązanie jest niewydajne i stosunkowo łatwo można to naprawić.
Oczywiście, to te krótkoterminowe powody, które mają zastosowanie w danej chwili i dotyczą konkretnego zadania (aczkolwiek nierzadko są propagowane na inne części systemu). Natomiast dużo ważniejszą motywacją do przeprowadzania przeglądów kodu są czynniki długoterminowe, które pozwalają choć w części zabezpieczyć dalszy rozwój oprogramowania.
Pierwszym z takich czynników jest wspomniane już propagowanie zmian. W przypadku, gdy system informatyczny składa się z wielu funkcji i zestawów widoków, to mimo iż każda taka część funkcjonalna rządzi się swoimi prawami, to w jakimś stopniu korzysta z innych. To oznacza, że częściowo używa lub nawet powiela fragmenty kodu z innych miejsc oraz dostosowuje je do swoich potrzeb - jest to często szybsze i rozsądniejsze niż pisanie wszystkiego od nowa. Ale z tego powodu bardzo ważne jest to, aby kod, który jest bazą dla innych funkcji, był jak najlepszej jakości. Dlaczego? Argument jest prosty - kopiowanie błędnego lub niedokładnego kodu oznacza, że później poprawki muszą być zrobione w dwóch lub nawet większej liczbie miejsc, a wiadomo, że to opóźnia prace i frustruje.
Drugim czynnikiem jest odpowiednie zarządzanie rozwojem juniorów, początkujących, praktykantów czy innych osób, których staż programistyczny (komercyjny) nie jest jeszcze zbyt długi. Co za tym idzie - nie mają wyrobionych pewnych nawyków i dopiero ten proces trwa. Dlatego w przypadku takich osób przeglądy kodu są szczególnie istotne, gdyż pozwalają utrwalić dobre praktyki oraz zwrócić uwagę na te złe. Jak to się mówi, "czym skorupka za młodu nasiąknie, tym na starość trąci" - jeśli młoda osoba będzie z automatu stosowała niepoprawne działania programistyczne, to możemy spodziewać się problemów w przyszłości. Oczywiście, nie wszystko da się wyplenić, ale można się starać i przeglądy kodu są niezłą okazją do tego.
Same code reviews należą do technik statycznej analizy kodu, czyli takich, w których kod weryfikowany jest bez uruchamiania programu. Ma to swoje zalety - możemy skupić się na jednej rzeczy oraz jest w miarę proste do przeprowadzenia, nawet bez skonfigurowanego projektu i bez jego pobrania na dysk. Z drugiej strony, istnieją też błędy, które zwyczajnie można przegapić podczas zwykłej analizy kodu. Dlatego warto łączyć statyczną weryfikację z dynamiczną i zwyczajnie uruchamiać dany fragment oprogramowania, sprawdzając, jak w praktyce zachowują się poszczególne elementy (np. mając do dyspozycji listę DoD). Pamiętajmy, że rezultatem przeglądu kodu powinno być nie tylko lepsze źródło i szerzenie pozytywnych zmian w zespole programistycznym, ale też przede wszystkim po prostu lepsze oprogramowanie.
Czy można podejść do tego inaczej?
Inną techniką weryfikacji kodu, o której niektórzy mogli słyszeć na różnych zajęciach, kursach lub studiując koncepcję metodyk zwinnych, jest programowanie parami, zwane również programowaniem ekstremalnym. Polega to na tym, że nad daną funkcją pracują równocześnie dwie osoby, z których jedna faktycznie koduje, a druga siedzi obok, kontroluje proces programowania, podrzuca pomysły i na bieżąco ocenia to, co pisze pierwszy programista (w możliwie neutralnym tonie - tu nie chodzi o to, aby wyżywać się na koledze czy koleżance). Po jakimś czasie następuje zamiana ról. Oczywiście, same pary też się zmieniają co pewien czas.
W teorii może wydawać się, że jest to marnotrawstwo zasobów ludzkich - przecież druga osoba może zajmować się w tym czasie czymś innym. Natomiast w praktyce programowanie parami ma kilka zalet:
- jest to de facto przeglądanie kodu, ale na bieżąco - druga osoba może wyłapać pewne pułapki programistyczne już na etapie ich pisania, a nie dopiero, gdy cała duża funkcja powstanie. Co za tym idzie, potencjalnie skraca to czas późniejszych poprawek,
- propaguje wiedzę o kodzie w zespole - dlatego tak ważne jest mieszanie par, aby naturalnie informacje o kodzie w danym projekcie "rozpływały" się po grupie programistów,
- pozwala każdemu stać się lepszym, szczególnie jeśli młodsi programiści będą w parze z bardziej doświadczonymi, ale także w drugą stronę - doświadczeni mogą zobaczyć, jaki pomysł na rozwiązanie ma "młody" (lub "młoda"),
- pozwala lepiej zrozumieć problemy, z jakimi mierzą się inni programiści i starać się pomóc je rozwiązywać, aby nie chodzili w kółko. Spotkania na stojąco nie we wszystkim pomogą - czasem trzeba zobaczyć to z bliska,
- dodaje trochę "pozytywnego stresu", przez co programiści, pod czujnym okiem "publiczności", skupiają się na pracy i poprawiają swój warsztat. Może też zapobiegać prokrastynacji, chyba że trafi się dwóch "leniuchów" w parze,
- zacieśnia więzi społeczne w zespole.
Oczywiście, trzeba to wyważyć - nie każdy zespół będzie dobrze pracował w parach, nie zawsze warunki na to pozwalają lub zadania są do tego nieodpowiednie. Przykładowo, czysto mechaniczne wymagania (choćby kolejny CRUD) są na tyle "prostolinijne", że parowanie programistów faktycznie może być tutaj uznane za marnowanie zasobów. Tym niemniej, jest to technika, nad którą warto się pochylić.
Jak to robimy w Wilda Software?
U nas funkcjonują przeglądy kodu, które są zazwyczaj robione po każdym zadaniu, ale jeśli nawet zbiera się większa grupa zmian, to i tak każdy review dotyczy każdej modyfikacji osobno. Oprócz samej statycznej analizy, do większości zadań mamy stworzone listy DoD, które pozwalają zweryfikować funkcjonalność także na poziomie dynamicznym. To taki standard - wiadomo, że w zależności od zadania możemy przyjąć również inne sposoby weryfikacji (w tym testy automatyczne), które wykraczają poza temat tego artykułu.
Ze szczególną uwagą podchodzimy do przeglądów w przypadku kodu nowych osób - tak, jak opisałem wyżej, w tym przypadku wyrabianie określonych nawyków i utrwalanie standardów jest sprawą szczególnie istotną, dlatego tutaj code review jest znacznie dokładniejszy. Zazwyczaj odpłaca się to potem lżejszą pracą dla sprawdzającego w przypadku kolejnych przeglądów.
A kto przeprowadza analizę? Najczęściej nasi główni programiści lub po prostu osoba, która dane zadanie wygenerowała. Zdarzają się również przeglądy przeprowadzane wewnątrz zespołu, przez osobę, która z danym zadaniem wcześniej nie miała do czynienia. Dzięki temu wiedza o funkcjonalności i poszczególnych fragmentach kodu może zostać szybciej przeniesiona pomiędzy różnymi osobami.
Podsumowanie
Przeglądy kodu mogą wydawać się niepotrzebne, gdyż zajmują czas. Jednak jest to myślenie krótkowzroczne - w praktyce bez recenzji kod bardzo szybko się psuje i po pewnym czasie dochodzi do sytuacji, w której oprogramowanie wymaga gruntownej przebudowy, w efekcie czego zespół IT traci jeszcze więcej czasu. Code reviews nie zapobiegną całkowicie takiej ewentualności, ale pozwalają ją kontrolować, znacznie ograniczać i wcześniej wykryć potrzebę refaktoryzacji mniejszych fragmentów, co pozytywnie wpłynie na późniejszy rozwój kodu.
Pozdrawiam i dziękuję - Jakub Rojek.