Po co dbać o jakość kodu?
Znowu trafiłeś do projektu, w którym odnalezienie się zajmuje wieki, a co druga metoda ma komentarz „nie dotykać”? A może dopiero uczysz się programować i zastanawiasz się jak to możliwe, że można tworzyć projekt przez wiele lat i panować nad tym co się w środku dzieje? Jeżeli tak to mam dla Ciebie dobrą wiadomość. W tym wpisie opowiem Ci o podstawowych zasadach tworzenia wysokiej jakości kodu i dowiesz się jak do takiej jakości skutecznie dążyć. Przygotuj się na długi i treściwy kawałek tekstu.
Czym jest jakość kodu?
Pytanie na które musimy sobie odpowiedzieć w pierwszej kolejności. Bo jak można mówić o poprawianiu czy dbaniu o coś jeżeli nie wiemy czym to coś jest.
Po pierwsze trzeba powiedzieć, że jakość kodu, mimo usilnych starań niektórych managerów, nie jest czymś do końca mierzalnym. W pewnym sensie jest to zagadnienie subiektywne i różniące się w zależności od zespołu. Dlatego jeżeli ktoś próbuje oceniać jakość kodu jedynie za pomocą konkretnych liczb takich jak złożoność cyklomatyczna czy długość klas to prawdopodobnie nie zna się za dobrze na jakości kodu. Co w takim razie charakteryzuje wysokiej jakości kod? Są to trzy obszary, które teraz omówimy.
Czytelność
Żeby móc pracować z kodem trzeba móc go odczytać.
„Programs should be written for people to read, and only incidentally for machines to execute.” — Structure and Interpretation of Computer Programs
Wygląda to podobnie jak przy czytelności czyjegoś pisma. Ma ona wpływ na to czy cokolwiek z jego notatek się dowiemy i tak samo czytelność kodu wpływa na to czy dowiemy się co on robi.
Czytelność kodu wpływa na wiele aspektów pracy. Zaczynając od szybkości wdrażania się nowych pracowników, przez analizowanie tego jak działa używany przez nas fragment kodu, a kończąc na szybkości i łatwości wprowadzania zmian.
Jeżeli łatwo nam się czyta kod to mamy więcej wiedzy o tym jak on działa. A dzięki temu czujemy się pewniej pracując z takim kodem.
Niezawodność
Warunek chyba oczywisty, a mimo to często ignorowany. Kod wysokiej jakości to również kod, który działa.Ten punkt dotyczy głównie tego czy aplikacja zachowuje się w przewidywalny sposób. Ewentualne błędy nie powinny wynikać z tego, że coś zostało wykonane niedbale. Jeśli patrząc na jakieś miejsce w kodzie nie jesteś pewien jak się zachowa w konkretnej sytuacji to jest duża szansa, że właśnie w tym miejscu pojawi się następny bug.
Również takie rzeczy jak pokrycie testami albo przemyślana architektura wpływają na to czy kod możemy uznać za przewidywalny i niezawodny. Jeżeli wprowadzając zmiany nie mamy pewności czy nie zepsuły one czegoś w istniejącym kodzie to jak możemy mówić o pewności tego, że kod działa? Również ilość i skomplikowanie wszelkich warunków w aplikacji wpływa na ten element.
Podsumowując niezawodność kodu, a więc i jego wysoka jakość to jego przewidywalność i brak dziwnych rozwiązań, których działania nikt do końca nie rozumie.
Utrzymywalność
Jeżeli myślisz, że kod pisze się raz, a potem zostawia się projekt samemu sobie, żeby działał to albo pracujesz w agencji interaktywnej albo robisz zadania na studia.
Prawda jest taka, że każdy kod pisany zawodowo, jeżeli nie mamy zamiaru go od razu porzucić, musi być prosty w utrzymaniu. Wysoka jakość kodu praktycznie gwarantuje prostotę utrzymania.
Na co się składa taka łatwość utrzymania kodu? Przede wszystkim jest to spójność rozwiązań i ich uniwersalność. Aby kod był łatwy w utrzymaniu, a przez to miał wysoką jakość, wprowadzanie w nim zmian i naprawianie błędów powinno być jak najprostsze. Jeśli dodanie kolejnej, podobnej funkcji wymaga poświęcenia wielu dni albo wręcz tygodni to jest to znak, że jakość kodu jest niska.
Utrzymywalność związana jest też z ciągłym dostosowywaniem kodu do aktualnej sytuacji. W przypadku kiedy wprowadzenie zmian jest proste nie masz oporów przed wprowadzeniem modyfikacji, które powodują, że Wasz kod spełnia najnowsze standardy.
Po co dbać o jakość kodu?
Chciałbym nie musieć odpowiadać na to pytanie. Bo chyba każdy woli pracować z kodem, który jest łatwy w analizie, rozszerzaniu i modyfikacji, prawda?
Dbając o jakość w projekcie zapewniamy sobie mniej stresu w pracy. Dla nas jako programistów wysoka jakość kodu oznacza, że możemy się skupić na wprowadzaniu zmian zamiast na walce z tym co już jest. Dzięki temu nie poświęcamy wielu godzin na domyślanie się „co autor miał na myśli” zanim uda nam się wprowadzić naszą zmianę. Wysoka jakość wpływa też na możliwość stosowania aktualnych technologii. Odpowiednia separacja i spójność w kodzie pozwalają na prostsze aktualizacje lub zmiany używanych bibliotek. Kod wysokiej jakości to także łatwość wdrażania nowych pracowników. A przez to brak konieczności poświęcania mnóstwa czasu na odpowiadanie na pytania nowej osoby dotyczące tego co dany kod robi.
Dbanie o jakość kodu to także korzyści dla pracodawcy. Mimo, że na pierwszy rzut oka czas poświęcony na przemyślenie wprowadzanego rozwiązania albo poprawienie tego co już istnieje brzmi jak strata pieniędzy to w dłuższej perspektywie takie podejście przynosi zysk. Być może teraz poświęcisz trochę więcej czasu na wprowadzenie poprawek. Jednak jeżeli tego nie zrobisz to każda kolejna zmiana będzie wymagała coraz więcej czasu. I prawdopodobnie wartość ta będzie rosła wykładniczo. Aż w końcu dojdziecie do momentu kiedy dodanie kolejnej funkcji będzie możliwe tylko jeżeli cały projekt napiszecie od nowa. Czy więc w porównaniu do tego kilka chwil więcej na każdy feature nie brzmi jak ogromne oszczędności?
Antyprzykład
W jednym z poprzednich projektów, przy którym pracowałem, programiści klienta przez wiele lat tylko dokładali kolejne klocki. Nie modyfikując i nie usprawniając niczego co już istniało. Kiedy przyjechaliśmy do nich i zaczęliśmy z nimi pracę okazało się, że dodanie tak prostych rzeczy jak wyświetlenie wartości z bazy to co najmniej tydzień pracy. Na zmergowanie kodu, który był w innych branchu w repozytorium musieliśmy czekać ponad miesiąc. Bo tyle czasu to wymagało. Nie wspominając już o losowych błędach i braku pewności czy to co robimy w ogóle działa poprawnie. Tak więc programiści poświęcali mnóstwo czasu na rzeczy, które powinny być trywialne. A dodawanie nowych funkcji wyceniało się na tygodnie pracy niezależnie od ich wielkości. Wtedy też się przekonałem, że granice niskiej jakości kodu są dużo dalej niż myślałem.
Wybrane zasady w dbaniu o jakość
Wspomniałem, że jakości kodu nie da się zdefiniować konkretnymi wartościami. Mówiłem też, że jest ona trochę względna i zależna od zespołu. Jednak istnieją pewne reguły, które są uniwersalne. I których utrzymanie na pewno wiąże się z poprawieniem jakości Waszego kodu. Tutaj wymieniam tylko 4 ale sam znalazłem przynajmniej 15. Prawdziwą ich skarbnicą jest za to obowiązkowy podręcznik każdego programisty czyli „Czysty kod” Wujka Boba.
Trzymanie się wytycznych stylu kodowania
Przetłumaczenie zwrotu „style guide” nie jest tak proste jak spełnienie tej reguły. Większość języków programowania ma zdefiniowane pewne standardy związane z pisaniem w nich kodu. Mogą one dotyczyć miejsca stawiania spacji, sposobu pisania nazw zmiennych itd. Czasami też zespoły tworzą własne style. Jednak niezależnie od tego czy korzystamy z wytycznych twórców języka czy tych wypracowanych w firmie nie powinniśmy pozwolić sobie na odstępstwa. Każde takie miejsce w kodzie, w którym autor inaczej zapisuje nazwy zmiennych, albo inaczej wstawia nawiasy powoduje, że czytając je automatycznie poświęcamy więcej czasu na analizę.
Jest to tak prosta zasada, że nie wyobrażam sobie, że ktoś potrafił znaleźć wymówkę od jej stosowania.
Czytelne nazwy
Jeżeli studiowałeś informatykę to być może trafiłeś na kod prowadzących, w którym zmienne nazywały się a, b, x, g, z… Teraz wyobraź sobie, że takie nazwy pojawiają się w dużym projekcie w pracy. Jak szybko będziesz potrafił odpowiedzieć na pytanie co przechowuje zmienna c albo x? Bez zagłębienia się w kod prawdopodobnie nie będziesz miał w ogóle pojęcia po co ona tam jest. Dlatego właśnie czytelne, wyjaśniające swoje przeznaczenie nazwy to tak ważna sprawa. Pamiętaj, że czytelność to bardzo ważna cecha wysokiej jakości kodu.
Polecam tutaj tekst na blogu Koziołka, w którym omawia problem wymyślania nazw w kodzie.
Nie powtarzaj się
Zasada bardziej znana pod skrótem DRY. W skrócie chodzi o to aby unikać kopiowania kodu albo pisania tego samego wiele razy tylko dlatego, że różni się małym szczegółem. Wyciąganie fragmentów kodu do funkcji z parametrem potrafi zdziałać cuda jeżeli chodzi o zredukowanie liczby powtórzeń.
A im mniej powtórzeń w kodzie tym mniejsza szansa, że przy zmianie jakiegoś zachowania zapomnimy je zmienić w którymś miejscu. Jeśli masz jakiś fragment skopiowany X razy to przy modyfikacji musisz zmienić to samo X razy. Jeżeli zaś dany fragment jest zamknięty w funkcji to zmieniasz go raz i działa tak samo w X miejscach.
Nie twórz zbyt złożonej struktury
Czasami poznając jakieś zagadnienie od razu zaczynamy widzieć pełno miejsc, w których możemy je wykorzystać. Dokładnie tak jest np. ze wzorcami projektowymi. Poznajemy je i nagle uważamy, że wszędzie powinny być wprowadzone. W takim momencie kończymy z kilkunastoma warstwami abstrakcji nawet dla najprostszych fragmentów kodu.
Przeinżynierowanie i wprowadzenie zbyt dużej liczby wzorców jest takim samym problemem jeśli chodzi o jakość kodu jak ich brak. Bo co z tego, że użyłeś jakieś super wzorce jak teraz dodanie do projektu innego typu walidacji danych wymaga utworzenia minimum 43 klas i interfejsów w każdej możliwej warstwie aplikacji? Nie tędy droga. Wszystkiego z umiarem.
Dobrą praktyką jest wprowadzanie pewnych wzorców i uniwersalnych rozwiązań dopiero w momencie kiedy jakiś kod wymaga tego. I nigdy wcześniej. Bo w 9 na 10 przypadków dodana wcześniej uniwersalność wymaga przepisania jej. Bo nie jest uniwersalna w naszym aktualnym przypadku.
Refactoring
Czasami zdarzy się tak, że polegliśmy i kod, który powstał nie jest w najlepszym stanie. Wtedy, jeżeli tylko nie pracujemy w firmie, która nie przejmuje się swoimi pieniędzmi, nastaje czas na próby doprowadzenia wszystkiego do stanu używalności. Tym procesem jest refactoring.
Pod tym pojęciem kryje się bardzo dużo zagadnień, których nie będę tutaj dokładnie opisywał. Ważne jest jednak żeby pamiętać, że refactoring to nie rzucenie się na przepisywanie całości od nowa. Proces naprawiania kodu powinien skupić się po pierwsze na elementach, które najbardziej „śmierdzą” w kodzie. Ale także na tych, które też najprościej naprawić. Bo jeżeli widzisz, że w jakiejś klasie formatowanie kodu kuleje to nie zostawisz tego bo to nie jest najpoważniejszy problemu u Was. Jeżeli coś można naprawić dosłownie jednym kliknięciem to grzechem jest odpuścić.
Przeprowadzanie procesu refactoringu to proces cykliczny, a wręcz ciągły. Jednak jeżeli robimy to w taki ciągły sposób to jest to również proces prosty i przyjemny. Bo kto próbował wygospodarować w projekcie specjalny czas na refactoring ten wie jakie to jest trudne. W końcu jeżeli dawno niczego nie poprawialiśmy to zrobienie tego nagle oznacza lawinę zmian. Dlatego wysoka jakość kodu to również ciągły refactoring. Powinien on być wręcz stałym elementem każdego zadania. Bo praktycznie zawsze jest coś co możemy poprawić.
Kompleksowy refactoring wymaga doświadczenia. Ale nawet junior może spokojnie wprowadzać niewielkie poprawki. Nawet jeżeli będzie to tylko poprawa formatowania albo nazewnictwa w jakimś pliku. Ostatecznie wystarczy dopytać osoby bardziej doświadczone czy dobrze robimy.
Testy jednostkowe
Na początku pisałem, że jednym z elementów kodu o wysokiej jakości jest niezawodność. Jak w takim razie być pewnym tej niezawodności? Ano wprowadzając testy. Przede wszystkim testy jednostkowe, które na bieżąco dają znać czy wprowadzona przez nas zmiana niczego nie zepsuła. A skoro dają taką informację to również powinny obowiązkowo istnieć przed ruszeniem z procesem refactoringu, który zdecydowanie modyfikuje kod, a więc nie trudno w tym przypadku o zepsucie czegoś co działało.
Temat testów jednostkowych jest naprawdę obszerny i można o nich znaleźć wiele materiałów płatnych i darmowych. Sam posiadam TĘ książkę. Jeżeli nie chcecie czegoś po angielsku to polecam. Jednak jeżeli angielski jest dla Was oczywistym wyborem to po wpisaniu w Google frazy „unit testing c#” znajdziecie mnóstwo materiałów. Możecie też poszukać wspomnianej wcześniej książki w oryginale, który jest naprawdę wartościową pozycją.
Podsumowanie
Jakość kodu to niewątpliwie element, na który każdy programista powinien zwracać uwagę. Mimo, że wysoka jakość kodu wymaga przemyślanego wprowadzania zmian i stałego zwracania uwagi na aktualność rozwiązań to jest to coś co się opłaca.
Napisałem ten tekst bo widziałem już naprawdę ciężki w utrzymaniu kawał kodu. Z drugiej strony na co dzień widzę jak stosowanie prostych zasad i dbanie o jakość ułatwiają pracę. Dzięki temu, że w zespole staramy się trzymać poziom jeżeli chodzi o jakość i aktualność kodu praca jest wystarczająco przyjemna. I mamy czas na bawienie się nowymi technologiami i granie w piłkarzyki ;)
Mimo, że jest to temat dosyć rozmyty i zależny od konkretnego zespołu to można go opisać pewnymi zasadami. Zasadami, które zdecydowanie warto znać. Z resztą z tego powodu cały czas się dokształcam w tym temacie i większość książek, które mam zamiar przeczytać w tym roku dotyczy tematu jakości kodu i jakości pracy.
I również każdemu z Was polecam stałe trzymanie ręki na pulsie i życzę trafiania do projektów, które są utrzymane na najwyższym poziomie.
Mnie zawsze uczono, aby kod był przejrzysty i klarowny ze względu na jego unikalność w użytkowaniu. Chodzi mi o to, że jeśli inna osoba będzie musiała korzystać z mojego kodu to będzie miała ułatwioną robotę.
ten blog to kawał dobrej roboty :) Codziennie tutaj zaglądam i sprawdzam czy pojawił się nowy post.
Podpisuję się obiema dłońmi pod tym, o czym piszesz. Szkoda trochę, że jak zwykle, aby to zrozumieć trzeba to przerobić na własnej skórze, bo czytając o tym, nie weźmiesz sobie tego do serca tak bardzo jak wtedy kiedy się sparzysz :). Co oczywiście, nie zmienia faktu, że trzeba edukować i uświadamiać. Świadomy junior, to bardzo cenny kolega w pracy :).
Dodam jeszcze, że dla mnie najważniejsze jest aby kod był czytelny. Jeśli kod jest jak książka, z bohaterami i akcją, jak dobra beletrystyka, zrozumiesz tak na prawdę każdą, „zawiłą fabułę”.