Antywzorce, które prowadzą Twój projekt Node.js (i nie tylko) donikąd
Node.js jest teraz bardzo popularny, jeśli chodzi o tworzenie oprogramowania. Wiele firm, od startupów po duże firmy technologiczne, używa go do budowania swoich usług. Node.js jest bardzo wydajny i daje mnóstwo możliwości dzięki dużej społeczności open source. Ten runtime daje nam swobodę: możesz używać go z JavaScriptem albo TypeScriptem, pisać klasy, funkcje albo mieszać jedno z drugim, korzystać z bardziej opinionated frameworków albo z mniej opinionated, nie jesteś zamknięty w żadnym konkretnym paradygmacie, który definiuje twoje wybory. To świetne, ale jednocześnie jest to niebezpieczne środowisko, które pozwala tworzyć spaghetti code, mogące zepchnąć projekt w pustkę i sprawić, że biznes straci pieniądze. Czasem, kiedy czytam jakieś artykuły albo oglądam filmy, widzę ludzi promujących łatwą drogę, chodzenie na skróty, gadanie o klasach vs funkcjach, o tym, jakiego frameworka zawsze projekt używać, rozwodzenie się nad nowymi funkcjami we frameworkach itd. Kiedy zabierasz się za prawdziwy projekt na dużą skalę, gdzie potrzebujesz solidnych rozwiązań, wyraźnego rozdzielenia odpowiedzialności i dobrze przetestowanego, utrzymywalnego kodu, który da ci pewność, że aplikacja będzie niezawodna i nie narazi biznesu na ryzyko, sytuacja wygląda zupełnie inaczej. Okazuje się, że wymyślne frameworki i biblioteki z najwyższej półki nie zawsze są najlepszym wyborem i sięgasz po bardziej opinionated wzorce, które widziałeś gdzieś indziej. W tym artykule chciałbym powiedzieć więcej o rzeczach, które możesz zepsuć w swoim projekcie Node.js. Część z tych antywzorców dotyczy także innych języków i technologii.
Brak sensownych testów (i dbania o nie)
Myślę, że to jeden z najbardziej krytycznych, jeśli nie najbardziej krytyczny, antywzorzec nie tylko w świecie Node.js, ale w całej branży. Zaniedbywanie testów może wpędzić projekt w poważne problemy, łącznie z problemami prawnymi. Nie tylko unikanie testów, ale też ich słaba jakość, to poważne ryzyko. Brak ustalonych zasad i dobrej strategii testowania to duży antywzorzec. Kiedy projekt operuje na prawdziwych danych użytkowników, musisz mieć wdrożone testy jednostkowe, integracyjne i end-to-end. Kiedy aplikacja ma obsługiwać duży ruch, musisz mieć też testy obciążeniowe; nie ma dla programistów nic bardziej stresującego niż wdrażanie kodu bez wiedzy, jak ten kod zachowa się w scenariuszu wysokiego ruchu. Posiadanie testów to nie tylko kwestia 'test coverage', ale też ich jakości. Ważne jest ustalenie zasad i architektury testowania oraz zdefiniowanie, czym w twoim projekcie są testy jednostkowe, integracyjne i end-to-end. Dla mnie, jeśli chodzi o podejście do testowania, TDD jest złotym standardem wszędzie tam, gdzie potrzebujesz stabilnego, niezawodnego i bezpiecznego kodu; w niektórych branżach, takich jak fintech, ochrona zdrowia, transport publiczny i lotniczy, to podejście jest obowiązkowe. Oczywiście jest wielu ludzi, którzy powiedzą, że to wolne albo że time to market jest ważniejsze, ale w prawdziwym życiu prędzej czy później takie szybkie podejście doprowadzi do strat pieniędzy po stronie klientów, wycieku prywatnych danych albo problemów prawnych i możesz zostać zmuszony do wzięcia odpowiedzialności za to.
Brak code review
To kolejny grzech w dostarczaniu oprogramowania. Development w pośpiechu i sprowadzanie czasu programistów wyłącznie do produkowania kodu to złe podejście. Budowanie zespołu, w którym ludzie nie wiedzą, co jest faktycznie dostarczane poza ich własnymi podwórkiem, jest bardzo niebezpieczne. Jeśli czujesz się winny dlatego że spędzasz czas na przekopywaniu bazy kodu, żeby zdobyć choć odrobinę wiedzy o kodzie, to znaczy, że twoje środowisko jest zepsute. Jeśli działamy w strukturze zwanej zespołem, to MY dostarczamy kod, a nie on, ona czy ja; zadanie nie kończy się w chwili, gdy kod 'działa', ale wtedy, gdy wszyscy, którzy pracują z tym kodem, zgadzają się na zmiany. To daje każdemu jasny obraz całości, a nie tylko jej fragmentu.
Brak dobrze ustalonych wzorców w kodzie i trzymania się ich
Jak zrobić solidny code review w projekcie, w którym nie ma żadnych standardów stylistycznych kodu? Jeden programista lubi function declaration, inny woli function expression, każdy ma swoje własne preferencje, jeśli chodzi o kod; kiedy tworzysz własny solo projekt, prawdopodobnie dbasz o spójność, znasz swój styl i się go trzymasz. To samo dotyczy prawdziwego kodu produkcyjnego: spójność jest obowiązkowa i trzeba ustalić zasady, żeby kod taki był. Razem z zespołem ustalcie reguły, które pozwolą dostarczać kod najlepszej jakości jako efekt doświadczeń i preferencji całego zespołu. Żeby łatwo stosować to w praktyce, używajcie linterów i commit hook'ów; to nie tylko poprawi jakość kodu, ale też pozwoli programistom skupić się w code review na rzeczach, które naprawdę mają znaczenie, zamiast w kółko poprawiać te same niespójności.
Brak komunikacji między programistami
Praca asynchroniczna to świetne doświadczenie, pozwalające programistom skupić się na trudnych problemach i wejść w tryb deep work, ale kiedy nie ma nawyku synchronicznych spotkań, podczas których zespół może omawiać problemy i uczyć się od siebie nawzajem, zaczyna on przypominać grupę niezależnych freelancerów pracujących nad tym samym projektem, rozwiązujących napotkane problemy w pojedynkę i podchodzących do nich według własnego widzimisię. Nie ma wtedy solidnego code review, nie ma dobrej jakości kodu ani niezawodności, kiedy zespół istnieje tylko na papierze. Sesje pair programmingu są też potrzebne, żeby odblokowywać osoby, które utknęły, i realnie pracować nad rzeczami razem; pamiętaj, kiedy pracujecie wspólnie, macie co najmniej dwie głowy, więc jest bezpieczniej, szybciej i lepiej. Skłaniam się ku podejściu, w którym zespół powinien mieć wspólną wiedzę, wspólny mózg, jeśli chodzi o kod nad którym pracuje.
Słaba architektura aplikacji (lub jej brak)
Nie potrzebujesz architekta, żeby planować architekturę, wystarczy, że nauczysz się patrzeć szerzej, nie tylko na używane frameworki, biblioteki itd. Czasami w projekcie nie ma żadnej architektury i niestety często wynika to z braku komunikacji w zespole oraz ze sprowadzania czasu programistów wyłącznie do dowożenia kolejnych linijek kodu. Wiem, że są pivoty, zmieniające się wymagania i cała reszta; kod jest dla biznesu, ma rozwiązywać problemy, to prawda, ale jednocześnie jest kręgosłupem realnego produktu dlatego nie możemy zaniedbywać architektury. Im słabsza architektura, tym więcej długu technicznego produkuje zespół i tym szybciej on rośnie. Nikt nie wie, gdzie wstawić nową funkcję albo klasę ani gdzie powinna leżeć logika biznesowa, bo jest porozrzucana po wszystkich warstwach aplikacji. Projektowanie architektury nie polega na tworzeniu abstrakcji ani irytujących zasad, których trzeba się nauczyć, ale na przyspieszaniu całego procesu dostarczania projektu, od developmentu po utrzymanie. Node.js daje wolność: możesz zrobić, co tylko chcesz. To tylko runtime, który dostarcza pewne funkcje i pozwala uruchamiać JavaScript, ale jeśli pozwolisz tworzyć rzeczy bez żadnej kontroli, mogę cię zapewnić, że od samego początku będą nieutrzymywalne.
Bezrefleksyjne akceptowanie generowanego/kopiowanego kodu
Sięganie po Stack Overflow lub obecnie AI, to zawsze była ta sama historia. Musisz szybko coś zescaffoldować albo po prostu czegoś nie wiesz, więc sięgasz po AI i niemal natychmiast masz odpowiedź. Czasami ilość kodu wyprodukowanego przez AI jest duża i naprawdę trudno się skupić podczas review zmian, więc wypychasz kod, który wydaje ci się zrozumiany, ale to tylko iluzja. To samo dotyczy kodu kopiowanego i wklejanego z innej części codebase'u albo ze Stack Overflow czy z jakiegokolwiek innego źródła, nadal kluczowe jest pełne zrozumienie kodu, który tworzysz Ty i Twój zespół. Żeby robić to dobrze, ucz swój zespół, żeby każdy upewniał się, że rozumie dostarczany kod, prosił o review i wyjaśnienie, kiedy nie wszystko jest jasne.
Złe używanie TypeScript`a
W dzisiejszych czasach TypeScript jest obowiązkowy, kiedy budujesz prawdziwy produkt w skali z zespołem programistów. TypeScript wcześnie wyłapuje błędy, dobrze dokumentuje kod, ma świetne wsparcie LSP, dzięki któremu edytor tekstowy może bardzo dobrze pokazywać komunikaty o typach i podpowiedzi typów, a odkąd został przepisany w Go, jest bardzo szybki w codziennym developmentcie, ale... TypeScript może strasznie namieszać w twoim kodzie i zaśmiecić logikę, jeśli używasz go źle. Jeśli wymagania aplikacji się zmieniają, a model danych nie jest spójny z twoimi typami, to prędzej czy później Cię to spowolni. Użycie tego narzędzia powinno być zgodne z twoimi intencjami i decyzjami, odzwierciedlając je w kodzie. Nie zaniedbuj aktualizowania interfejsów i typów, a jeśli trzeba, także refaktoryzacji; pamiętaj o walidacji, TypeScript znika w runtime, używaj go w strict mode, zmuszając zespół do trzymania się zasad.
Podsumowanie
Może powinienem był zrobić z na ten temat osobną sekcję, ale wiele rzeczy, których tutaj omówiłem, jest — nie zawsze, ale często — spowodowanych pośpiechem. Możesz myśleć o testach, architekturze, czystym kodzie, zależy Ci na komunikacji, ale czujesz oddech deadline'ów na karku i idziesz na skróty, bo martwisz się o projekt, swoją pozycję itd. Ale nie ma innej drogi niż szczerość wobec złożoności zadania, komunikowanie blokerów, domaganie się spotkań o architekturze i wzorcach w kodzie, wywoływanie dyskusji, niezgadzanie się, gdy ktoś łamie ustalenia zespołu, i dopytywanie, kiedy ktoś daje ci niejasne wymagania. Mimo trendów i modnych sloganów mówiących, że kod jest tani i łatwy do stworzenia, kod to odpowiedzialność i fundament dobrego projektu, a Ty i Twój zespół jesteście ludźmi, którzy go dostarczają i o niego dbają.
