Na stażu w Comarchu poznam… SOLID #4

Zazwyczaj rozpoczynając swoja karierę programistyczną, mało uwagi przywiązuje się do jakości tworzonego kodu. Sam zresztą jestem zwolennikiem nauki „na błędach” i wychodzę z założenia, że lepiej zrobić coś „brzydko”, a potem to poprawiać, niż przez tydzień zastanawiać się i nie zrobić nic. W praktyce wygląda to tak, że jeśli pracujemy w zespole nad jakimś projektem to zazwyczaj, zanim nasz kod zostanie „zmegowany” do gałęzi produkcyjnej, musi przejść tak zwane „review”. W różnych firmach różnie się do tego podchodzi. Ja osobiście spotkałem się między innymi z wymogiem zaliczenia wszystkich testów jednostkowych, napisaną pełną dokumentacją (również przeglądniętą przez inną osobę) oraz pozytywną akceptacją napisanego kodu przez innego developera. Oczywiście, jeżeli „review” zakończy się oceną negatywną to kod z powrotem wraca do programisty, gdzie musi zostać poprawiony i poddany ponownej ocenie.

Fot: Pxhere, CC0.

Ucząc się programowania i pracując nad swoim być może pierwszym dużym projektem, warto zwrócić uwagę na kilka dość elementarnych zasad, które zostały zaproponowane przez słynnego na świecie, amerykańskiego programistę, autora bestsellera „Czysty kod” – Roberta C. Martina.

Wujek Bob, czyści kod!

Kto nie słyszał o wujku Bobie? Mam nadzieję, że nikt… ale jeśli jeszcze znalazły się takie osoby to koniecznie odsyłam ich do lektury „Czystego kodu,” bądź też znanej doskonale wszystkim programistom wyszukiwarki google.

Trochę odchodząc od tematu. Na początku artykułu wspomniałem, że jestem zwolennikiem nauki „na błędach” i jak się okazuje, że podobne zdanie ma również sam wujek Bob:

Programowanie jest bardziej rzemiosłem niż sztuką. Aby pisać czysty kod, musimy na początku napisać brudny kod, a następnie go oczyścić. – „Wujek Bob”, Robert C. Martin.

SOLID – co to takiego?

Wracając do tematu artykułu, chciałem dzisiaj przedstawić wam pięć podstawowych założeń programowania obiektowego potocznie zwanymi „SOLID”, bez zbędnej zwłoki oto one:

  1. Zasada jednej odpowiedzialności (ang. Single Responsibility) – Klasa powinna mieć tylko jedną odpowiedzialność (nigdy nie powinien istnieć więcej niż jeden powód do modyfikacji klasy).
  2. Zasada otwarte-zamknięte (ang. Open-close) – Klasy (encje) powinny być otwarte na rozszerzenia i zamknięte na modyfikacje.
  3. Zasada postawienia Liskov (ang. Liskov substitution principle) – Funkcje, które używają wskaźników lub referencji do klas bazowych, muszą być w stanie używać również obiektów klas dziedziczących po klasach bazowych, bez dokładnej znajomości tych obiektów.
  4. Zasada segregacji interfejsów (ang. Interface segregation principle) – Wiele dedykowanych interfejsów jest lepsze niż jeden ogólny.
  5. Zasada odwrócenia zależności (ang. Dependency inversion principle) – Wysokopoziomowe moduły nie powinny zależeć od modułów niskopoziomowych – zależności między nimi powinny wynikać z abstrakcji.

Pewnie większość z was spotkała się z tymi założeniami po praz pierwszy lub wcześniej jakoś nie przywiązywała do nich wagi. Należy jednak mieć na uwadze, że co prawda przy jakiś małych projektach pisanych na przykład na zaliczenie danego kursu podczas studiów mogą one nie mieć takiego znaczenia, ale już przy nieco ambitniejszych aplikacjach będą odgrywały kolosalną różnicę.

Część z was, pracując nad swoimi projektami, pewnie je nawet stosowała całkiem nieświadomie. Podobnie bywa z wzorcami projektowymi, które też stosujemy czasami nieświadomie. Ręka do góry, kto tworzył instancję klasy z danymi konfiguracyjnymi i nie wiedział, że to singleton? Aby uniknąć takich przypadków, warto usystematyzować swoją wiedzę, bo to właśnie tym można się wyróżnić podczas rekrutacji na staż czy to w Comarchu czy też innej firmie.

Jak to wszystko ma się do prawdziwej pracy?

Teoria teorią, ale najciekawsza jest praktyka. Postanowiłem więc zasięgnąć opinii Pana Marcina Czajczyka, Team Leadera w Comarch i podpytałem go o kilka ciekawych zagadnień. Zapraszam do lektury naszej krótkiej rozmowy.

