bookmark_borderDRY is dead

Reguła DRY jest obok YAGNI, SOLID, czy KISS jednym z najpopularniejszych akronimów pisanych wielkimi literami, które wypłynęły na sposób w jaki staramy się tworzyć oprogramowanie. Jest prosta, intuicyjna i przyswajana przez programistów na wczesnym etapie edukacji. Tyle, że zrodzona w zupełnie innych okolicznościach, niż te z którym dziś mamy do czynienia stała się martwa.

Jej truchło leży na środku sceny i zatruwa nas swoimi oparami.

Prosty pomysł

Nie jestem historykiem programowania i nie dysponuję pewnością co do genezy reguły DRY, ale wyobrażam sobie, że powstała w czasach programowania proceduralnego. W każdym razie proceduralnością pachnie, wręcz cuchnie.

Prosty to pomysł. Banalnie prosty. Mamy sobie kod. Kod należy organizować w procedury, czyli wycinać bloki kodu, które są używane wielokrotnie. Jeśli w jakimś bloku się powtarzamy, to działamy źle, powinniśmy wydzielić go i przekształcić w procedurę.

Ciągle mało

Od czasu programowania proceduralnego trochę się pozmieniało. Przede wszystkim nastąpiła wielka eksplozja paradygmatu obiektowego. Złożoność oprogramowania wzrosła. Biznes miał już swoje systemy automatyzujące księgowość, sumujące przychody, generujące raporty. Trzeba było pisać przeglądarki internetowe, komunikatory, systemy tradingowe dla giełd i snake’a na 3310. Poza ostatnim – były to coraz większe wyzwania.

Tyle tylko, że reguła DRY pasuje do programowania obiektowego jakby mniej. W zasadzie w ogóle nie pasuje, jak się dobrze zastanowić.

Co się dzieje, gdy próbujemy w obiektowym kodzie unikać powtarzania się? Pierwsze co przychodzi do głowy to dziedziczenie: piękny akademicki koncept, który załamał się pod ciężarem rzeczywistości. Pies się wabi, kot ma imię, zróbmy klasę zwierzę. Zrobiliśmy. Szybko okazuje się, że pies się wabi, ale jednak imiona ma inne niż kot, że wabi się, ale kot jakoś gorzej reaguje na własne imię, że zwierzę ma imię, ale nie do końca, bo tylko zwierzę domowe. Robimy klasę zwierzę domowe i dzikie. O cholera – przecież prawie nikt nie nazywa swoich rybek.

Drugie popularne rozwiązanie to żartobliwa oznaka senior developera. Co robi senior developer, gdy przychodzi do chaotycznego projektu? Tworzy folder utils.

Klasy gromadzące wspólne, niepasujące nigdzie indziej kawałki kodu znajdują się obecnie w większości projektów. W niektórych z nich widziałem nawet rozrosłe do 8-16 tysięcy linijek „commony”. Tych polipów da się uniknąć i tak naprawdę powinny zostać rozmasowane z głową na różne fragmenty projektu, ale ich głównym źródłem jest próba stosowania reguły DRY – wydzielenia gdzieś, gdziekolwiek, bo tak najłatwiej, kodu, który się powtarza.

Mikroserwisy – gwóźdź do trumny

Kiedy zapytałem kolegi, który odszedł z Amazona – firmy, która traktowana jest jako modelowa i pionierska, jeśli chodzi o stosowanie architektury mikroserwisów – jak organizują części wspólne, skąd wiedzą, że inny zespół nie napisał już czegoś podobnego, odparł:

 Nie szukamy. Piszemy swoje rozwiązanie. Przy tej skali jest to tańsze i szybsze.

Skala, ogrom systemów, z jakimi przychodzi nam się dzisiaj mierzyć sprawia, że konieczne staje się dzielenie ich na mniejsze. W zasadzie od zawsze było to jedną z podstaw programowania, ale w architekturze mikroserwisów oraz komponentowym podejściu do aplikacji webowych (Angular, React) trend ten uwyraźnia się i przybiera na sile.

I w gruncie rzeczy jest to dużo bardziej obiektowe oraz zgodne z naturą, niż dziedziczenie. Organizmy są do siebie podobne, ale ciężko powiedzieć, żeby były takie same. Oko człowieka nie jest tym samym okiem, co oko psa, czy sokoła. Tylko z pozoru i powierzchownie można zakładać, że jest możliwe współdzielenie. Nie jestem ekspertem z dziedziny genetyki, ale nie wydaje mi się, że gdyby wyciąć z człowieka fragmenty DNA, których nie współdzieli z małpą stałby się automatycznie orangutanem. Silne jest we mnie podejrzenie, że choć wiele w tym podobieństw, czy identycznych fragmentów kodu, to jednak subtelności, kilka „linijek” różnicy sprawia, że dziedziczenie trzeba wziąć w duży, znaczący cudzysłów.

Jak żyć?

Wygląda na to, że reguła DRY dobrymi intencjami wybrukowała nam piekiełko. Czy czas ją odrzucić? Być może. Na pewno warto ją jednak przemyśleć. Warto pamiętać, że w wielu scenariuszach nie powinna być dla nas rekomendacją, że nie jest uniwersalnym narzędziem. Powtarzalność kodu może sygnalizować problemy, a równie dobrze może być najlepszym możliwym rozwiązaniem.

Czy dwukrotne powtórzenie identycznego kodu jest złe? Jeśli w tej samej klasie, pewnie jest. Jeśli w tym samym module, być może. A jeśli w projekcie składającym się ze 100 000 linii kodu, w odległych od siebie modułach? Chyba nie.

Czy częściowe powtórzenie kodu jest złe? Cóż, może nie jest złe arbitralnie, a zależy to od tego, czy da się zbudować odpowiednią abstrakcję, która pozwoli tego uniknąć? Często stosujemy pewne zasady ortodoksyjnie. Dobrze jest jednak zostać centrystą. Wybrać złoty środek. Reguły w programowaniu to zwykle zaledwie sugestie. Nie kierujmy się nimi ślepo.

bookmark_borderKomunikacja asynchroniczna

Kiedy myślimy o zespole programistów, a konkretnie o jego wydajności – przychodzi nam na myśl zapewne jego doświadczenie, motywacja, czy uzdolnienia. Tymczasem sposób w jaki porozumiewają się członkowie zespołu może zaoszczędzić lub roztrwonić znaczące ilości czasu.

Komunikację w zespole można rozróżnić na synchroniczną i asynchroniczną. Pierwsza oznacza, że nadawca i odbiorca wiadomości muszą w tym samym czasie skupić swoją uwagę. Drugi rodzaj pozwala na odpowiedź po pewnym czasie.

Dominujący rodzaj komunikacji w zespole deweloperskim ma fundamentalne znaczenie dla jego wydajności.

Jeśli zespół komunikuje się synchronicznie, to każda rzecz wymagająca wyjaśnienia rozprasza programistów. Oderwanie dewelopera od kodu, choćby na pięć minut, rujnuje jego skupienie i obniża produktywność.

Przykłady komunikacji synchronicznej:

  • programista czegoś nie wie i potrzebuje zadać pytanie, wstaje, idzie do biurka kolegi i pyta
  • product owner odpowiada na pytania podczas regularnych spotkań, gdzie omawiane są po kolei wątpliwości różnych osób
  • tester nie wie jak przetestować zadanie, podchodzi do programisty i pyta
  • programista potrzebuje pomocy w debugowaniu, podchodzi do pierwszego z brzegu kolegi i prosi o pomoc

Marnujemy w ten sposób dużo czasu i energii. Z pozoru jest łatwiej – nie musimy czekać na pomoc, możemy omówić coś na żywo, wydawać by się mogło bardziej dogłębnie, a jednak… podczas spotkań głównie czekamy na swoją kolej, w trakcie pracy przy biurku współpracownicy przerywają nam flow.

Komunikacja asynchroniczna dla sporej ilości osób jest nieintuicyjna, ma jednak ma wiele zalet:

  • pozwala osiągać długie okresy skupienia
  • pozostawia pisemny ślad, do którego można się odwołać, który można przeszukiwać po jakimś czasie
  • pozwala udzielać bardziej przemyślanych, szczegółowych odpowiedzi na pytania

O ile dla pracowników „z biznesu” asynchroniczność może wydawać się dziwna, o tyle dla programistów powinna być oczywistością. Każdy przecież wie, że ponowne załadowanie kontekstu zajmuje czas, że czekanie na odpowiedź serwera blokuje wykonanie programu. Mimo to – w realnym życiu często zdarza nam się o tych zasadach zapominać.

Przykłady komunikacji asynchronicznej:

  • product owner odpowiada na pytania mailowo, nie natychmiast, ale sprawnie
  • tester zamiast pokazywać programistom problemy opisuje je słownie albo nagrywa filmy pokazujące błędy
  • programista mający pytanie zadaje je na slacku lub mailowo, a osoby z zespołu w momencie, gdy nie potrzebują skupienia odpowiadają mu

Najbardziej efektywne zespoły, w jakich miałem przyjemność pracować komunikowały się w dużym stopniu asynchronicznie. Analogicznie – najbardziej niewydajne, omawiały godzinami na spotkaniach w dużych grupach kwestie, które mogły zostać omówione w e-mailu.

Warto pochylić się nad sposobem w jaki organizujemy sobie pracę i dostrzec wzorce, a następnie zastanowić się, czy nie sabotujemy własnej produktywności złymi praktykami.