18 mar 2024 POIT #237: Software Craftsmanship: Clean code
Witam w dwieście trzydziestym czwartym odcinku podcastu „Porozmawiajmy o IT”. Tematem dzisiejszej rozmowy w serii podcastów o software craftsmanship jest clean code czyli czysty kod.
Dziś moim gościem jest Łukasz Drynkowski, z którym mam przyjemność współtworzyć portal z ofertami pracy dla branży IT o nazwie SOLID.Jobs.
Główne myśli o clean code z tego odcinka to:
- czysty kod to kod zrozumiały dla drugiego człowieka będąc przy tym prosty ale realizujący postawione przed nim zadanie,
- stosujmy go nie tyko w kodzie produkcyjnym ale i w testach,
- czysty kod to tradeoff, identyfikujmy poprawnie miejsca i sytuacje gdzie trzeba złamać te zasady.
Subskrypcja podcastu:
- zasubskrybuj w Apple Podcasts, Google Podcasts, Spreaker, Sticher, Spotify, przez RSS, lub Twoją ulubioną aplikację do podcastów na smartphonie (wyszukaj frazę „Porozmawiajmy o IT”)
- ściągnij odcinek w mp3
- poproszę Cię też o polubienie fanpage na Facebooku
Linki:
- Profil SOLID.Jobs na LinkedIn – https://www.linkedin.com/showcase/solid.jobs/
- SOLID.Jobs – https://solid.jobs/
Wsparcie na Patronite:
Wierzę, że dobro wraca i że zawsze znajdą się osoby w bliższym lub dalszym gronie, którym przydaje się to co robię i które zechcą mnie wesprzeć w misji poszerzania horyzontów ludzi z branży IT.
Patronite to tak platforma, na której możesz wspierać twórców internetowych w ich działalności. Mnie możesz wesprzeć kwotą już od 5 zł miesięcznie. Chciałbym oddelegować kilka rzeczy, które wykonuję przy każdym podcaście a zaoszczędzony czas wykorzystać na przygotowanie jeszcze lepszych treści dla Ciebie. Sam jestem patronem kilku twórców internetowych i widzę, że taka pomoc daje dużą satysfakcję obu stronom.
👉Mój profil znajdziesz pod adresem: patronite.pl/porozmawiajmyoit
Pozostańmy w kontakcie:
- 📧 Jeśli masz jakieś pytania lub komentarze, pisz do mnie śmiało na krzysztof@porozmawiajmyoit.pl
- 📩 Zapisz się na newsletter, aby nie przegapić kolejnych ciekawych odcinków
- 🎙 Subskrybuj podcast w lub
Muzyka użyta w podcaście: „Endless Inspiration” Alex Stoner (posłuchaj)
Transkrypcja podcastu
To jest 237. odcinek podcastu Porozmawiajmy o IT, w którym w cyklu rozmów z Łukaszem Drynkowskim z portalu z ogłoszeniami pracy IT SolidJobs, który zresztą mam przyjemność współtworzyć, dyskutujemy o Software Craftsmanship, czyli o rzemiośle programisty. Zapraszamy do słuchania i komentowania. A teraz życzymy Ci już miłego słuchania.
Odpalamy!
Cześć, Łukasz!
Cześć, Krzysztofie!
To już nasze kolejne spotkanie w ramach serii podcastów o Software Craftsmanship. Mówiliśmy już o etosie programisty, mówiliśmy o Secure by Design, o programowaniu obiektowym i funkcyjnym, a dzisiaj, mam wrażenie, dotkniemy zupełnych podstaw, czyli clean codu, czystego kodu. Bo jeśli właśnie zabraknie nam zadbania o ten aspekt, to niezależnie od tego, jak bardzo doświadczonych programistów byśmy nie mieli w projekcie, jak bardzo zaawansowany albo prosty byłby to projekt, to jest to po prostu proszenie się o problemy i przepis na katastrofę.
No właśnie, clean code. Łukasz, co to dla Ciebie znaczy, jak Ty to interpretujesz, jak Ty to widzisz?
Przygotowując się do tego odcinka, wpisałem frazę clean code w wyszukiwarkę YouTube’a. Co się okazało? Okazało się, że wcale tutaj tych materiałów nie ma aż tak dużo, albo inaczej: są, ale pewnie dotarcie do tego, co jest sensowne, nie jest może najprostsze. Myślę, że nie chciałbym się skupiać w tej rozmowie tutaj na takich podstawach, tylko właśnie spróbujmy opisać, jakie znaczenie ma ten clean code taki bardziej high level.
Natomiast jeśli chcecie tutaj, Drodzy Słuchacze, takie bardziej szczegółowe zasady, to polecam właśnie na YouTubie wyskoczyła fajna playlista, którą częściowo już przesłuchałem, Uncle Boba czyli myślę że najlepszego możliwego źródła. Krótka playlista, 9 godzin podzielone na kilka filmów po około godzince. Myślę, że w tak wolnej chwili naprawdę warto, po tym co już odsłuchałem, to polecam. Tym bardziej że ja np. książkę czytałem pewnie jakieś 10–15 lat temu, więc takie odświeżenie tych zasad myślę, że warto.
Natomiast przechodząc do Twojego pytania, czym jest ten clean code, to myślę, że dla każdego tutaj z nas ta definicja trochę będzie się różnić i ja to rozumiem przez pisanie kodu, który jest po prostu jak najprostszy w zrozumieniu, i takiego, który pozwala na łatwy dalszy rozwój oprogramowania.
Przygotowując się do odcinka, znalazłem takie statystyki, które mówią, że samo pisanie kodu zajmuje programiście… to jest średnia branżowa, oczywiście to od każdego z nas zależy od języków programowania, od zespołu, ale średnia branżowa to jest tylko 10% tego, co my programiści robimy, to jest pisanie nowego kodu takiego, który daje tę wartość biznesową, czyli z tych 40 godzin w tygodniu, które mamy, to są 4 godziny tygodniowo. I teraz pytanie, czy temu Timowi Ferrisowi, jak pisał książkę, o to chodziło. No chyba niekoniecznie, tak?
Nie wydaje mi się. No tak, ale właśnie coś w tej prostocie jest, bo dla mnie z kolei czysty kod to oczywiście można go interpretować czy widzieć jako kod prosty, jako taki wystarczający, taki, który realizuje to, co sobie założyliśmy i nic poza to, to jest jak gdyby z jednej strony, ale z drugiej strony jest w tym czystym kodzie, gdy patrzysz się na projekt, na jakiś tam fragment powiedzmy kodu właśnie napisany, ale zgodnie z tymi najważniejszymi zasadami opisanymi przez Uncle Boba, to ma się poczucie, że on jest ładny, że on jest jakiś taki, wiesz, piękny, przy czym wynika to właśnie z tej prostoty, z tego ułożenia, z tej klarowności, nie?
I to jest podobnie jak z takim, wiesz, dobrym designem, który widzi się, no tutaj może przytoczę na przykład Apple’a. Tam właśnie cała ta prostota definiuje to, że te produkty są ładne i się podobają.
I tak samo kod. Jak już masz troszkę do czynienia z różnego typu projektami napisanymi lepiej czy gorzej, to gdy patrzysz na właśnie taki kod, który mniej więcej podąża tymi zasadami, to masz wrażenie, że on jest z jednej strony prosty, ale z drugiej strony widzisz, że tam wszystko działa, widzisz, że jesteś w stanie to po prostu dobrze wykonać, rozczytać, zinterpretować, wiesz, co tam się dzieje. Więc to mam wrażenie, że z jednej strony jest podążanie według takich zasad dosyć oczywistych, a z drugiej strony jest swego rodzaju piękno właśnie w takim kodzie wynikające z tej prostoty. Ja tak to widzę.
Tak, ale oczywiście nie robimy tego dla sportu. Nie chodzi o to, żeby kod był czysty, dlatego, żeby był czysty. To już Donald Knuth powiedział, że nie piszemy kodu po to, żeby komputer go zrozumiał, tylko piszemy go po to, żeby inni programiści zrozumieli, czego chcemy od komputera. Programowanie to w dużej mierze jest sport zespołowy, tak? Tak że już nawet abstrahując od tego, nawet my wracamy do swojego kodu po jakimś czasie i teraz pytanie jest, czy jesteśmy w stanie od momentu zero usiąść i coś wprowadzić, jakieś zmiany, poprawki, czy będziemy tutaj tracić teraz pół dnia na to, żeby dojść do tego, co autor miał na myśli. I chodzi o to, żebyśmy po prostu byli w stanie rozwijać to oprogramowanie w miarę sensowny sposób, w miarę po prostu szybko, sprawnie i bez bezsensownych narzutów czasowych.
To już Donald Knuth powiedział, że nie piszemy kodu po to, żeby komputer go zrozumiał, tylko piszemy go po to, żeby inni programiści zrozumieli, czego chcemy od komputera. Programowanie to w dużej mierze jest sport zespołowy, tak? Tak że już nawet abstrahując od tego, nawet my wracamy do swojego kodu po jakimś czasie i teraz pytanie jest, czy jesteśmy w stanie od momentu zero usiąść i coś wprowadzić, jakieś zmiany, poprawki, czy będziemy tutaj tracić teraz pół dnia na to, żeby dojść do tego, co autor miał na myśli.
Tak, bo chociaż w programowaniu przepisuje się pewne takie aspekty sztuki, no to musimy pamiętać, tutaj pewnie warto odnieść się do pierwszego odcinka z naszej serii o etosie programisty, że przede wszystkim realizujemy jakiś projekt, który z kolei zaspokaja potrzebę klienta czy użytkownika. I musi to być mimo wszystko dzieło utylitarne, które w praktyce po prostu się sprawdza. Super jest, jeśli przy tym jest ładne z tego naszego kraftowego punktu widzenia, spełnia te podstawowe zasady. To jest jak gdyby idealny układ. Natomiast trzeba pamiętać, że tutaj forma nie może przerosnąć treści. Właśnie to, co zaznaczyłeś.
Patrząc tutaj jeszcze w takim kierunku, to pytanie brzmi: kto powinien odpowiadać za to, w czyim interesie jest to, żeby ten kod był czysty, bo tutaj myślę, że to jest taki paradoks, że w zespołach najbardziej na tym czystym kodzie to zależy tym programistom, ewentualnie jakimś team leadom. Natomiast tak naprawdę to na tym, żeby ten kod był czysty, to powinno zależeć biznesowi, tym osobom, które wydają tutaj na to pieniądze, bo efekt jest taki, że po prostu oszczędność, czyli tak naprawdę tutaj powinno to być odwrotnie.
Tak, tak. Oczywiście wszystkim powinno na tym zależeć. Na końcu dnia to klient, ta osoba, firma zamawiająca projekt na tym zyskuje najbardziej, ponieważ w dużej mierze myślę, można powiedzieć, że czysty kod jest kodem bardziej jakościowym, który łatwiej jest utrzymać, rozbudowywać itd., który ma mniej bugów. Więc na końcu ten klient zyskuje najbardziej.
Zyskuje też organizacja, oczywiście biznes, bo mniej reklamacji, mniej problemów, to też jest mniej później straconego czasu i pieniędzy, ale zyskują też i programiści, którzy poprzez stosowanie tych zasad tworzą kod, który może być łatwiej zrozumiany przez kolejne osoby, które będą gdzieś tam dołączały do zespołu, albo nawet dla nas samych, bo pewnie nieraz miałeś taką sytuację, że wracałeś gdzieś tam do starego projektu i widziałeś, że git blame mówił, że to Ty napisałeś, a Ty właściwie nie do końca byłeś przekonany, że wiesz, o czym tutaj ten kod do Ciebie mówi. To może nam służyć jako osobom tworzącym w przyszłości wręcz stosowanie tych zasad.
Tak, tu chyba jest nawet taki dość popularny komiks XKCD, że właśnie jest taka sytuacja, kto tu panu tak… Okazuje się, to ja.
Git blame nie kłamie. Ale wiesz co, chciałbym na chwilkę wrócić, bo do tych podstaw, skąd to się w ogóle wzięło, wspomniałeś tutaj o wujku Bobie, Uncle Bobie, który jest autorem właśnie tej bestsellerowej książki Czysty Kod, stąd tutaj ta nazwa i o tym tutaj mówimy.
Natomiast chciałem taką ciekawostkę tutaj powiedzieć i tak się w sumie zastanowiłem, kiedy ta książka mogła być wydana, jak stare są te zasady. I trochę byłem zaskoczony, bo wydawało mi się, że ta książka jest przynajmniej tak stare jak ja, a tymczasem została wydana w 2008 roku, czyli 16 lat temu mniej więcej. I to z jednej strony to wcale nie jest taki okres czasu, który mógłby powiedzieć, że właściwie jesteśmy, czy znamy te zasady już od bardzo długiego czasu. Zastanawiam się, jak to było wcześniej, kiedy te zasady były spisane.
Tak, ale to nie było tak, że tych zasad nie było, Uncle Bob to spopularyzował, zebrał. Pewnie te zasługi popularyzatorskie to tak, natomiast to nie jest tak, że to wszystko on wymyślił, on to ładnie zebrał, spopularyzował. Ja tu jeszcze taką może trochę kontrowersję miałem ostatnio tutaj wprowadzać w rozmowy, żeby to się bardziej niosło, to coś kontrowersyjnego powiem. Uważam, że nie mamy prawa nazywać się senior developerami, jeśli nie przeczytaliśmy w życiu tej książki. Uważam, że jest taka książka, która powinna być na pierwszym roku studiów informatycznych po prostu jako obowiązkowa.
Tak, no niestety muszę się z Tobą zgodzić. Znaczy tak, jak powiedziałeś, przed tą książką ludzie stosowali pewnie wiele z tych zasad. Ci, którzy byli bardziej doświadczeni, ci, którzy dużo widzieli i nauczyli się tego pewnie metodą prób i błędów w dużym stopniu. I wiesz… Mógłbym się z Tobą nie zgodzić i powiedzieć, że tych zasad można się nauczyć, uczestnicząc w wielu projektach, widząc, jak się przewracają. To też jest jakiś tam sposób pod warunkiem, że mamy odpowiednią dozę refleksyjności i jesteśmy w stanie przeanalizować, dlaczego coś tam poszło nie tak. Ale możemy oczywiście tutaj zastosować znacznie prostsze rozwiązanie i po prostu sobie tę książkę zakupić i zapoznać się z tymi zasadami.
Ja natomiast tutaj w tym kontekście mam może też kontrowersyjne zdanie, że być może warto tę książkę przeczytać, kiedy ma się już trochę doświadczenia. Bo ja pamiętam, czytałem ją zupełnie gdzieś tam na początku i nie wszystkie te zasady były dla mnie oczywiste pod względem pozytywów, takich zysków, to czego właściwie możemy doświadczyć, to co nam to daje tak naprawdę.
Bo kiedy czytasz sobie o takiej zasadzie: stosuj proste nazwy, które dobrze opisują to, co funkcja, metoda, klasa ma robić, to może czujesz podskórnie, że to ma sens, ale jak nie zobaczyłeś iluś tam klas, które są nazwane bez sensu i właściwie nie wiesz, co one robią, to w praktyce dopiero widzisz, jaki jest sens.
Warto przypomnieć sobie tę książkę kilka razy. Kiedy ma się już trochę doświadczenia, to te zasady mogą do nas bardziej, lepiej i łatwiej docierać.
Tak, ale z jednej strony wszyscy wiedzą, czym jest czysty kod, wszyscy stosują, a z drugiej strony wchodzisz w nowy projekt i pierwszy plik, który otwierasz, metoda na 3000 linii kodu, jakieś wielkie spaghetti, używanie jakichś skrótów, które nic Ci nie mówią – to jest moim zdaniem wielki problem. Przecież mamy tutaj podpowiadanie składni, naprawdę nie trzeba wpisywać za każdym razem tej pełnej nazwy, wystarczy raz wpisać coś, mówię o jakimś nazewnictwie zmiennych metod, raz wpisać, potem to już się podpowiada, a jednak ludzie tu stosują jakieś skróty, które potem nowej osobie nic nie mówią. Niby to są podstawy, ale jednak w praktyce nie zawsze to tak wygląda.
Też mogę tu opowiedzieć historię kolegi. Zmienił projekt, pojawił się w nowym środowisku i mówi, że on w takiej firmie Solid Company pracuje i dostał pytanie od kolegi programisty, a co to jest ten Solid? Dlaczego ta nazwa? Czyli niby to jest tutaj w takiej zbiorowej świadomości te zasady, te po prostu dobre praktyki, ale jednak się okazuje, że nie zawsze.
Wiesz, bo tak naprawdę każdy z nas wie, że powinien się dobrze odżywiać, nie jeść cukru, wysypiać i ćwiczyć, a jak weźmiemy pod uwagę, ile osób to faktycznie wdraża, to już nie jest tak różowo. Myślę, że tutaj z tymi zasadami, dobrymi praktykami jest podobnie. Właśnie, i jak gdyby sama teoria teorią, czy taka świadomość świadomością, no ale później, jak przychodzi co do czego, codzienność, konieczności wdrożenia feature’a, rozbudowy itd. i nie zawsze pamiętamy o tym, żeby to robić zgodnie ze sztuką.
Tak to później wychodzi, że niestety funkcje są wielkie, nazwy kiepskie, nie ma testów, nie ma wzorców itd.
To tutaj jeszcze kontynuując Twój wątek, co ja np. zauważyłem gdzieś tam w mojej pracy, czy tam jak się stykałem po prostu z rozwojem oprogramowania. Ja tu trochę mówię z perspektywy takiego full stack developera i takich kultur organizacyjnych, gdzie raczej nie było podziałów na backend i frontend, gdzie to osobne osoby robiły. I to, co ja zauważyłem, to że fajnie robimy ten backend, stosujemy te różne praktyki, zasady, staramy się napisać ten kod czytelnie, może nawet gdzieś jakieś wzorce z projektu wyłożymy, a idziemy do frontendu i już zapominamy o tym wszystkim i ten frontend to już jest tak napisany, jakby to robił po prostu gimnazjalista.
I nie chcę tu obrażać frontendowców oczywiście i dlatego mówię, że z perspektywy osób, które robiły full stack development, to też zauważyłem, że często inaczej traktujemy tę część np. frontendową, inaczej backendową, to oczywiście można też na inne elementy systemu tak sprowadzić. Inaczej traktujemy np. część bazodanową, czy pisząc jakieś np. procedury składowane. Inaczej ten kod traktujemy, a inaczej kod traktujemy, jak piszemy to, co jest naszym bread and butter, tego naszego programowania, to tu jednak potrafimy zastosować te zasady.
Oczywiście te zasady też są różne. Niekoniecznie 1:1 można to samo do tego JavaScriptu np., a do tego języka backendowego część tutaj jest po prostu inne trochę podejście, też różne paradygmaty tutaj programowania wchodzą w grę. Inaczej ten czysty kod wygląda przy programowaniu obiektowym, inaczej pewnie przy funkcyjnym.
Tak, coś w tym jest, ale myślę sobie, że tego nie unikniemy, w sensie zawsze będzie tak, że ten nasz kod nie będzie idealny, nie będzie spełniał tych wszystkich zasad, zawsze gdzieś będzie trzeba iść na skróty, nie zawsze się będzie opłacało robić wszystko według tych zasad. Rzeczywistość jest bardziej skomplikowana, natomiast myślę sobie, że warto zdawać sobie sprawę z tego, że to wszystko jest swego rodzaju długiem technicznym, niestosowanie tych zasad, właśnie takie pójście na skróty, odkrywanie koła na nowo, kiedy moglibyśmy zastosować jakiś tam wzorzec.
Warto być tego świadomym albo wręcz warto dołączać właśnie tego typu braki do jakiegoś właśnie spisu tych elementów długu technicznego, które być może w naszym projekcie prowadzimy, żeby nie dopuścić do takiej zasady zbitej szyby – badania w Stanach pokazują, że jeśli w pustostanach jedna szyba zostanie wybita przez chuliganów, to zaraz później już leci to wszystko.
Tak. Tutaj użyłeś też sformułowania wzorce, to ja tutaj jeszcze zadam takie przewrotne pytanie, czy wzorce projektowe to jest czysty kod tak, bo np. zgodnie z moją definicją czysty kod to jest napisanie tego kodu jak najprościej, jak najmniej złożoności dokładając, a wzorce projektowe często jednak tę złożoność do kodu dodają, tak? Może potrzebną, może niepotrzebną. Tak że czy stosowanie wzorców projektowych to jest czysty kod?
Pewnie być może jest fragmentem, ale to nie są synonimy zdecydowanie, bo wzorce projektowe określają właśnie jakiś wzorzec, czyli jakiś przepis na zrobienie czegoś. Oczywiście możemy znaleźć różnego typu biblioteki do różnych języków, frameworków itd., które dają już jak gdyby implementację, np. Singletona czy jakiegokolwiek tam wzorca sobie nie weźmiemy. Natomiast w wielu przypadkach to jak gdyby my implementujemy ten wzorzec, robimy jakąś fabrykę czy cokolwiek takiego. I tutaj też wiele rzeczy może pójść nie tak.
To, że wiemy mniej więcej, jak taka fabryka powinna się zachowywać, to nie znaczy, że nasz sposób implementacji będzie zgodny z tymi zasadami czystego kodu. Ale zgodzę się z tym, że jeśli mamy możliwość wykorzystania tej wiedzy wynikającej z doświadczeń developerów, którzy wcześniej już mieli konieczność właśnie implementowania tego typu rozwiązania, to pewnie warto to zrobić, bo raczej nie porywałbym się na odkrywanie koła na nowo. Więc dla mnie to jest jakby element czystego kodu, ale nie coś, co nam zagwarantuje, czysty kod.
Okej, czyli jak zwykle w IT odpowiedź brzmi: to zależy. Tak, czasami wydaje mi się, że te wzorce projektowe mogą być elementem czystego kodu, tym bardziej jeśli stosujemy takie wzorce, które są ogólnie tutaj w teamie, czy też bardziej popularne. I każdy jak na to spojrzy, to zobacz, o to jest to. A czasami jeśli dodajemy po prostu tę złożoność niepotrzebnie, da się coś zrobić prościej, to może niekoniecznie ten wzorzec jest tutaj czymś, co daje tę dodatkową wartość, a utrudnia. Ale pamiętajmy też, że czysty kod to jest trade-off. To nie zawsze jest tak, że czysty kod to jest coś, co tutaj musimy zrobić. Czasami musimy się zastanowić, co zyskujemy, co tracimy.
Np. przy takich niskopoziomowych optymalizacjach ten czysty kod może nie być możliwy do zaimplementowania. Możliwe, że tutaj wydajność jest czymś bardziej pożądanym w danym module, elemencie.
Czy w GameDev’ie itd.
Tak, to jest na przykład dobry przykład. Też wiem, że sytuacja dużo się poprawiła w ostatnich latach, ale np. ORM-y często wymuszały na nas, żeby coś było publiczne, co np. zgodnie z jakimiś zasadami czystości powinno być co najmniej protected albo private, a ORM wymagał, żeby gdzieś tam coś było publiczne. Albo na przykład Virtual, niekoniecznie chciałeś, żeby ta klasa była możliwa dalej do dziedziczenia po niej, teraz już te ORM-y się wycwaniły i już nawet można simple klasę pewnie tutaj użyć jako encję bazodanową, a on sobie poradzi. Ale jeszcze kilka lat temu musieliśmy otwierać te klasy na dziedziczenie na przykład. W tej chwili tutaj już sytuacja się mocno poprawiła.
Tak sobie myślę, mówiliśmy, że clean code jest tak naprawdę o prostocie i faktycznie jakby spojrzeć na wiele z tych zasad, to one są dosyć proste, nie? Małe klasy, mało parametrów do funkcji, jakieś nieużywanie duplikacji itd. To wydaje się, że to są takie łatwe rzeczy. W toku, że tak powiem, walki z kodem może nam gdzieś to umknąć, bo nie mamy tego całego obrazu i oczywiście możemy zrzucić tutaj odpowiedzialność trochę na code review, że być może to jest taki filtr, który powinien wyłapywać niektóre z tych elementów.
Okej, po części się z tym zgadzam, bo clean code to jest część też jakiegoś uzgodnienia z naszym zespołem, jak podchodzimy do pewnych rzeczy, jakie zasady stosujemy, a być może których nie. Okej. Natomiast jestem też w stanie, że powinniśmy stawiać mocno na zautomatyzowane rozwiązania, które nam po prostu mogą w tym pomóc.
Oczywiście istnieją tutaj narzędzia typu lintery, continuous integration, które nam pomagają. Też takim moim zdaniem definitywnym narzędziem, które pomaga pisać kod, który jest czysty, są testy jednostkowe. Jeśli czegoś się nie da przetestować w łatwy sposób, to jest taki bardzo mocny indykator, że coś zrobiliśmy źle.
Jeszcze może tutaj dodam to, co mówi guru Uncle Bob: pisząc kod, nie powinniśmy się skupiać na tym problemie i na rozwiązaniu na tym kodzie tu i teraz, ale też powinniśmy gdzieś tam w tyle głowy myśleć o takim big picture, o architekturze, o tym, jak te klocki między sobą tutaj współgrają, nie powinniśmy tylko i wyłącznie zajmować się rozwiązaniem problemu, ale też myśleć o tych warstwach abstrakcji, o tym, żeby te warstwy abstrakcji tutaj się nie przenikały, tylko żeby ze sobą jakby odpowiednio rozmawiały przez odpowiednie, nazwijmy to, interfejsy.
Nie mówię tutaj o takim słowie interfejs w rozumieniu takim, jak my programiści rozumiemy, ale po prostu interfejs między jedną warstwą a drugą, żeby po prostu – użyję znowu tego słowa – był czysty przepływ sterowania, czysty przepływ informacji, danych. To jest właśnie ta idea, tak, żebyśmy też trochę z boku patrzyli na to, co robimy, a nie tylko tutaj byli focusowani na rozwiązaniu problemu.
Inna sprawa, to też może niekoniecznie starajmy się od razu pisać coś super, ekstra i na wyrost. Napiszmy tę sztukę i potem myślę, że coś po prostu zrefaktoryzujmy, zobaczmy, co działa, co nie działa, gdzie coś naszym zdaniem teraz jest niezrozumiałego, to też tu wspomniałeś o tych kod review, tak? To ja już mówiłem o tym w poprzednich naszych rozmowach, ale moim zdaniem jak wystawiam ten pull request, to pierwsze co robię, no to sam go przeglądam. Jeszcze zawsze w moim przypadku to tak wygląda, że zanim dodam tę dodatkową osobę do tego pull requesta, to jeszcze zazwyczaj idzie jeden push z poprawkami, tak, po tym pierwszym moim przeglądzie.
Oczywiście można to zrobić lokalnie, ale mnie się wydaje, że to narzędzie jest na tyle wygodne, z którego ja np. korzystam, że wolę to zobaczyć spushowane, tym bardziej że niczemu to nie szkodzi.
Oczywiście istnieją tutaj narzędzia typu lintery, continuous integration, które nam pomagają. Też takim moim zdaniem definitywnym narzędziem, które pomaga pisać kod, który jest czysty, są testy jednostkowe. Jeśli czegoś się nie da przetestować w łatwy sposób, to jest taki bardzo mocny indykator, że coś zrobiliśmy źle.
Jasne, niczemu to nie przeszkadza, a wręcz pokazuje, jaka była historia też rozwoju tego kodu i sposób myślenia itd. Ale okej, nie wracajmy do tego tematu, zainteresowanych odsyłamy do odcinka o code review w naszej poprzedniej serii o narzędziach programisty.
Nasze wcześniejsze dwa spotkania dotyczyły dwóch głównych paradygmatów programowania obiektowego i funkcyjnego. Jestem ciekaw, co ty o tym myślisz, jak te zasady clean code zmieniają się, a może są takie ponadczasowe, kiedy myślisz o programowaniu obiektowym i funkcyjnym. Czy jest jakaś różnica? Bo ja mam, nie ukrywam, pewne zdania na ten temat.
Moim zdaniem jest taka grupa tych zasad, które są ponadczasowe i adekwatne zawsze. Nazewnictwo zmiennych, które mówi ci od razu, o co tutaj chodzi. Trzymanie też jak najmniejszych tych funkcji, klas, żeby po prostu dążenie do tego, żeby coś było, brakuje mi polskiego słowa, a po angielsku pewnie to powiem niepoprawnie, ale comprehensible, czyli żeby było takie zrozumiałe na pierwszy rzut oka i żeby coś stanowiło też taką całość. To jest też może ważne, żeby dany element stanowił taką jednostkę, którą możemy po prostu też gdzieś reużyć.
Potem jest druga grupa zasad, czyli te zasady, które są gdzieś tam specyficzne dla danej sytuacji, dla danego języka programowania nawet, dla danego paradygmatu programowania. Jakie jest tutaj Twoje zdanie w takim razie?
Tak, tak jak powiedziałeś, są jakieś zasady typu unikanie duplikacji czy sensowne nazownictwo, które niezależnie od tego, jaki byśmy sobie paradygmat nie wymyślili, to zawsze będą miały sens, ponieważ w dużej mierze służą one ludziom, właściwie komputer sobie z tym poradzi, niezależnie od tego, jaką nazwę tam użyjemy, niezależnie, ile parametrów funkcji czy metody sobie wciśniemy, natomiast ta poprawa czytelności jest zawsze warta uwagi, niezależnie od tego, w jakim paradygmacie tutaj sobie kodujemy.
Natomiast jeśli chodzi o programowanie funkcyjne, mam wrażenie, że mamy prostszą sytuację, ponieważ część z tych zasad jakoś się tam nie do końca aplikuje, np. wzorce projektowe, które prawie w ogóle nie występują w programowaniu funkcyjnym. Tam wszystko jest funkcją, generalnie polega na składaniu funkcji, więc nie ma konieczności stosowania wzorców projektowych. Pamiętam kiedyś taką prezentację, na jakimś meetupie właśnie robiłem na temat wzorców projektowych w Elixirze i tam się okazało, że to zawsze jest takie trochę przekombinowane, że da się to zrobić po prostu prościej, natywniej, korzystając z funkcji.
No tak, ale tutaj myślimy o wzorcach projektowych, ale są też wzorce architektoniczne, są wzorce w rozumieniu good practice, tego jak te funkcje mają być złożone ze sobą. Też to są wzorce.
Tak, patrząc z tego punktu widzenia, to nazwałbym to może jakimiś dobrymi praktykami czy dobrymi rozwiązaniami architektonicznymi. Jasne, oczywiście to zawsze ma jakieś swoje zastosowania, natomiast myślę, że te wzorce takie klasyczne, projektowe mało występują jako takie w programowaniu funkcyjnym.
Tym niemniej, niezależnie od tego właśnie w jakim języku, w jakim paradygmacie pisze się oprogramowanie, to bardzo wiele z tych podstawowych zasad ma swoje zastosowanie. Oczywiście zasada solid nie występuje, ponieważ ona jest specyficzna dla planowania obiektowego, ale już na przykład dry, jak najbardziej, więc musimy sobie wybrać, dobrać do tego paradygmatu, który używamy, te zasady, które mają tam zastosowanie.
Tak i pamiętajmy, że zasady są po to, żeby je łamać, tak? Tylko musimy rozumieć, dlaczego to robimy.
O, to jest ważne.
Jeśli rozumiemy, dlaczego łamiemy w danej sytuacji tę zasadę, np. właśnie tutaj trade-off jakości, a optymalizacji działania, to wszystko okej. Albo programujemy współbieżnie i tutaj jest np. sytuacja, że musimy zapewnić, żeby ta zmienna nie była gdzieś w dwóch różnych wątkach jednocześnie dostępna, tego typu sytuacje często po prostu powodują, że musimy zrezygnować z pewnych zasad, z pewnych wzorców.
Tak, taki świadomie zaciągany dług techniczny myślę, że jest okej, jeśli właśnie spełnia tę zasadę, że wiemy, dlaczego to robimy, a nie po prostu nam się to odkłada, a my tak naprawdę nic nie próbujemy też z tym robić. Założeniem długu technicznego jest to, że będziemy jakoś też nim zarządzać, czyli będziemy wybierać ewentualnie takie elementy, które chcemy zmienić, a niektóre po prostu uznamy, że nie są tego warte, bo nie wiem, z punktu widzenia biznesowego czy projektowego nie ma sensu w to inwestować czas. Na tym to zarządzanie długiem technicznym polega.
A zadam Ci takie jeszcze pytanie, bo mówiłeś, że czytałeś książkę, tak? 15 lat temu tak jak ja. Gdybyś taką jedną myśl miał tutaj przytoczyć, która gdzieś tam wbiła Ci się do głowy, to co to by było? Jaka byłaby to zasada? Nie chciałbym tutaj tych wszystkich zasad omawiać, bo to nie jest miejsce i pewnie fajniej to też, jeśli nasi słuchacze gdzieś zobaczą to na YouTubie, gdzie jest jeszcze przykład widoczny na ekranie. Ale taka jedna zasada, która Tobie po prostu gdzieś tam się wbiła.
Jasne. Wiesz, to co gdzieś tam jakoś intuicyjnie czułem, ale co też bardzo dobrze naocznie zobaczyłem w momencie, kiedy zacząłem bardziej funkcyjnie programować, to jest to, żeby te kawałki kodu, niezależnie od tego, czy to są funkcje, metody, czy klasy były małe, realizowały tylko jedną odpowiedzialność. Bo w momencie, kiedy się wraca po jakimś tam czasie do takiego kodu, to dużo prościej go zrozumieć. Debagowanie jest znacznie prostsze i rozszerzanie jest prostsze. To jest taka podstawa, która może na co dzień nie przynosi od razu mierzalnych, zauważalnych efektów, ale jak się jest już trochę czasu w projekcie, to widać, że ta zasada jest naprawdę istotna, a jest dużo trudniej, kiedy mamy taki big ball out of mud, rozbić to na jakieś mniejsze klasy, mniejsze odpowiedzialności. Zajmuje to taka refaktoryzacja po prostu trochę czasu, więc na to bym postawił. A Ty, jak u Ciebie?
Bardzo dobry przykład. Oczywiście tutaj z gwiazdką. To, co mi się wbiło w głowę, to że jeśli my jako programiści musimy w kodzie napisać komentarz, to jest to nasza porażka. Ten kod powinien sam pokazywać intencje, sam mówić, co on robi. Oczywiście tu duża gwiazdka, wiadomo, że to nie jest tak zawsze. Czasami warto, np. komentarz powinien zawierać, dlaczego coś zrobiliśmy, jeśli łamiemy zasady, tak jak przed chwilą powiedzieliśmy. Wtedy warto napisać komentarz, dlaczego złamaliśmy te zasady. Natomiast jeśli komentarz opisuje, co tam się dzieje, to musi wynikać z tego kodu, co tam się dzieje.
To, co mi się wbiło w głowę, to że jeśli my jako programiści musimy w kodzie napisać komentarz, to jest to nasza porażka. Ten kod powinien sam pokazywać intencje, sam mówić, co on robi. Oczywiście tu duża gwiazdka, wiadomo, że to nie jest tak zawsze. Czasami warto, np. komentarz powinien zawierać, dlaczego coś zrobiliśmy, jeśli łamiemy zasady, tak jak przed chwilą powiedzieliśmy.
Tak, przy czym ja bym tutaj wyraźnie rozróżnił komentarz w takim rozumieniu, jak Ty powiedziałaś, a dokumentację, bo jestem osobiście fanem, żeby dokumentacja była w kodzie blisko kodu, wtedy łatwiej jest ją utrzymywać, np. opisując, co dana klasa czy metoda robi, jakie parametry przyjmuje itd., wtedy według mnie to ma sens, ale jeśli próbujemy opisywać, co tam się wewnątrz dzieje krok po kroku, to faktycznie coś tutaj poszło nie tak.
Tak, oczywiście tu nie mówimy o tej dokumentacji takiej, która generuje się potem np. automatycznie. Chodzi o takie komentarze, które właśnie opisują, co tam się dzieje. Prawie zawsze to jest po prostu jakaś porażka. Zmieńmy nazwę tej metody, spróbujmy podzielić metodę, zróbmy to tak, żeby po prostu dało się to na pierwszy rzut oka zrozumieć. To też jest jedna z refakturyzacji, czyli zamiana komentarzy w czytelny kod. Ciekawe, czy Fowler to opisał. Chyba nie.
A widzisz, może to jest jakaś tam szansa dla Ciebie, żeby…
Zaistnieć.
Marka osobista sama się nie zbuduje, więc tak.
Tak, tutaj polecamy oczywiście książkę Krzysztofa przy okazji.
No właśnie, tak, dobry most. Mówimy nie bez kozery tutaj o tych zasadach w ramach cyklu o rzemiośle programisty, bo to faktycznie jest element warsztatu, który też wymaga praktyki. W sensie trzeba troszkę tych rzeczy napisać, żeby zobaczyć, co się sprawdza, co nie, jak to się robi itd. I przyznam Ci jeszcze, że mam taką trochę obawę, bo widzę też po młodszych kolegach i koleżankach, że to nie przychodzi tak łatwo, że wymaga to trochę wprawy, praktyki. Myślę sobie, czy to nam się zupełnie nie posypie, kiedy zaczniemy polegać na tym kodzie generowanym przez AI, czy wtedy w niepamięć zupełnie odejdą właśnie te zasady, bo na koniec dnia będzie się dla nas liczyło to, że robi to to, co tam w prompcie napisaliśmy i po prostu sobie copy-paste.
AI trzeba na czymś nauczyć, tak? I teraz pytanie, czy ten zbiór, na którym uczymy to AI, to był ten czysty kod, czy nie był czysty?
No tak, tak, to jest właśnie to. Trochę mam obawy, liczę na to, że być może ten proces code review ze starszymi kolegami nakieruje i pozwoli zobaczyć, jakie są te zasady i kiedy się powinno je stosować. Może jeśli faktycznie mamy taką szansę, żeby gdzieś tam lintery i sprawdzanie statycznych kodów wykorzystywać itd. w procesach CI, może to też gdzieś pomoże, ale jeśli będziemy faktycznie tylko kopiowali ten kod wygenerowany maszynowo, to może się okazać, że nie będziemy mieli okazji nauczyć się tych zasad, bo na koniec dnia będzie to realizowało to, co sobie gdzieś tam wypromptowaliśmy, ale wewnątrz będzie po prostu nieczystym kodem.
A co myślisz o testach? Czy to też podchodzi nam pod czysty kod i co byś wskazał jako takie najistotniejsze, jeśli chodzi o unit testy?
Unit testy moim zdaniem są jednym z najważniejszych narzędzi, które pozwalają nam pisać czysty kod, bo po prostu jak nie jesteśmy w stanie, już chyba o tym wspomniałem, ale jak nie jesteśmy w stanie tutaj dobrze czegoś łatwo przetestować, to jest znak, że z tym kodem produkcyjnym coś jest nie tak.
Natomiast chciałbym tu zwrócić jeszcze jedną uwagę na czystość samych testów, bo też wydaje mi się, że często jest tak, że idąc do testów, zapominamy, że też tutaj są jakieś zasady, też to powinno być dobrze ustrukturyzowane. Też jakby ten zbiór zasad tego pisania czystego kodu dla testów jest może nierozłączny, ale są to inne zasady. Też jeśli chodzi o same testy, są tu wzorce, które też powinniśmy znać, też powinniśmy stosować, też powinniśmy zadbać o to, żeby te testy były gdzieś tam łatwe w obsłudze i żeby też za bardzo ta zależność nie była taka, że nie jesteśmy w stanie teraz nic zmienić w kodzie tym produkcyjnym, bo nam się tysiąc testów posypie i będziemy musieli tutaj większość czasu spędzać na naprawie testów.
Jest cały obszar, myślę, że tu nawet całe książki są, co też jako ciekawostkę powiem, kiedyś czytałem taką książkę, i tak się z nią nie zgadzałem, po prostu, jak czytałem, co tu za bzdury autor mówi, i potem się okazało, że autor też się z tym nie zgadzał, napisał drugą wersję i poprawił. Ale już nie będę może reklamował, bo tej drugiej wersji nie czytałem, więc nie wiem, czy tutaj się poprawił.
Tak że pamiętajmy też, że testy to też czysty kod w testach, pewnie cały obszar, o którym byśmy mogli tutaj nagrać osobny odcinek.
Pamiętajmy, że kod testów to na koniec dnia też jest kod, trzeba go utrzymywać, trzeba go rozumieć, więc większość z tych zasad czystego kodu jak najbardziej się też tam aplikuje.
Dobrze, Łukasz, patrzę na zegarek. Całkiem nam się dzisiaj długawy, że tak powiem, podcast udał, ale bardzo dobrze, bo ten temat jest istotny i warto o nim mówić. To co, może spróbujemy jakoś to wszystko podsumować?
Podsumujmy może, zaczynając od tego, czym ten czysty kod jest. Dla każdego oczywiście ta definicja się trochę różni. Dla mnie to jest po prostu pisanie kodu, który będzie zrozumiały dla innego człowieka i taki, który jest po prostu jak najprostszy, a spełnia te zadania, które ma spełniać.
Drugi element podsumowania, no to pamiętajmy, że ten czysty kod nie tylko stosujmy w tym kodzie produkcyjnym, ale pamiętajmy też o testach, pamiętajmy o tym, że jeśli jesteśmy programistą tutaj backendu, a mamy pisać frontend, to też tam ten czysty kod musi być, jeśli piszemy tutaj jakieś zapytania SQL, też tutaj są zasady osobne, oddzielne, nie chodzi tutaj tylko o ten nasz bread and butter, zawsze o tym pamiętajmy.
Pamiętajmy też, kolejny element podsumowania, że czysty kod to jest trade-off, nie bójmy się tutaj pisać tego kodu trochę mniej czystego, oczywiście są takie zasady uniwersalne jak nazewnictwo i tutaj chyba nie widzę sytuacji, gdzie moglibyśmy złamać te zasady, natomiast pamiętajmy, no dobra, okej, może zawody jakieś na napisanie jakiegoś programu w jak najmniejszej liczbie znaków na czas, okej, ale mimo wszystko, starajmy się tutaj pamiętać, że to nie zawsze jakby ten czysty kod to jest ten właściwy kod, bo może być trade-off na przykład związany z wydajnością oprogramowania.
No i co – testy, testy, testy.
Właśnie, pamiętajmy o tych wszystkich punktach. Pamiętajmy też, żeby do tego tematu podchodzić z głową. To nie jest święty Graal. Tak jak tutaj Łukasz powiedział, trzeba wiedzieć, kiedy się łamię te zasady, bo one są właśnie po to, żeby je łamać. Co oczywiście nie znaczy, że w większości sytuacji jednak to będzie bardzo sensowne, żeby z nich skorzystać.
I oczywiście jeszcze ostatni punkt, czyli starajmy się zawsze myśleć tutaj tak systemowo, starajmy się myśleć o architekturze, starajmy się po prostu myśleć o tych warstwach, o przepływach informacji, przepływach sterowania. Myślę, że jeśli to będziemy robić dobrze, to reszta jakoś tam sama wyjdzie w praniu.
Tak jest, dokładnie. Super, myślę, że mamy to. Tak że Łukasz, bardzo Ci dziękuję za kolejne spotkanie. Zapraszamy oczywiście do wcześniejszych odcinków zarówno z tej serii o Software Customership, jak i do wcześniejszej o narzędziach programisty. Zapraszamy też na SolidJobs, gdzie możecie znaleźć oferty pracy w IT zawsze z widełkami wynagrodzeń. Podkreślę to: zawsze z widełkami wynagrodzeń. I jeśli jest taka potrzeba w Waszym projekcie, to oczywiście możecie tam jak najbardziej wystawić też ogłoszenie.
Jeszcze ode mnie tutaj taka prośba, bo ostatnio czytałem, że to się sprawdza: jeśli te rozmowy dają wam tutaj jakąś wartość, uważacie, że staliście się po nich trochę lepszymi specjalistami, to proszę, podzielcie się tą informacją o tych naszych rozmowach, o tych podcastach ze znajomymi i postarajcie się gdzieś polubić, udostępnić na platformie, na której słuchacie, żebyśmy po prostu mogli docierać do szerszej grupy. Dziękuję bardzo.
Będziemy bardzo wdzięczni. Dzięki, Łukasz. Cześć!
Cześć!