Nic nie wpływa na pracę zespołu w projekcie tak bardzo jak zły, nieczytelny, niechlujny, pisany na szybko bez przemyślenia kod. Dynamikę zespołu da się poprawić, wymagania można przedefiniować, a harmonogram można zmodyfikować. Jednak zły kod psuje się, stając się coraz większym ciężarem dla zespołu.
Jaki jest czysty kod?
- elegancki
- efektywny
- prosty
- łatwy do zmian
- opisywalny
- minimalny
- taki jakiego się spodziewaliśmy
- stworzył go PROGRAMISTA – nie język
Pisząc ten artykuł, przede wszystkim skupię się na kilku podstawowych kwestiach dotyczących zmiennych oraz funkcji.
Wymyślając nazwy zmiennych stosujmy się do kilku zasad, które sprawią, że nasz kod będzie bardziej czytelny – nie tylko dla nas ale i również dla osób, które będą razem z nami tworzyć kod lub w przyszłości przejmą po nas projekt.
Przede wszystkim nazwy zmiennych nie mogą być jednoliterowe (dwuliterowe również nie 😉 ). Ile razy widziałeś/aś zmienną x, z, c, a, b, czy jakąkolwiek inną literę alfabetu i zastanawiałeś/aś się czy dana zmienna jest w ogóle potrzebna? Do czego ona służy? Jaki był cel jej stworzenia? Aby odpowiedzieć sobie na te pytanie musiałeś/aś przejrzeć kod w poszukiwaniu miejsc, gdzie dana zmienna mogła zostać użyta. Mniejszy problem, jeśli była to zmienna w obrębie jednej funkcji, a co jeśli była to zmienne globalna? Oczywiście wyjątkiem są tutaj zmienne wykorzystywane w pętlach: i, j, k.
Poszukując wykorzystania danej zmiennej sam/a zadałeś/aś sobie pytanie: „Jaki był cel jej stworzenia?”. Jest to kolejna ważna kwestia, którą powinniśmy uwzględniać podczas tworzenia nazw zmiennych. Powinny one przedstawiać intencję osoby tworzącej kod. Dzięki temu nie będziesz się już zastanawiać czy zmienna odnosiła się do tej funkcjonalności, czy może do innej.
Pamiętaj również, aby Twoje nazwy nie zawierały drobnych różnic. Większość z nas, jak nie wszyscy, posługują się wyborem np. metody z klasy, poprzez podpowiedzi naszego IDE. Zastanów się jak prosto pomylić się w wyborze odpowiedniej funkcji, kiedy jedyna różnica w ich nazwach to pojedyncza litera na końcu? Jeśli już o tym mowa, to dobrą praktyką jest również tworzenie nazw, które jesteśmy w stanie wymówić. Nie powoduje to problemów z domyślaniem się jakie zadanie ma do wykonania dana funkcja.
Parę przykładów:
Źle | Dobrze |
---|---|
int sc List<string> list1 = new List<string>(); const goodWorkersInThisOffice const goodWorkerInThisOffice function sendEmails(a,b) {} Class sdntRcrd |
int studentsCount List<string> students = new List<string>(); function sendEmails(content,recipients) {} Class StudentRecord |
Źle
int sc List<string> list1 = new List<string>(); const goodWorkersInThisOffice const goodWorkerInThisOffice function sendEmails(a,b) {} Class sdntRcrd
Dobrze
int studentsCount List<string> students = new List<string>(); function sendEmails(content,recipients) {} Class StudentRecord
W różnych projektach często spotykam się z sytuacją, kiedy ktoś nie zastosował stałych. Wyobraź sobie sytuację, kiedy masz parę warunków, od których zależy działanie Twojego kodu. W każdym z nich sprawdzasz, czy np. ilość produktów w koszyku jest większa od 6, czy ktoś zakupił dany produkt itd. Wszystko działa super dopóki nie przyjdzie pora, aby zmienić warunki promocji. Jak łatwo zapomnieć o jednym warunku lub po prostu się pomylić. Aby uniknąć takich sytuacji posługujemy się właśnie stałymi. Dzięki ich wykorzystaniu nie tylko możemy jednocześnie zmienić wartość w wielu miejscach, lecz, co często ważniejsze, ich nazwy mówią nam czego dotyczy dana wartość.
Źle | Dobrze |
---|---|
if (p.count > 9) {} |
const MIN_PRODUCTS_IN_CART = 9; if (products.count > MIN_PRODUCTS_IN_CART) {} |
Popularnym błędem wśród mniej doświadczonych koderów, jest również zapisywanie typów zmiennych w ich nazwach. Nie ma co się tutaj chyba zbytnio rozpisywać, bo wydaje mi się, że sprawa jest stosunkowo prosta. Nie raz na pewno zdarzyło Ci się, że dana zmienna na początku Twojej pracy była string’iem, jednak po paru zmianach stała się ona np. int’em.
Źle |
---|
int securityCodeString // zmienna była kiedyś stringiem |
Starajmy się również nie używać przedrostków. W wielu przypadkach jest on zbędny i, w oczach bardziej doświadczonych programistów, po prostu miesza niepotrzebnie w kodzie.
Źle | Dobrze |
---|---|
Class Worker { private w_name: string; setName(name: string) { w_name = name; } } |
Class Worker { private name: string; setName(name: string) { this.name = name; } } |
Przejdźmy teraz powoli nazw do metod/funkcji.
Dobra praktyką, stosowaną przez wiele osób, jest stosowanie czasowników jako nazw metod, np. course.submit()
Używanie przedrostków „get”, „set”, „is” również jest polecane oraz ułatwia później wykorzystywanie danych funkcjonalności.
if (course.isCompleted()) { totalCompetedHours += course.getTotalHours(); user.setTotalHours(totalCompletedHours); }Jeżeli mamy taką możliwość starajmy się również nie przeciążać konstruktorów, a używajmy fabryk. Bardzo ułatwia to późniejsze czytanie kodu.
Źle | Dobrze |
---|---|
City lodz = new City("Łódź"); |
City lodz = City.FromName("Łódź"); |
Na ten moment mam dla Ciebie tyle.
Chciałbym jednak wspomnieć jeszcze o paru rzeczach poza tworzeniem nazw:
- Dodawajmy do naszego kodu kontekst. Nie używajmy do tego jednak nadmiarowości przedrostków do zarządzania kontekstem:
Źle Dobrze Name = "Imię nazwisko" class StudentAddreess class WorkerAddreess
Worker.name = "Imię nazwisko" class Address
- Nie bójmy się przycisku „refactor” w naszym IDE. To nic złego zmienić nazwy na takie, które będą bardziej dopasowane do sytuacji lub/i bardziej czytelne.
- Stosujmy narzędzia, które pomogą nam w utrzymaniu i pisaniu czystego kodu. Równie ważną rzeczą podczas pisania czystego kodu jest stosowanie przez zespół wspólnie wypracowanych zasad, np. tabulatory czy spacje, długość wcięcia, pusta linia na końcu pliku, maksymalna długość linii itd. Dzięki temu nie tylko będzie prościej przeprowadzać Code Review, ale również bardziej czytelne staną się dla nas komunikaty o zmianach, które dostarcza nam np. GIT. Osobiście polecam: editorConfig, tslint, eslint, prettier oraz dla VS Code: typescripthero
Mam nadzieję, że dzięki temu artykułowi Twój kod będzie czytelniejszy i bardziej zrozumiały dla innych. Super, jeśli był on dla Ciebie tylko odświeżeniem podstawowych informacji na temat czystości kodu. Obojętnie, która z opcji jest bliższa Twojej wiedzy na ten temat, pamiętaj o tym, że kod, który aktualnie piszesz ktoś może po Tobie przejąć.
Hej, artykuł fajny
Miałbym kilka uwag, jeśli chodzi otreść
– Worker.name = „Imię nazwisko” – mutowanie obiektów nie jest zbyt wydajne ani zbyt dobre. Przy zmianie stanu, powinniśmy zawsze tworzyć nowy -dużo wydajniejsze rozwiązanie.
– brak zwracanego typu metod oraz niektórych argumentów funkcji;)
– const MIN_PRODUCTS_IN_CART = 9;
if (products.count > MIN_PRODUCTS_IN_CART) {}
stosowanie ifów w takich przypadkach jest zbędne i nie prowadzi do polepszenia czytelności. Użyłbym tu raczej programiwania funkcyjnego np
of(products).filter(({count}) => count > minProductsInCart)
– kolejna sprawa to konwencja nazw – jeśli mówimy o Front-Endzie, to UPPER_SNAKE_CASE jest tolerowalną konwencją, ale lepszą moim zdaniem jest po prostu lowerCamelCase zgodnie z tym, co rekomenduje np. goolge team – https://angular.io/guide/styleguide#constants 😉
– ogólnie jeśli chodzi o clean code to można by pisać encyklopedię z dobrymi praktykami. Zabrakło mi tu jednak kilku podstawowych platform agnostic rozwiązań – DI, zasady dobrego programowania OOP, functional ireactive programming, immutable states, pure functions oraz wytłumaczenia co znaczy kod testowalny 😉
Nie mniej jednak dobra robota!
Pozdrawiam
Fajny post – Dobra robota. Ze swojej strony polecam wszystkim zainteresowanym tematem książkę „Czysty kod. Podręcznik dobrego programisty” Roberta C. Martina