Łukasz Dudziński, StrefaKodera.pl: Jak w Comarch podchodzi się do review kodu? Czy są zdefiniowene jakieś konkretne procedury?

Marcin Czajczyk, Comarch: Staramy się wykonywać review kodu przy okazji każdego projektu. Zdecydowanie robimy to przy kodzie napisanym przez młodszych programistów, gdzie review ma za zadanie sprawdzić poprawność implementacji, jak i zrozumienia przez programistę specyfikacji Po drugie jest dobrym sposobem na weryfikacje czytelności kodu, gdyż wszak najlepiej uczyć się na swoim kodzie. W przypadku bardziej zaawansowanych programistów review jest wykonywany zawsze dla krytycznych funkcjonalności. Z tymi mniej krytycznymi wszystko zależy od ilości czasu (jak blisko jest termin dostarczenia) oraz znajomości siebie nawzajem w zespole. Podczas implementacji podobnych funkcjonalności w kolejnych projektach zespół generalnie zna zasady, przez co review raz jest szybsze, a dwa w przypadku braku czasu można pozwolić je pominąć lub też odłożyć na później. W większości wypadków review jest wykonywany przez jedną osobę w postaci lidera zespołu, który odpowiada za jakość i terminowość dostarczenia kodu, nie mniej przy bardziej zaawansowanych przypadkach review przeprowadzone jest w szerszym gronie.

Jak wygląda proces akceptacji dodania nowej funkcjonalności do projektu, czyli mówiąc językiem programistycznym merge brancha developerskiego na produkcje?

Tutaj wydzieliłbym dwie sytuacje:

  1. praca nad nowym wdrożeniem (bez wersji produkcyjnej),
  2. praca nad wdrożoną wersją.

W pierwszym przypadku generalnie pracujemy na tej samej wersji bez branchy. Pozwala to na szybszą pracę. Część zadań można prowadzić równolegle, podzielić pomiędzy kilku programistów. W takiej sytuacji review jest tylko punktem do sprawdzenia przed wysłaniem frunkcjonalności do testowania. Taka praca zapewnia też mniejsze problemy z utrzymaniem spójności w przypadku baz danych.

W drugim przypadku praca głównie odbywa się na branchach, przez co podczas przygotowywania wersji do wdrożenia na produkcje robi się się merge. W takiej sytuacji przeprowadza się review podczas merge i sprawdza czy implementacja nie gryzie się z innymi, już zmergowanymi branchami. Po samym mergu wykonywane są testy potwierdzające poprawność działania zarówno samej zmiany, jak i całej aplikacji.

Widzę, że zawędrowaliśmy trochę do tematów testów, możesz coś więcej o tym powiedzieć?

Używamy w tym momencie Jenkisna jako narzędzia Continuous Integration. Jest on stosowany na wszystkich środowiskach, w tym także produkcyjnych. Dział testów pracuje nad testami automatycznymi dla całego produktu. Następnie testy te są uzupełnianie o zmiany wykonane pod konkretne wdrożenie. Testy te używane i dostosowywane są podczas dalszego życia danego wdrożenia i przed każdym deployem na produkcję muszą przejść bez błędów.

Inna historia jest z testami w postaci unit testów. Te z kolei zazwyczaj tworzone są przez programistów i umożliwiają szybkie sprawdzenie poprawności działania konkretnego fragmentu kodu w odizolowaniu od zewnętrznych zależności. Z pomocą odpowiednich narzędzi można również zbadać stopień pokrycia testami jednostkowymi kodu naszej aplikacji. . Jak we wszystkim, także w przypadku testów jednostkowych, najlepiej jest utrzymać umiar i nie produkować zbędnego i nikomu niepotrzebnego kodu, który będzie sprawdzał rzeczy na poziomie 2+2. Oczywiście zrezygnowanie całkowicie z nich uważam także za błędne podejście, bo dla krytycznych funkcjonalności czy też bardziej skomplikowanych algorytmów powinny zdecydowanie powstać. Po pierwsze pozwalają na dokładniejsze przetestowanie skomplikowanego fragmentu, a po drugie gwarantują poprawność w przyszłości jak i umożliwiają  lepsze zrozumienie działania aplikacji.

W Comachu zajmujesz się pracą ze stażystami. Jak to wygląda od tej strony?

Osobiście moje grupy stażowe wykonują projekty typu PoC (Proof of concept), które mają na celu sprawdzenie możliwości wykonania pewnych pomocniczych bibliotek, które ułatwiłby pracę na co dzień, a których nie ma w open-sourcowym świecie. Z tego też powodu inne są kwestie akceptacji kodu, który ma głównie działać i pokazywać możliwość realizacji zadania. Z uwagi na dość krótki czas stażu, review kodu ma raczej pokazać sposób myślenia, podejścia do rozwiązania. Dość często widać też czy  dany stażysta wcześniej programował. Można poznać programistę, którego praktyka sprowadza się do projektów zaliczeniowych na studiach, jak i takiego – nazwijmy to bardziej świadomego.

