Skip to content Skip to sidebar Skip to footer

Deskryptywne nazewnictwo i formatowanie: Klucz do czytelnego kodu w Ferrycie

Pisanie czystego kodu to fundamentalna zasada, której przestrzeganie przynosi wymierne korzyści zarówno w krótkim, jak i długim okresie. W świecie tworzenia oprogramowania, gdzie zespoły pracują nad złożonymi projektami, czytelność kodu jest kluczowym czynnikiem wpływającym na efektywność pracy. Czytelny kod jest łatwiejszy do zrozumienia, co pozwala programistom szybciej wprowadzać zmiany, naprawiać błędy oraz rozwijać nowe funkcjonalności. W platformie Ferryt, pozwalającej na tworzenie złożonych i rozwijalnych projektów, dbanie o przejrzystość kodu jest bardzo istotne.

Pisanie czytelnego kodu to nie tylko estetyka, ale przede wszystkim praktyczność. Kod, który jest napisany w sposób jasny i zrozumiały, redukuje ryzyko błędów wynikających z nieporozumień. Pozwala również na łatwiejsze przejmowanie projektów przez nowych członków zespołu, co jest szczególnie ważne w dynamicznych środowiskach pracy. Czysty kod przekłada się na lepsze i łatwiejsze utrzymanie projektu, umożliwiając szybkie odnajdywanie i poprawianie błędów, co w efekcie skraca czas i koszty związane z utrzymaniem oprogramowania.

Starannie dobrane nazewnictwo i odpowiednie formatowanie kodu to dwa kluczowe elementy, które składają się na pisanie czystego kodu. Deskryptywne nazewnictwo sprawia, że kod staje się intuicyjny i łatwy do zrozumienia, a odpowiednie formatowanie zapewnia jego przejrzystość. W tym artykule skupimy się na tych dwóch aspektach, omawiając zasady i najlepsze praktyki, które pomogą Ferryt developerom tworzyć bardziej czytelne, zrozumiałe i łatwe do utrzymania oprogramowanie na platformie Ferryt


Dbanie o deskryptywne nazewnictwo

Deskryptywne nazewnictwo pozwala na pisanie kodu, który jest intuicyjny i łatwy do zrozumienia, niczym dobrze napisana książka. Dzięki temu developerzy mogą szybciej odnaleźć się w kodzie i zrozumieć jego logikę, co jest szczególnie istotne w pracy zespołowej i przy rozwoju długoterminowych projektów.

Dobrze dobrane nazwy powodują, że napisany przez nas kod w Ferrycie się samodokumentuje, czyli do zrozumienia logiki tego kodu nie potrzebujemy dodatkowych objaśnień, dokumentacji, czy instrukcji. Poniżej przedstawię kilka praktycznych zasad dotyczących stosowania „czystego” nazewnictwa.

Nazwa akcji opisuje, co akcja robi

Precyzyjne nazewnictwo akcji pomaga jasno określić ich rolę bez konieczności wchodzenia w akcje i sprawdzania logiki, którą wykonują. Do nazywania akcji powinny być wykorzystywane czasowniki określające jaką czynność akcje wykonują. Nadawanie odpowiednich i przemyślanych nazw (symboli) akcji przy ich tworzeniu jest bardzo istotne, ponieważ edycja symbolu akcji po czasie może być skomplikowana, ze względu na odpinanie uprawnień oraz konieczność wprowadzania zmian w designerze ekranów oraz ponownego podpinania akcji ze zmienionym symbolem w kontrolkach „uruchom akcje”

Antyprzykład:

W poniższym przykładzie akcja „ChceWyszukać” odpowiada za wyszukanie klienta w systemie, natomiast akcja „WybranyKlient” w rzeczywistości odpowiada za pobranie danych klienta z serwisu zewnętrznego za pośrednictwem funkcji workflow. Jak widzimy, nazwy akcji nie opisują, co akcja robi, a nazwy nie zawierają czasowników.

Przykład:

Czystszym zapisem byłoby nadanie nazw akcji z wykorzystaniem czasownika oraz opisaniem, co akcja robi, jak poniżej.

Nazwa reguły opisuje, co reguła robi

Reguły powinny być nazywane w sposób, który jasno określa ich działanie. Nazwy (symbole) powinny być unikalne, aby nie stosować tej samej lub podobnej nazwy dla kilku reguł. Dzięki temu można uniknąć nieporozumień i błędów wynikających z niejasności. Tworząc i nazywając reguły, powinniśmy kierować się zasadą atomowości oraz na tyle, na ile jest to możliwe, wykorzystywać zasadę SRP (Single Responsibility Principle), która mówi, że każda funkcja, w realiach Ferrytowych reguła, powinna odpowiadać za jedno, konkretne zadanie. Dodatkową dobrą praktyką jest nadawanie symbolu oraz nazwy. W nazwie możemy używać polskich znaków i spacji, co ułatwi odczyt.

Antyprzykład:

W poniższym przykładzie widzimy duże podobieństwo w symbolach reguły oraz funkcji Work Flow oraz bardzo wysoki stopień ogólności, nie określający czynności, jaką reguła wykonuje (nullowanie wartości).

Przykład:

Poniżej symbole zostały dobrane tak, aby opisywały, co reguły i funkcja workflow rzeczywiście robią (nullowanie wartości oraz pobieranie danych klienta).

Przykład po dodatkowej refaktoryzacji:

W poniższym przykładzie dokonano dodatkowo mały refactor, wprowadzając nazwy ponieważ łatwiej czyta się tekst z polskimi znakami oddzielony spacjami niż bez polskich znaków zapisany CamelCase’ em. Dodatkowo połączyłem dwie reguły nullujące pola w jedną regułę odpowiadającą za nullowanie wybranych pól.

Nazwa pętli opisuje, po czym pętla iteruje

Pętle wykonują iteracje po danym zakresie zgodnie ze zdefiniowanym warunkiem, dopiero wewnątrz pętli dzieje się logika, zatem nazwa pętli powinna opisywać tylko i wyłącznie, po czym pętla iteruje, zaś opis logiki powinien zostać zawarty w symbolach i nazwach reguł, warunków lub innych bloczków wewnątrz tej pętli.

Antyprzykład:

W poniższym przykładzie nazwa nieprecyzyjnie wskazuje, że w pętli sprawdzane jest źródło rachunku, nie zaś po jakim zakresie pętla iteruje.

Przykład:

Poniżej pętla jasno wskazuje, po czym iteruje, natomiast dalsza logika jest zapisana w nazwach reguł wewnątrz tej pętli.

Warunki opisują, co sprawdzają

Warunek sprawdza zadane przez nas twierdzenie logiczne, zatem korzystając z kontrolki warunek, zadbajmy o zatytułowanie jej odpowiednio – czyli opisując, co chcemy w tym dokładnie warunku sprawdzić. Może być to sprawdzenie pojedynczego wyboru TAK/NIE lub wielokrotnego wyboru z kilkoma wartościami.

Antyprzykład:

W poniższym przykładzie nazwa kontrolki jak i poszczególnych warunków nie opisuje wprost, co dany warunek sprawdza.

Przykład:

Poniżej warunek jasno wskazuje, co sprawdza i jakie są możliwe opcje, zaś dalsza logika jest zapisana czytelnie w symbolach i nazwach reguł.

Nazwa pola opisuje, jakie dane pole przechowuje

Nazwy pól powinny jednoznacznie wskazywać, jakie dane są w nich przechowywane, co ułatwia zrozumienie kodu. Zasada ta w szczególności dotyczy pól technicznych – należy unikać stosowania ogólnych nazw z licznikami np. licznikTechniczny1, DataTechniczna4 itd., ponieważ w przypadku używania takich pól pomiędzy regułami i akcjami bardzo ciężko jest dojść, za co dane pole o takiej ogólnej nazwie odpowiada i co się z tym polem wcześniej lub później dzieje.

Antyprzykład:

W poniższym przykładzie nazwy pól nie pozwalają określić bez szukania w całym kodzie, za co licznik i data odpowiada i jaką wartość przechowuje.

Przykład:

Poniżej nazwy pól pozwalają na zrozumienie logiki jaka stoi za regułą, która ów pola wykorzystuje. Pola te można dla uporządkowania i sygnalizacji, że są to pola techniczne umieścić w strukturze, którą nazwałem PF.PolaTechniczne. W poniższym przykładzie dodatkowo użyłem tzw. cukru składniowego zapisując dodawanie wartości liczbowej do pola w formie +=


Dbanie o odpowiednie formatowanie kodu

Formatowanie kodu wpływa na jego czytelność, umożliwiając developerom szybsze zrozumienie logiki i struktury kodu. Odpowiednie formatowanie ułatwia również wykrywanie błędów i utrzymanie spójności w projekcie. Poniżej przedstawię kilka praktycznych zasad dotyczących stosowania „czystego” formatowania.

Nie sprawdzanie warunków if (true) {}

Używanie edytora zaawansowanego niesie za sobą wiele korzyści w tym pozwala na kompilowanie kodu linijka po linijce bez potrzeby sprawdzania warunku if (true) {} oraz pustych else {} dzięki temu nasza reguła potrafi z ośmiu linijek zostać skrócona do jednej co znacznie przyspiesza czytanie.

Antyprzykład:

W poniższym przykładzie widzimy typowe dla edytora domyślnego sprawdzenie warunku if (true) oraz domyślną linijkę sprawdzającą warunek else {}

Przykład:

Korzystając z edytora zaawansowanego możemy pozbyć się warunku pozostawiając interesującą nas linijkę kodu, która się wykona nie zależnie czy sprawdzimy zawsze spełniony z logicznego punktu widzenia warunek if (true) . Dzięki takiemu podejściu ograniczamy liczbę linijek kodu, który przy kilku warunkach mógłby wymagać dodatkowo scrollowania po treści reguły, co wydłuża i utrudnia płynne zapoznawanie się treścią reguły.

Formatowanie warunków, aby były czytelne

Warunki, które są długie zawierają kilka składowych połączonych operatorami logicznymi można oddzielać nowymi liniami po to, aby każdy ze składowych warunków zaczynał się od nowego wiersza – znacznie ułatwia to czytanie treści takiego warunku klikając na kontrolkę „Warunek” z poziomu Flow Designera.

Antyprzykład:

W poniższym przykładzie monolityczny warunek nie jest oddzielony nowymi liniami. Zapis taki jest trudny do zrozumienia bez dokładnego wczytania się aby odszukać operatory logiczne i zrozumieć logikę.

Przykład:

Poniżej ten sam warunek podzieliłem nowymi liniami po operatorze logicznym dzięki czemu jest on o wiele łatwiejszy i szybszy do zrozumienia.

Usuwanie pustych elsów, zakomentowanych linijki oraz dbanie o indentacje

Usuwanie pustych bloków „else” sprawia oraz pozbywanie się zakomentowanych linijek kodu pozwala utrzymać czystość i klarowność kodu, eliminując niepotrzebny szum i ułatwiając jego przeglądanie. Dbanie o odpowiednią identację – czyli wcięcia w kodzie (domyślnie 4 znaki spacji lub tabulator) zapewnia jednolity wygląd kodu, co znacząco ułatwia jego czytanie i zrozumienie przez wszystkich członków zespołu.

Antyprzykład:

W poniższym przykładzie w regule widzimy zakomentowane linijki oraz puste „elsy”, przez co suma linijek to 21.

Przykład:

Poniżej kod został wyczyszczony ze zbędnych elementów dzięki czemu liczba linijek została zredukowana z 21 do 9. Dodatkowo warto wspomnieć, że sprawdzanie, czy wartość pola bool jest prawdziwa nie jest konieczne, ponieważ samo użycie tego pola jako warunku już to implikuje.

Deklarowanie zmiennych lokalnych w regułach

Zmienne lokalne pozwalają na bardziej czytelne i zrozumiałe pisanie kodu, ponieważ jest jasne, gdzie dana zmienna jest używana i w jakim kontekście. Błędy są łatwiejsze do zlokalizowania i naprawienia, ponieważ zmienne lokalne są używane tylko w małych, określonych zakresach, a ich nazwy są łatwe do odczytania przez człowiek. Co więcej stosowanie zmiennych lokalny pozwala uniknąć powtórzeń tego samego fragmentu kodu w kodzie, co znacznie ułatwia wprowadzanie zmian.

Antyprzykład:

W poniższym przykładzie, poza nieprecyzyjną nazwą pętli i reguły, warunek w regule jest trudny do odczytania i zrozumienia, w kodzie pojawiają się elementy powtarzalne, które można wydzielić jako zmienne lokalne.

