Jak podejść do pracy z legacy kodem?

Legacy kod może nam dostarczyć wielu emocji, niestety głównie tych negatywnych, czyli złości, bezsilności i stresu (szczególnie jak ma wiele lat, lub/i był pisany niedbale). Co zatem zrobić, jak podejść do tego tematu, i ogólnie, dlaczego taki straszny kod czasami powstaje? Większość programistów w swojej karierze zawodowej prędzej czy później trafi na taki kod, który będzie właśnie tym „legacy kodem”. Na bazie własnego doświadczenia, a także doświadczeń innych, chciałbym przybliżyć to, jak my podeszliśmy do legacy kodu, jakie były powody jego powstania i jak należy z nim postępować.

Czego dowiesz się z artykułu?

Tekst, jest pierwszą częścią przemyśleń o legacy kodzie i tego jak do niego podejść. Po ponad trzech latach pracy w obecnej firmie i doświadczeniu zebranym z poprzednich postanowiłem uporządkować  swoje refleksje, a w konsekwencji stworzyć właśnie ten artykuł. Na samym początku musimy zmierzyć się z teorią – tak było również w moim przypadku. Zanim jako zespół wzięliśmy się za techniczne aspekty związane ze spłatą długu technologicznego, musieliśmy poznać wiele szczegółów i zdefiniować precyzyjnie problemy, tylko po to, aby wiedzieć, jakie później zastosować rozwiązania. Kluczowy w tym wszystkim jest właśnie pierwszy etap, głównie dlatego, że musimy dowiedzieć się, co robić, aby przy naprawie błędów, nie popełniać tych samych, które spowodują, że zmienimy wszystko … o 360 stopni (a nie 180 – a przecież o to chodzi). Zapraszam więc do dalszej lektury, która wprowadzi Cię w definicje, i pozwoli poznać aspekty teoretyczne legacy kodu.

Jak zdefiniować legacy kod?

Chcąc napisać coś ciekawego o legacy kodzie, na początku trzeba zdefiniować, czym tak naprawdę jest ten byt. W mojej opinii definicja zależy trochę od tego, co mamy na myśli, mówiąc o takim oprogramowaniu. Jedni powiedzą, że legacy kod, to stary kod, nierozwijany, który budzi postrach wśród programistów, którzy przychodzą do firmy i widzą ten straszny wytwór „epoki kamienia łupanego”. Kod będący niczym hydra lernejska, gdzie ucięcie głowy powoduje, że mimo usilnej walki – te odrastają, a my tracimy siłę i nadzieję na osiągnięcie zwycięstwa. Inni powiedzą, że wcale to nie musi być stary kod, może to być nowy, dobrze napisany kod, który jednak już sobie tylko leży, nie rozwijamy go, a jedynie co – to naprawiamy, lub modyfikujemy funkcje w nim zawarte. Często też ten kod nie ma testów. W zasadzie tam już nic nowego nas nie czeka. Patrząc jednak na takie definicje, można dojść do wniosku, że mimo wszystko zakres samej definicji legacy kodu jest bardzo szeroki.

Najbardziej trafna, która według mnie jest najlepsza i która poprzez moje wieloletnie doświadczenie odzwierciedlała się w rzeczywistości, to taka, która mówi, że legacy kod, to taki, który nie jest gotowy na przyszłość, czy też – nie jest robiony z myślą o przyszłości. I to w zasadzie w naszym przypadku wyczerpuje definicję. W drugiej części mojego artykułu napiszę właśnie – jak przygotowujemy u nas kod, który jest “gotowy na przyszłość”.

 

Dlaczego powstał legacy kod?

To ważne pytanie. Wchodząc w dany projekt i widząc zagmatwany kod, w którym nie wiadomo co się dzieje, a gdy już po długim czasie walki, zrozumiesz, co tam się wydarzyło, to zaczynasz się zastanawiać, jak ktoś mógł wymyślić takie rozwiązanie. Przecież ono nie ma sensu, i wprowadza jedynie zbędne komplikacje. Dlaczego więc taki kod się pojawił?

  1. Niedostateczna wiedza

Przyczyn zapewne było kilka. W projektach, z którymi pracowałem, ewidentnie było widać ogromny brak wiedzy. Kod był pisany byle jak, funkcje były „protezami” tego systemu. Nie pomyślano o przyszłości projektu, czy tego, że będzie trzeba go utrzymywać, aktualizować, rozwijać. Nie pisano testów i nie stworzono dokumentacji ani  przedwdrożeniowej, ani powdrożeniowej, która charakteryzowałaby projekt i mówiła, jak z nim postępować na przyszłość. Nie pomyślano o tak prozaicznych rzeczach, jak SOLID, DRY i KISS. Oczywiście autora kodu nie ma co oceniać po fakcie, jednak warto nie popełniać ponownie takich błędów.

  1. Frameworki