Generalnie stażyści umieją bez większych problemów zrealizować powierzone zadania, co bardzo nas cieszy. Natomiast dość często zapominają o wszelkich negatywnych przypadkach, jakie mogą wystąpić. Ignorują wyjątki, nie tworzą własnych lub tworzą zbyt generalne, nie mówiące zbytnio co tak naprawdę się stało. Jest to bardzo ważny aspekt, którego nie można pominąć, bo później jak coś stanie się (zwłaszcza na produkcji), to traci się bardzo dużo czasu na znalezienie przyczyny problemu.

A czy masz jakieś rady dla młodych adeptów kodowania i nie tylko?  Jak radzić sobie z rozwojem „starego oprogramowania”? Jak zaplanować podział architektoniczny nowego projektu?

Do starego kodu należy podchodzić przede wszystkim ze spokojem i nie próbować na siłę go ulepszyć i poprawić, bo można na takiej poprawie stracić dużo czasu i przy okazji coś popsuć. Rozwijając takiego typu aplikacje, trzeba dobrze przeanalizować zmianę i odpowiednio zaplanować pracę. Czasem jest tak, że człowiek musi poświęcić jeden czy dwa dni na analizę kodu, aby finalnie napisać parę prostych linijek.

Najlepszą architekturą w przypadku samego kodu jest podział na warstwy. Tak jak cały projekt  składa się z warstw – klasycznie: baza danych, aplikacja, prezentacja. Do komunikacji pomiędzy warstwami (baza danych <-> aplikacja <-> prezentacja) powinno się używać dedykowanych obiektów przenoszących informacje (klasy DTO), a poszczególne operacje powinny być pogrupowane i rozdzielone. W ten sposób powstanie warstwa klas DAO opowiadających za wykonanie operacji na bazie danych, warstwa klas procesujących, które będą realizować logikę biznesową, a także warstwa klas fasadowych, przez które z aplikacją komunikują się inne aplikacje prezentujące dane użytkownikom. Wszystkie warstwy powinny się komunikować w oparciu o interfejsy a nie o klasy. Taki podział pozwala dość łatwo zmienić np. bazę danych lub strukturę w bazie danych bez wpływania na logikę biznesową. Umożliwia to też dość łatwą migrację na różnego rodzaju serwery i zmianę np. z aplikacji EJB na aplikację opartą o framework Spring. Dodatkowo poza podziałem horyzontalnym należy dzielić kod wertykalnie, budując odpowiednie moduły. Generalnie takie podejście wpisuje się dość dobrze w pojęcie SOLID zaproponowane przez Roberta Martina. Z tak podzielonym kodem łatwiej jest finalnie zbudować dużą aplikację w postaci monolitu, jak i przejść na architekturę opartą o usługi, albo nawet o mikrousługi.

Czy chciałbyś coś jeszcze dodać od siebie?

Po latach programowania i używania różnych frameworków wiem, że czasami głupie i bardzo proste rozwiązania są najlepsze, a szukanie wykwintnych rozwiązań nie zawsze jest dobrym pomysłem. Warto o tym pamiętać.

Dzięki za wywiad.

Również dziękuję.

Praktyka, praktyka i jeszcze raz praktyka…

Często dostaję różne pytania od czytelników i czytelniczek odnośnie tego, jak uczyć się programowania, jaki robić projekt, gdzie aplikować nie mając doświadczenia? Udzielając odpowiedzi zawsze wspominam o różnych programach stażowych organizowanych podczas wakacji przez wiele firm z branży IT. Jednym chyba z takich najpopularniejszych jest właśnie program stażowy Comarch, na który macie jeszcze ostatnią szansę aplikować, bowiem czas na wysyłanie zgłoszeń do tegorocznej edycji mija już jutro – 25 kwietnia o godz. 16:00. Warto się pospieszyć, a więcej szczegółów na ten temat znajdziecie na stronie kariera.comarch.pl/staze/staz-it.

Powodzenia i nie zapomnijcie o radach z dzisiejszego wpisu!

Artykuł ten powstał przy współpracy z firmą Comarch SA. Jest on czwartym z dziesięciu wpisów jakie ukażą się na blogu w ramach serii „Staż w Comarchu 2018”. Kolejny artykuł zostanie opublikowany za tydzień we wtorek 1.05.18. Już teraz zapraszam do lektury.

Przeczytaj również

, , , , , , , , , , , , , , , , ,