Przykład:

Poniżej, poza oczyszczeniem nazewnictwa i usunięciem zakomentowanych linijek, zadeklarowałem dwie zmienne lokalne: jedna przechowuje datę zobowiązania, a druga datę dzisiejszą. Dodatkowo zadeklarowałem zmienną lokalną odpowiedzialną za sprawdzenie warunku logicznego, czy system powinien zmienić datę, oraz zmienną lokalną odpowiedzialną za flagę zmiany daty. Dzięki tym zabiegom zrozumienie logiki kodu jest znacznie łatwiejsze. Ponadto, w przypadku potrzeby wprowadzenia zmiany, np. w polu daty zobowiązania, można to zrobić w jednym miejscu w kodzie (zmieniając wartość zmiennej lokalnej), zamiast w trzech miejscach, jak w powyższym antyprzykładzie.

Korzystanie z metody interpolacji i werbalizacji stringów dla zachowania czytelności złożonych łańcuchów znaków

Dzięki interpolacji i werbalizacji zmiany w formatowaniu lub treści stringów są prostsze do wprowadzenia, ponieważ cała struktura tekstu jest bardziej przejrzysta i zrozumiała. Werbalizacja stringów ułatwia dodawanie nowych zmiennych lub modyfikowanie istniejących bez ryzyka naruszenia integralności całego tekstu. Co więcej werbalizacja stringów sprawia, że kod jest bardziej zbliżony do naturalnego języka, co ułatwia jego zrozumienie nawet osobom, które nie są głęboko zaznajomione z technicznymi szczegółami.

Antyprzykład:

W poniższym przykładzie zastosowano konkatynacje stringów oraz znaków specjalnych. Bez dogłębnego wczytania się w konstrukcje tego łańcucha znaków ciężko stwierdzić jaka będzie finalna forma takiego stringa. Co więcej ciężko odnaleźć interesujące nas pole charakterystyczne.

Przykład:

Poniżej wykorzystałem dwie metody stringowe: interpolacje (symbol $), dzięki czemu wartości pól lub zmiennych możemy wpisywać w nawiasach klamrowych oraz werbalizacje (symbol @), dzięki czemu nie musimy martwić się o odpowiednie dodawanie znaków specjalnych, w naszym przykładzie każda nowa linijka będzie miała odzwierciedlenie w docelowym stringu.

Podsumowanie

Stosując wymienione w tym artykule zasady pamiętaj o tym, że kod to żywy organizm, zatem najlepszym sposobem na wprowadzeniu powyższych zasad jest iteracyjna praca z kodem małymi krokami. Osobiście często zmieniam i dostosowuję nazwy lub formatowanie mojego kodu kilkukrotnie, ponieważ często po ponownym przeczytaniu kodu zauważam możliwość kolejnej drobnej refaktoryzacji.

Poniżej w ramach podsumowania wypisałem punkty o których warto pamiętać tworząc nowy lub czytając stary kod:

W kwestii dbania o nazewnictwo:
  • Nazwy akcji i reguł: Powinny jasno opisywać, co robią (używamy czasowników).
  • Nazwy pętli: Wskazują, po czym iterują.
  • Nazwy warunków: Opisują, jaki warunek sprawdzamy.
  • Unikanie powtarzalnych nazw pól charakterystycznych z liczebnikiem: np. LicznikTechniczny12, StringTechniczny3.
  • Iteracyjność w dopasowaniu nazw: Nazwy mogą być zmieniane i poprawiane w miarę postępu pisania programu, aby były bardziej precyzyjne.
W kwestii dbania o formatowanie:
  • Zachowanie odpowiedniej indentacji, spacji i nowych linii.
  • Długie warunki z operatorami logicznymi :Oddzielamy nowymi liniami.
  • Unikanie sprawdzania if (true) {}.
  • Usuwanie pustych „else” i zakomentowanych linijek
  • Deklarowanie zmiennych lokalnych: tam gdzie to możliwe i potrzebne.
  • Korzystanie z metod stringowych: Takich jak interpolacja i werbalizacja.

Dbanie o te aspekty pisania kodu sprawia, że program napisany w Ferrycie staje się bardziej czytelny, zrozumiały i łatwiejszy w utrzymaniu.

4 Comments

Leave a comment