Pracownicy również korzystali z narzędzi, które w czasach, kiedy tworzono oprogramowanie były mniej lub bardziej popularne, jednak ich wybór w mojej ocenie był czysto losowy i niepoprzedzony większą analizą. Użycie frameworka, którego wsparcie w okresie wytwarzania było wygaszane, spowodowało, że nie można już było za wiele zrobić z projektem. Rozwijano go z użyciem narzędzi niemających już szans na rozwój, w dodatku bez możliwości sprawnej aktualizacji (o tym, jakie podejście przyjęliśmy przy naszym przepisywaniu projektu, opiszę szerzej w kolejnej części) było to tak naprawdę napisaniem go od nowa, lub dalszym pogłębianiem długu technologicznego. Niestety programiści przez wiele lat przyjmowali tę drugą opcję.

  1. Stałe utrzymanie i refaktoryzacja

Niestety, coś, co w zasadzie wydaje się oczywiste, w projektach. nad którymi pracowałem, w dużej mierze było to zaniedbane. Dotyczy to zarówno starych i nowych projektów. Zdarzało się, że brak kontroli nad tym, co dzieje się w strukturach danej firmy, prowadzi do tego, że powstają dziesiątki usług, w najgorszej możliwej opcji, czyli mikroserwisy, nad którymi przy mniejszym zespole nikt nie ma kontroli – co de facto odzwierciedla też projekty, nad którymi przejąłem opiekę. Dodatkowo brak kontroli nad wspólnym wzorcem, który wyznaczałby jakość poszczególnych serwisów, sprawił, że wiele razy stosowano antywzorce. Programiści chcieli sami coś zrobić od nowa, na nowszym frameworku, żeby odświeżyć aplikacje, a to, co robili, było pogłębianiem długu technologicznego. Wielu programistów odeszło z firmy, biorąc wiedzę o danym projekcie ze sobą, a później już nikt o nich nie wiedział.

  1. Brak dokumentacji

Wielu programistów uważa pisanie dokumentacji za zbędne, albo firmy nie dają im należytego czasu na wykonanie swojej pracy, tak żeby mogli ją przygotować lub uzupełnić. W sytuacji gdy posiadamy osobę kontrolującą projekt, to ta powinna dopilnować, żeby starczyło czasu na stworzenie odpowiedniej dokumentacji. W przeszłości odkopywanie projektów zabrało nam kilka miesięcy, tak jak przygotowanie procedur i dokumentacji do narzędzi, jakimi dysponujemy. Czasami używane było w zespole określenie “systemy sznureczkowe”, co trafnie odzwierciedla pewne powiązania między systemami, o których nie mogę szerzej napisać. Jednak jak można się domyślić – mamy wiele zależności między wieloma systemami – a tym samym, wiele miejsc na błędy, awarie itp., jednocześnie nie mając o tym świadomości… do czasu pożaru, którego chyba nikt nie chce doświadczyć.

  1. Spoczywanie na laurach

Programiści. Czasami nieświadomie spoczywający na laurach, tylko dlatego, że pracując w jednej firmie wiele lat, wydaje im się, że nic ich nie zaskoczy, że mają ogromną wiedzę i doświadczenie. W zespołach, w których pracowałem, wielu uważało, że firma powinna dawać im możliwości rozwoju, bo oni nie mają na to czasu ani chęci. Niestety w mojej opinii to tak nie powinno tak działać. Dlaczego? Odpowiedź jest chyba prosta. Pracownicy są motorem napędowym firmy, liderzy są dla nich osobami wyznaczającymi cele. Jeżeli nie ma w zespole ambitnych osób, które chcą zmieniać otaczający świat, chcą sprawiać, że to, co robią każdego, dnia będzie lepsze, jeżeli spoczną na laurach, uważając, że mając kilks lat doświadczenia wiem wszystko – mogą się bardzo przejechać, a co gorsza, mogą zaprowadzić firmę nad przepaść, a tam czasami może już nie być odwrotu. Możesz mieć wiele szkoleń, możesz odbyć wiele kursów, ale jeżeli to, co tam jest “nie jara Cię”, to one Ci nic nie dadzą. Oczywiście to działa w dwie strony. Jeżeli pracodawca, czy przełożony nie potrafi dostrzec ambicji i potencjału w jednostkach, jeżeli nie potrafi zachęcić do ambitnych działań, i stwarzać do tego warunków, to również nic nie osiągniemy. To ogromna synergia między jednostkami powoduje, że każdego dnia stajemy się lepsi. Nie spoczywamy na laurach, gdyż programowanie wymaga ciągłej nauki i pogłębiania wiedzy. Czasami żyjemy w przekonaniu, że to, co robimy, jest dobre, dopóki ktoś nam nie wykaże błędów, tym samym pojawia się swego rodzaju “błysk”. I od tego momentu nic już nie jest takie samo.

 

