Nie wstydzę się prostej architektury
Patrząc na dyskusje w internecie, dyskusje w pracy i przeglądając oferty i prezentacje firm robiących coś w zakresie IT, mam wrażenie, że im bardziej złożona struktura projektu tym lepiej. Może bardziej profesjonalnie? Jakby wszyscy dążyli do użycia w każdym kodzie całej wiedzy jaką gromadzili przez lata. Niezależnie od tego jak niewielki jest sam projekt.
CV Driven Development
W pewien sposób rozumiem takie działanie ze strony programistów. W końcu chcemy budować swoje doświadczenie i kierować karierą w taki sposób, żeby łapać się coraz ciekawszych zadań. Rozumiem też, że firmy, żeby przyciągnąć potencjalnych kandydatów do pracy muszą mamić ich „fajnymi” projektami.
Ta pogoń za pracą w złożonych i przez to potencjalnie „fajniejszych” projektach trwa. Poza tym każdy z nas chce mieć poczucie, że to czego się nauczył jest przydatne. Nie po to się uczymy miliona technologii, żeby potem robić coś prostego, prawda?
I w ten sposób programiści są gotowi na proponowanie rozwiązań, które przede wszystkim zapewnią im samym spełnienie oczekiwań pod względem złożoności i fajności projektu, a niekoniecznie przynoszą wartość projektowi. Nierzadko proponowane są rozwiązania, które po prostu dobrze będą wyglądały w CV. Stąd właśnie ta nazwa „CV Driven Development” czyli rozwój projektu kierowany chęcią dodania dobrych wpisów do swojego CV.
Niech Wam będzie
Ale to, że programiści tak robią to jedno. Z drugiej strony są firmy, które na to pozwalają.
Pomijam tutaj sytuację kiedy firma jest nieświadoma takiego procederu. Ale zwykle oznacza to wtedy, że dział programistów tam jest tylko „dodatkiem”, albo, że zamawia coś u zewnętrznego dostawcy. Tylko, że wtedy ten dział IT staje się takim dostawcą, w pewnym sensie zewnętrzną firmą.
Mogę więc powiedzieć, że firmy bezpośrednio mające pod skrzydłami programistów (mamy w głowie, że dział IT w firmie nie-IT traktujemy tutaj jako tą osobną firmę) pozwalają na tego typu zagrywki, gdzie liczy się wrzucanie maksimum rozwiązań do kodu.
I tutaj mogę zgadywać, że chodzi właśnie o fakt, że w ten sposób taka firma daje po prostu programistom „lizaka”, który uspokoi ich zapędy w szukaniu nowej pracy, gdzie kod będzie bardziej fancy.
Tylko czy to ma zawsze sens?
Na początku było proste
Jeżeli śledzicie moje poczytania na Youtubie to pewnie trafiliście też na streamy, gdzie piszę kod jakiejś aplikacji. Zwykle są to projekty, które sobie wymyśliłem jako coś, co ma szansę być przydatne. I jeżeli śledzicie te poczynania trochę dłużej, to być może zauważyliście, że wszystkie moje projekty, kiedy robię np. MVP czy nawet PoC, są do bólu proste (pomijam tutaj ich jakość, zwykle to jest PoC i porządek wychodzi z czasem).
Jeżeli nie nastawiam się, że dany kod ma służyć pokazaniu jakiegoś konkretnego rozwiązania, które można zastosować w kodzie, to jadę po linii najmniejszego oporu. Nie silę się na wprowadzanie od początku „fajnych” rozwiązań jak mikroserwisy (ooooo, taaak! ale by to było trendy, taka strata!), eventy, CQRS, wielowarstwowa architektura czy kilka baz danych.
A mógłbym przecież natrzaskać tyle świeżej treści, gdzie pokazuję jak to wszystko upchnąć w jeden projekt! Sądzę, że wyświetlenia moich materiałów robiłyby wtedy wyniki przynajmniej 2-3x lepsze.
Tylko, że projekty, które robię są proste. Ich założenia są proste. Zwykle mają jakąś jedną ideę, którą implementuję. Dlaczego więc miałby dostać super złożoną architekturę?
Czas, czas!
Jeżeli wymyślam projekt, który w założeniu rozwiązuje jakiś problem, to zależy mi, żeby ten problem rozwiązał jak najszybciej. A co daje nam szansę na szybkie dostarczenie? Prostota!
Mając do zbudowania serwis, który ma za zadanie odebrać od użytkownika dane, sprawdzić czy są poprawne, utworzyć kilka wpisów w bazie i zwrócić odpowiedź, spróbuj zrobić to jako prosty, do bólu standardowy kod, a potem wrzuć to w fajną architekturę, korzystającą z wielu popularnych buzzwordów. Potem powiedz mi, w którym rozwiązaniu szybciej miałeś poczucie, że ten projekt ma szansę być ukończony.
Budowanie złożonych struktur trwa. Zabiera czas, w którym nie dostarczamy niczego, co można przetestować. Im głębiej wchodzimy w rozważania na temat tego, na ile mikroserwisów podzielimy ten projekt, albo który system kolejkowy obsłuży 100tys. użytkowników „bo może kiedyś tylu będzie„, tym bardziej oddalamy się od możliwości sprawdzenia jak to wszystko w ogóle działa.
Im później sprawdzimy czy same założenia projektu, pod względem tego jak działa, jak wygląda, czy opcje jakie ma są w porządku, tym większa szansa, że taki projekt stanie się bezwartościowy w momencie ukończenia. Jest to całkowicie wbrew metodologii agile, która równocześnie jest powtarzana w każdej sekundzie i odmieniana przez wszystkie przypadki.
Tylko dlaczego nasze projekty nie są faktycznie agile?
Wąskie gardło, gdzie?
Jeżeli zapytać się takich osób, które od razu rzucają pełny arsenał do każdego projektu, dlaczego to robią, to niejednokrotnie odpowiedź będzie mniej lub bardziej związana ze słowem „skalowalność„. Bo jak damy mikroserwisy, kolejki, CQRS (koniecznie z zeparacją baz danych, a najlepiej też całych serwisów) to aplikacja będzie skalowalna! Obsłuży dzięki temu od jednego do miliona użytkowników!
Tylko czy na pewno? Skąd mamy pewność, że faktycznie będzie taki ruch? I czy w ogóle jest możliwe, żeby taki ruch był?
O czym mówię? Mówię o sytuacji, gdzie ciężki oręż jest wrzucany w projekt, który jest projektem wewnętrznym. W firmie, która ma np. 500 pracowników. Zastanów się teraz – jaka może być w takim razie maksymalna liczba użytkowników takiego systemu? Wystarczy, że podasz rząd wielkości.
W takim razie, w którym momencie ta niesamowita skalowalność przyniesie Wam korzyść?
Zaryzykuję stwierdzenie, że w tym przypadku chcąc rozwiązań problem wyimaginowanego wąskiego gardła, związanego z obsługą kosmicznej liczby zapytań, wprowadzacie jak najbardziej realne wąskie gardło – w postaci rozdmuchanych rozwiązań.
Bo jeżeli zwykły kod, oparty o jeden serwis, z płaską architekturą, postawiony na jednej maszynie, jest w stanie obsłużyć ruch przekraczający ilość wszystkich pracowników, to dokładanie kolejnego serwisu, komunikacji z nim poprzez HTTP, systemu kolejkowego, który dystrybuuje zdarzenia, wielu baz danych, które muszą się synchronizować, powoduje, że zwiększamy narzut czasowy i potrzebną moc w miejscach, które tego nie potrzebowały. I to one zaczynają być wąskim gardłem!
„Ale w przyszłości…!”
Jeżeli do tej pory jeszcze nie wykrzyczałeś argumentu ostatecznego to teraz na pewno to robisz. Tym argumentem jest powtarzane i w pewien sposób już wspomniane przeze mnie stwierdzenie, że ok, może teraz jest mało użytkowników, ale co jeżeli w przyszłości okaże się, że będzie ich dużo więcej?!
Świetnie, że myślisz przyszłościowo. Chcesz żeby Twoja aplikacja była kuloodporna. Tylko, że pomijasz tutaj dwie istotne sprawy.
Mało kto słyszał
Pierwsza z nich jest taka, że jednak większość aplikacji, zwłaszcza pisanych w dużych firmach, to aplikacje mniej lub bardziej wewnętrzne. W dodatku nawet nie używane przez całą firmę. Więc w żadnym momencie swojego życia nie będą obsługiwać ruchu zbliżonego do ruchu na Twitterze czy Facebooku. Ilość danych też mało kiedy będzie liczona w więcej niż gigabajtach.
Ale nawet jeżeli ostatecznie okaże się, że faktycznie ruch się zwiększył i pewne fragmenty aplikacji stały się mocniej używane to…
Co mówi szklana kula?
…który z fragmentów zyska w przyszłości popularność? Na jakiej podstawie zakładasz, że właśnie ten element, na który poświęciliście tyle czasu, żeby był turbo-skalowalny, będzie tym, który stanie się najpopularniejszy za kilka lat?
Można przewidywać przyszłość bazując na pewnych prognozach, ocenach, badaniach. Ale nadal jest to wielka niewiadoma co przyniosą użytkownicy.
I co w momencie jak okaże się, że idealny serwis do wydzielenia i skalowania to będzie ten, który ma połowę jednego i połowę drugiego serwisu?
Jasne, że upraszczam.
Oczywiste jest, że to co tutaj napisałem nie przekłada się na wszystkie projekty. Nie jest też tak, że każdy pracuje przy CRUDach. Ale rzeczywistość programistyczna jest taka, że właśnie takie nudne i proste projekty regularnie przewijają się przez nasze palce. I moim zdaniem powinno się do nich podejść z rozsądkiem i pokorą, a nie rzucać się z setką technologii i architektonicznych zabawek jak lew, który rzuca się z zębami na swoją ofiarę.
Ta cała dyskusja nie dotyczy projektów, które mają złożoną domenę albo są nowym wcieleniem czegoś co już istnieje i zebrało duży ruch. Jeżeli pracujesz przy takim projekcie to świetnie! Masz szansę stosować swoje najlepsze rozwiązania w boju!
Dodatkowo swoje słowa kieruję głównie do osób, które w jakiś sposób decydują o kształcie projektu. Niezależnie od tego czy to będą liderzy zespołu, czy architekci, czy też „zwykli” programiści, którzy mają szczęście być pytani o zdanie.
Podsumowując
Warto już zmierzać do końca tego wywodu. Więc w tym miejscu powiem kilka stwierdzeń, które wg mnie będą lepszą opcją niż przyjeżdżanie z ciężarówką sprzętu i materiałów do budowy karmnika dla ptaków.
Dajmy jej dojrzeć godnie
Zaczynając w zespole nowy projekt zastanówmy się przez chwilę, czy faktycznie jest to coś, co aspiruje do bycia kobyłą z milionem funkcji i równie dużą liczbą użytkowników i danych.
Jeżeli nie mamy pewności, w którą stronę pójdzie projekt, to stosujmy to agailowe podejście. Zróbmy najprostsze rozwiązanie, a kiedy sprawy zaczną rosnąć to pozwólmy tej prostocie ewoluować. Rozwijajmy ją naturalnie wraz z poznawaniem kolejnych faktów, np. na temat objętości danych albo wykorzystania konkretnych elementów.
Jeżeli pozwolimy aplikacji rosnąć wraz ze wzrostem użytkowników albo danych to będziemy mieć sytuację gdzie przyszli programiści (bądźmy szczerzy, ilu z nas będzie w tym projekcie za 5 lat?) będą mogli rozbudowywać prosty, szybki w zrozumieniu kod i dopasowywać go do faktów jakie mają, zamiast poświęcać setki godzin na walczenie z przekombinowaną architekturą, która co prawda zapewniała mityczną skalowalność, ale nikt tego nigdy nie użył.
Najpierw mierzmy
Kontynuując trochę poprzedni punkt, jeśli chcemy być pewni, że panujemy nad sytuacją i jesteśmy gotowi na świadome utrzymanie naszego projektu, to zamiast od razu pakować się w skomplikowaną architekturę – mierzmy. Dodajmy kilka metryk w kluczowych miejscach, np. takich, gdzie mamy przeczucie, że może się coś dziać. Dodajmy metrykę na liczbę użytkowników, na objętość kluczowych tabel, na czas odpowiedzi serwera albo zużycie zasobów.
W ten sposób, jeżeli już będzie potrzeba dodać gdzieś trochę „fajniejszej” architektury to będziemy wiedzieć gdzie dokładnie to zrobić. Bo powiedzą nam o tym metryki.
Schowajmy przesadne ego
Mam wrażenie, że niektórym z nas to ambicja nie pozwala zrobić zbyt prostego projektu. Czujemy, że prosty, płaski kod nie jest godny naszej osoby. I za wszelką cenę chcemy pokazać, że to co tworzymy to DZIEŁO SZTUKI. Tylko, że nawet artysta, chcąc pomalować ścianę na biało, nie będzie zamiast tego malować obrazu powtarzając, że „jemu nie wypada malować na jeden kolor„.
Dopasujmy się do realnych potrzeb. I na pewno w komentarzach pojawi się stwierdzenie, że jak zrobi się coś za prosto, to albo szef będzie podejrzliwy, albo dowalą nam więcej roboty jak zrobimy za szybko. I w tym miejscu, przez wzgląd na naszą branżę, nie mam oporu zapytać – w takim razie czemu męczysz się w firmie, która nie rozumie, że może korzystać na tym, że coś jest zrobione prosto i woli rzucać pieniądze na rozwiązania, które piętrzą problemy?
Ale róbmy to porządnie!
Na koniec ostatnia sprawa – prostota nie równa się bylejakość! Mówiąc o tym, żeby robić projekt prosto, unikać złożonej architektury, nie mam na myśli, żeby iść w kolejną skrajność i pisać kod na pałę, wszystko w jednej klasie, jedna metoda robiąca wszystko. Zasady dobrego kodu nadal nas obowiązują!
Schludny kod może być prosty. Nawet często powinien być prosty. Po prostu robiąc prosty serwis dodajmy jedna warstwę wyciągającą dane, zamiast pchać tam serwis, kolejkę, drugi serwis i bazę danych ciągnącą dane z drugiej bazy danych. Ale ta jedna warstwa niech będzie utrzymana w porządku. W końcu masz mniej kodu, więc łatwiej jest Ci się postarać i zrobić porządnie w skończonym czasie!
Nie sposób się nie zgodzić. Warto też spojrzeć jak powstawały bardzo popularne usługi – właśnie metodą jak najszybszego dostarczenia użytecznych rzeczy, a nie pięknego kodu. Nie chcę nawet myśleć ile razy robiono refactor tego czy owego na Facebooku lub Twitterze. Tylko co z tego, że to nie było dzieło sztuki? Wystarczyło, że działały
Zgadzam się z autorem. Nowi programiści, studenci poszukując najnowocześniejszych technologii, często rezygnując tym samym z opcji zdobycia cennego produkcyjnego doświadczenia w większych, być może starszych systemach, pisanych już jakiś czas temu. Tracą możliwość analizy i rozwiązywania rzeczywistych złożonych problemów. A szkoda. Porównywanie pisanych systemów do FB i Twittera dla ograniczonej liczby użytkowników jest jak najbardziej niezasadne. Natomiast zawsze z tyły głowy mam historię Nasze-klasy. Polskiego FB. Ten przypadek (choć nie wchodziłem w szczegóły) mam wrażenie pokazuje że w pewnym momencie, jak przyjdzie pik nie będzie możliwości szybkiego zareagowania, przez co problemy (słynny pan gąbka, później obrócony w walutę) spowodowały odejście pewnej grupy osób. Jeszcze jeden przypadek może już nie czysto architektoniczny, ale nasz rodzimy GG. Na początku wszyscy z niego korzystali, prosty, intuicyjny, lekki. Dodawanie radia, gier, nie wiadomo czego jeszcze spowodowało w mojej skromnej opinii przesyt i rezygnacje z tego komunikatora. A szkoda.