Co zatem z tym kodem zrobić? 

Firma, z którą współpracuje od niedawna, a do której realizuje pewien projekt, jest idealnym przykładem: “Po co ruszać legacy kod, jak on działa”. Nie sposób się nie zgodzić. Główny dochód firmy właśnie pochodzi z produktów pisanych od wielu lat, w których powstało wiele błędów, i które były z czasem powielane. Dług technologiczny jest ogromny, a wielu programistów boi się podejścia do refactoringu takiego kodu. Oczywiście, jest to normalne – w końcu to ten system daje przychody, i jego uszkodzenie wiąże się z ich utratą.

 

Pierwsza najważniejsza rzecz – mieć pełną świadomość, że mamy taki kod. A co dalej? Niestety tutaj nie ma łatwego rozwiązania, każde wiedzie przez wyboistą i trudną drogę. Na początku trzeba zacząć od tego, żeby przede wszystkim skończyć z pogłębianiem długu technologicznego. Wdrożyć w firmie odpowiednie procedury i nadzór nad procesami, a następnie starać się robić tak, żeby zmiany właśnie szły w dobrym kierunku. Każdej firmie przyniesie to korzyści – i dzięki temu będzie można łatwiej reagować na zmiany na rynku, w szczególności, jeżeli prowadzimy biznes w internecie.

 

Pracę nad naprawą takich struktur należy powierzyć doświadczonym osobom, które będą w stanie podjąć się tematu, w sposób analityczny, bez wywracania firmy do góry nogami, czy bez zatrzymywania jej w celu “napisania nowego systemu”, zwłaszcza jeżeli oprogramowanie jest mocno rozbudowane. To droga donikąd, a z biznesowego punktu widzenia dla wielkich projektów – nierealna. Doświadczony architekt/programista, zrozumie biznes i zrozumie system, i w konsekwencji wypracuje między nimi synergię, która pozwoli małymi krokami wprowadzać zmiany, które w konsekwencji sprawią, że system będzie łatwiej utrzymywać, a z czasem go zmieniać.

Oczywiście jeżeli mamy mały system, którego refaktor zajmie zbliżony, lub nieznacznie krótszy czas, w stosunku do jego przepisania, to warto nad taką opcją się zastanowić.

 

Najgorszą opcją, jaką możemy zrobić, to spędzić ogrom czasu nad zastanym kodem, a następnie doprowadzić do takiej sytuacji, że za rok, dwa, ktoś inny będzie poświęcał również swój wolumen czasowy na poprawę tego samego kodu. Nie idźmy tą drogą!

 

Podsumowanie

Legacy kod będzie z nami zawsze. Jeżeli jesteś programistą, który zmienia często pracę, i głównie działa w nowych projektach, to w zasadzie nawet nie wiesz, jakie efekty przynosi Twoja praca w konsekwencji. Wynika to z tego, że duże firmy, takie jak nasza, poświęcają lata całe na rozwój oprogramowania, i czas, kiedy zostanie uruchomione ono produkcyjne, niekiedy jest bardzo długi – a dopiero wtedy możemy stwierdzić, czy wszystko działa dobrze, spojrzeć na całość “z perspektywy czasu”.

 

Gotowe rozwiązania, w postaci frameworków, i bibliotek niosą nam wiele ułatwień, ale są również pułapką, w którą możemy sami wpaść, jeśli nie podejdziemy odpowiednio do budowy aplikacji.

 

Gdy już trafimy na taki kod, to zachowajmy przede wszystkim dobre nastawienie. Ten kod jest, i nic tego nie zmieni. Nie załamujmy się tym faktem, tylko małymi krokami starajmy się dążyć do celu, którym jest jego zmiana na lepsze.  Od tego mamy zespół, który powinien się skoncentrować właśnie na rozwoju kodu, i na poprawnym podejściu do nowych funkcjonalności.


Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *