Ostatnio pisałem o długu technicznym, który kumuluje się w produktach zbudowanych byle jak i tylko pozornie w pełni ukończonych. Ich bylejakość skutkuje podatnością na awarie, brakiem stabilności i powtarzalności działania, ale także problemami z dalszym rozwojem. Developerzy potykają się o dług techniczny, który w najlepszym razie spowalnia wykonanie zaplanowanych prac, a nierzadko uniemożliwia ich ukończenie. Dlatego z długiem technicznym trzeba walczyć.

Trudność polega na tym, że aby długowi przeciwdziałać, trzeba najpierw mieć świadomość jego istnienia, a przecież często jest zaciągany zupełnie nieświadomie i nie wynika z celowych zaniechań tylko z braku kompetencji. Zdarza się więc często, że Developerzy odkrywają dług dopiero po pewnym czasie, gdy już zdążył obrosnąć mnóstwem kolejnych zmian w produkcie, które nieuchronnie długiem tym zostały skażone. Odsetki, z jakimi będzie ostatecznie spłacany (a więc dodatkowa praca wynikająca z istnienia w produkcie niedoróbek), są wtedy wyjątkowo wysokie.

Istnieją przy tym formy długu, które są wyjątkowo zwodnicze, bo sprawiają wrażenie czegoś przynajmniej nieszkodliwego, a być może nawet korzystnego dla produktu. I o nich właśnie chciałbym dziś napisać.

Development wybiegający zbyt daleko w przyszłość

Jedną z form nieoczywistego długu technicznego jest budowanie rozwiązań nadmiarowych, które aktualnie nie są potrzebne, ale „na pewno w przyszłości staną się niezbędne”. Oczywiście wszystkie przygotowane na zapas rzeczy wytwarzane są z pełnym reżimem jakościowym, jakiego wymaga produkt – nie jest to partanina wykonywana szybko i niestarannie.

Developerzy liczą, że w ten sposób przyspieszą realizację przyszłych zmian w produkcie i ograniczą potencjalne marnotrawstwo, jakim wydaje im się wielokrotne przerabianie poszczególnych funkcjonalności. Wiedząc, jak mają one działać docelowo, warto zbudować je od razu tak, by dalszy development wymagał zmian minimalnych, nieprawdaż? Wydaje się to tym bardziej opłacalne, im wyższe jest prawdopodobieństwo, że plany rozwoju produktu nie ulegną istotnej zmianie.

Czy to aby na pewno dług, skoro nikomu nie wadzi, a jego istnienie może przynieść wymierne korzyści? Owszem, jest to dług, bo stanowi zaciągnięcie pożyczki, którą w przyszłości trzeba będzie spłacić z odsetkami. Szansa, że być może zostanie ona umorzona, jest zwykle niska, w przeciwieństwie do kosztów obsługi długu, które pojawią się nieuchronnie.

Po pierwsze, wytworzenie nadmiarowych rozwiązań spowolni bieżący development, nie dając nic w zamian. Potencjalne korzyści mogą, ale wcale nie muszą pojawić się w przyszłości. A im wolniej przebiegają prace nad produktem, czyli im niższa jest przepustowość procesu, w jakim jest rozwijany, tym trudniej skutecznie reagować na bieżące potrzeby interesariuszy.

Po drugie, wytworzone na zapas rozwiązania, mimo że nie są używane albo ich wykorzystanie ogranicza się wyłącznie do bieżących (prostych) potrzeb, trzeba brać pod uwagę, planując dalszy development produktu. Nieuchronnie rośnie jego złożoność, więc trudniej planować realizację kolejnych funkcjonalności, będzie też ona coraz bardziej czasochłonna. Niewykluczone, że spowodowane tym koszty, w połączeniu ze spowolnieniem tempa rozwoju produktu, nie zostaną nigdy zrekompensowane przez spodziewane przyszłe korzyści z istnienia „gotowych na przyszłość” rozwiązań.

Po trzecie, jeśli coś powstaje z myślą o wykorzystaniu tego dopiero w przyszłości i na razie nie jest do niczego używane, nie da się tego przetestować. Tak po prostu. Developerzy mogą jedynie sprawdzić, czy to, co zbudowali, działa tak, jak sobie zaplanowali. Nie są natomiast w stanie potwierdzić, że to działanie jest takim, jakiego będzie potrzeba w przyszłości, bo ta jeszcze nie nastąpiła.

A, właśnie! Czwartym problemem jest ryzyko, że przyszłość będzie się znacząco różnić od przewidywań, nawet tych uznawanych dziś niemal za pewne. Wtedy wytworzone z zapasem rozwiązania okażą się zbędne, a ich usunięcie dołoży pracy i zabierze czas, który mógłby zostać poświęcony na coś potrzebnego.

Nadmiarowa architektura i infrastruktura

Zbliżoną formą nieoczywistego długu technicznego jest planowanie i wytwarzanie architektury produktu i infrastruktury, z jakiej on ma korzystać, z dużym wyprzedzeniem, a nierzadko w ogóle przed rozpoczęciem developmentu konkretnych funkcjonalności.

Przez architekturę rozumiem tutaj podstawowe techniczne rozwiązania i mechanizmy produktu, z których Developerzy korzystają, aby zaimplementować wszystko to, z czego korzystają użytkownicy. Przykładem może być sposób przechowywania informacji i przesyłania jej pomiędzy poszczególnymi komponentami produktu oraz sama struktura tych komponentów (w jaki sposób łączą się ze sobą, za co odpowiadają itd.).

Natomiast infrastruktura to wszelkiego rodzaju sprzęt i narzędzia, których istnienie i poprawne funkcjonowanie niezbędne jest do działania produktu. Dobrym przykładem mogą być tutaj serwery, na których instalowane jest i uruchomione oprogramowanie udostępnione użytkownikom poprzez Internet.

Tworzenie architektury i infrastruktury, która na razie nie jest potrzebna, ale już jest „gotowa na przyszłość”, zmusza Developerów do podjęcia przedwcześnie wielu decyzji, które mają długofalowe skutki. Są nimi ograniczenia lub sztywne wymogi, które teraz wydają się nieszkodliwe, a o które w przyszłości Developerzy będą się ciągle potykać.

Decyzje architektoniczne i infrastrukturalne mają to do siebie, że zwykle ciężko je zmienić, bo wprowadzenie takiej zmiany jest kosztowne, czasochłonne i bywa ryzykowne.

Nadmiarowość architektury lub infrastruktury prowadzi wprost do marnotrawstwa. Są nim na pewno koszy ponoszone na utrzymywanie czegoś, co w praktyce nie jest jeszcze potrzebne (i nie wiadomo, czy kiedykolwiek takie będzie). Są nim też skutki nadmiernego skomplikowania rzeczy prostych, które mogłyby zostać wytworzone szybciej i taniej, gdyby nie wymogi nadmiernie rozbuchanej architektury lub źle dobranej infrastruktury.

Dlatego np. w metodach zwinnych (choćby w Scrumie) funkcjonuje idea wyłaniającej się architektury (ang. emergent architecture), która wybiega wprzód jedynie na tyle, w jakim stopniu ma to sens i jest realnie potrzebne. To pozwala uniknąć popadnięcia w niepotrzebny dług techniczny albo przynajmniej znacząco ograniczyć jego skalę.

Nieracjonalny perfekcjonizm Developerów

Jest w języku angielskim taki termin, jakiego czasami używa się w odniesieniu do developmentu produktów: gold plating. Opisuje niekorzystne w istocie zjawisko, w której coś, co jest wystarczająco dobre, przerabiane jest nadal, żeby stać się bliskie ideałowi. Wydawać by się mogło, że pominąwszy nieopłacalność tego zjawiska, nie szkodzi ono jakości produktu – ba, wręcz ją podnosi! Jakim cudem miałoby więc prowadzić do pojawienia się długu technicznego?

Przypominam: dług to zaciąganie zobowiązań, które trzeba będzie spłacić w przyszłości. W tym przypadku będzie to zwiększony koszt developmentu i jego spowolnienie, przez co pojawi się problem z dostatecznie szybkim reagowaniem na potrzeby interesariuszy. Cóż z tego, że produkt będzie perfekcyjnie zrobiony, skoro stanie się zbyt drogi? I czy aby na pewno użytkownicy docenią jakość dopieszczonych funkcji, jeśli będzie ich tylko kilka, bo na więcej nie starczy czasu i budżetu?

Nadmierny perfekcjonizm to inwestycja wyjątkowo perfidna, bo nie ma nawet teoretycznych szans, by się kiedykolwiek zwróć. Po prostu w nagrodę za spowolniony bieżący development nigdy nie pojawią się inne korzyści poza (być może) osobistą satysfakcją Developerów.

Poza tym każdy produkt rozrasta się z czasem, przez co wcześniej czy później pojawia się konieczność dokonywania różnych kompromisów, które zwolennicy gold platingu uznają za absolutnie niedopuszczalne. Może dojść do sytuacji, w której development trwał będzie w nieskończoność, bo efekty ciągle nie będą dostatecznie grubo i solidnie pokryte warstwą pozłoty.

Zracjonalizowanie procesu na tym poziomie wciąż jest możliwe, ale wymagać będzie od Developerów zdzierania wspomnianej już wcześniej pozłoty z wielu miejsc i zrozumienia, że dostatecznie dobre rozwiązanie nie jest od razu synonimem bubla. Niektórzy tego nie zaakceptują, więc Zespół ma szansę się rozlecieć. Inni pozostaną, frustrując się każdym dniem pracy, co będzie miało dewastujący wpływ na ich morale. A niewykluczone, że dojdą do wniosku, że skoro coś nie może być perfekcyjne, to równie dobrze może być zrobione byle jak…

Centrum sterowania wszechświatem

Wiele lat temu napisałem artykuł pod tym tytułem – zachęcam do jego lektury. To specyficzne wymieszanie nadmiarowych rozwiązań z gold platingiem i przedwczesnym wytwarzaniem rzeczy, które na razie są zbędne. Jest w stosunku do tych trzech rzeczy kolejnym krokiem na drodze do katastrofy i opiera się na złudnym wrażeniu, że tzw. pełna konfigurowalność rozwiązań może skutecznie wyeliminować konieczność dalszego developmentu produktu.

Zaczadzeni takim sposobem myślenia Developerzy próbują stworzyć produkt, który w ich zamierzeniu od pewnego momentu ma być „gotowy na wszystko” i nie będzie wymagał dalszego istotnego rozwoju. Rzecz jasna, nigdy się nie udaje.

Jest to jak najbardziej dług techniczny, ponieważ aby uzyskać, a potem utrzymać tę „gotowość na wszystko” (czyli pełną konfigurowalność rozwiązań), nie da się niczego zrobić prosto i szybko. Developerzy produkują coraz bardziej złożone mechanizmy, zatracając się w tym, a jeśli nie zatrzymają się w porę, nie da się już z tego wybrnąć. Od pewnego momentu taniej stworzyć nowy produkt niż próbować naprawiać ten skażony „gotowością na wszystko”.

W praktyce profesjonalni Developerzy powinni budować rozwiązania, które są minimalne, ale wystarczające, a przede wszystkim łatwe do zmieniania w sposób bezpieczny. Nie zaś rozwiązania ciężkie, złożone, tylko pozornie przygotowane na przyszłe zastosowania, których w rzeczywistości prawie nigdy nie uda się trafnie przewidzieć.

Uzależnienie od określonej technologii, narzędzi i praktyk

Kolejną formą nieoczywistego długu technicznego jest nadmierne przywiązanie produktu do jakiejś technologii i budowanie go w sposób wymagający użycia konkretnych narzędzi albo zastosowania określonych praktyk.

Do pewnego stopnia jest to nieuniknione i samo w sobie nie powinno być nigdy traktowane jako obciążenie. Możliwe jest jednak przekroczenie bezpiecznej skali tego typu ograniczeń, a wtedy zaczynają się problemy. Przy czym niekoniecznie musi to wynikać z narzucenia nieadekwatnych standardów przez organizację lub jej kierownictwo. Czasem dzieje się tak w wyniku decyzji Zespołu, jeśli tworzą go ludzie niechętni do dokonywania usprawnień, niegotowi na eksperymenty i uważający, że skoro coś działa, nie należy szukać dziury w całym, tylko używać tego czegoś dokąd się da.

Oczywiste jest ryzyko wynikające z silnego uzależnienia produktu od konkretnego dostawcy technologii i narzędzi. Pojawi się jednak również wymierny koszt, jakim będą trudności ze zrobieniem niektórych rzeczy bez wyjścia poza ustalony kanon narzędzi czy praktyk. Developerzy ciężkim wysiłkiem chłopa i robotnika heroicznie walczą wtedy z problemami, które – gdyby nie narzucone ograniczenia – dałoby się łatwo rozwiązać. A być może w ogóle problemy by się nie pojawiły, bo są spowodowane samym faktem użycia jakiejś technologii lub narzędzia.

Jeśli nikt nie zareaguje w porę na narastające objawy szkodliwego uzależnienia produktu od konkretnych rozwiązań technologicznych, wepchnięty on zostanie głęboko w czeluści ciasnej uliczki, z której już nie sposób będzie się wycofać.

Hype-driven development

Ostatnią formą długu technicznego, o jakiej chce napisać, a który nie jest oczywisty dla każdego, jest swoista odwrotność podejścia opisanego powyżej. Jego zarzewiem jest radosne sięganie po coraz to nowe narzędzia, techniki i praktyki, jeśli tylko wydadzą się choć trochę obiecujące. Bo „łatwiej coś będzie dzięki nim zrobić”, bo „są fajne”, bo „zawsze chciałem z tego skorzystać i w końcu jest okazja” itd.

Zakładam, że o szkodliwości takiego postępowania, zwłaszcza jeśli nie mówimy o samych początkach rozwoju produktu (gdzie będzie ono czasami uzasadnione), nie muszę nikogo przekonywać. Stale rozrastający się zbiór równocześnie stosowanych podejść, praktyk, narzędzi i technologii doprowadzi do chaosu, który przerośnie możliwości każdego Zespołu.

Co więcej, wiele nowinek technologicznych, którymi można się zafascynować i z rozpędu posłużyć, nie przetrwa próby czasu, a wtedy zaczną się problemy z samym produktem, bo albo trzeba będzie takie porzucone przez twórców narzędzie rozwijać dalej samemu, albo zastąpić go czymś innym, niekoniecznie łatwo i szybko, a prawie nigdy nie tanio.

Czemu uznaję to za dług techniczny? Bo Developerzy w zamian za natychmiastową gratyfikację („ależ to fajne!”) zaciągają kredyt, który przychodzi im spłacić po pewnym czasie, najczęściej z bardzo sowitymi odsetkami.

Ten dług techniczny ma jedną cechę, której pozbawione są pozostałe, o jakich pisałem. Skutki jego zaciągania pojawiają się stosunkowo szybko i są zwykle od razu bardzo dotkliwe, więc nie da się w niego przesadnie długo i daleko brnąć. Stąd niektórzy świadomie decydują się na to w sytuacji, gdy rozpoczyna się budowa zupełnie nowego produktu – hype-driven development trwa do momentu, gdy nie ujawnią się pierwsze problemy, co jest sygnałem, że chaos technologiczny wymaga uporządkowania. Nie chcę oceniać, czy to ma sens, ja mam pewne wątpliwości…

Tragedią jest natomiast przyzwolenie na takie podejście w przypadku produktu już istniejącego i mocno rozbudowanego. O ile wprowadzanie do niego nowych technologii i eksperymentowanie z praktykami lub narzędziami jest jak najbardziej niezbędne, trzeba robić to z wyczuciem. Pogrążenie w chaosie dużego rozwiązania, które powstawało przez kilka miesięcy albo lat, nie jest katastrofą, którą ktokolwiek chciałby oglądać od środka.

Dług techniczny a Developerzy

Opisałem kilka form niekoniecznie oczywistego długu technicznego, jaki wprowadzić mogą do produktu Developerzy. Oczywiście, można na ten temat wylać morze atramentu i zedrzeć ze dwie solidne klawiatury, zwłaszcza gdyby zagłębić się w jakąś specyficzną dziedzinę.

Przykładowo, jeśli Zespoły wytwarzają software, to czasami popadają w manierę defensywnego programowania, próbując uodpornić dostarczany kod na wszystkie możliwe zdarzenia, niekoniecznie racjonalne. Przy czym nie jest to od razu opisany powyżej gold plating, bo taki defensywny kod może być jednocześnie naprawdę kiepskiej jakości…

O tym, jaki dług techniczny i jak intensywnie przyrastał będzie w produkcie, decydują Developerzy. Nie chodzi mi przy tym o świadome dążenie do tego, żeby dług kreować, ale o to, że jego powolny przyrost jest nieuchronny, samo zjawisko zaś zależy od doświadczeń i sposobu pracy tych, którzy powinni z nim walczyć. Bo jedną rzeczą są kompetencje, a inną zdolność i wola ich użycia w określonym celu.

Tendencja do gold platingu albo budowania centrum sterowania wszechświatem z czegoś przecież się bierze. Powiada się, że generałowie zawsze szykują się na kolejną wojnę, wyobrażając sobie przy tym tę, w której już uczestniczyli – podobnie jest z Developerami. W każdy kolejnym produkcie, z jakim przyjdzie im pracować, starają się nieświadomie zapobiegać problemom, z którymi zetknęli się wcześniej, co popycha ich niestety w stronę postępowania nie zawsze racjonalnego i korzystnego.

Nie wiem, czy da się to zjawisko wyeliminować, ale istnieje prosty sposób na skuteczne ograniczanie jego skali i negatywnych skutków. Jest nim praca zespołowa, czyli organizowanie developmentu w ten sposób, żeby w miarę możliwości każdym tematem zajmował się cały Zespół, rozdzielając między siebie poszczególne rzeczy do zrobienia. Powstaje wtedy wspólnota pracy, w ramach której indywidualne ciągoty Developerów do podejmowania nierozsądnych decyzji łatwiej wychwycić. Nie mówiąc już o tym, że kluczowe decyzje podejmowane są zespołowo, więc są wynikową różnych punktów widzenia.

Jeśli pracy zespołowej jest niewiele i normą w Zespole jest, że Developerzy rozdzielają między siebie poszczególne tematy, pracując potem nad nimi równolegle, wszelkie przekonania, podświadome obawy itd. każdego z nich mają większą szansę na przekucie się w radośnie pączkujące różne formy długu technicznego. A że brak wspólnoty pracy sprzyja również ignorowaniu problemów, nim nie staną się one naprawdę dotkliwe (bo „to nie moja sprawa, ja pracuję nad czym innym”), reakcja na ów pęczniejący dług może być spóźniona, niekonsekwentna i mało skuteczna.

Jak usunąć dług techniczny?

O tym napiszę w osobnym tekście, który pojawi się wkrótce. Przy okazji pochylę się też nad tym, w jaki sposób zarządzać długiem technicznym – oczywiście nie po to, by utrzymywać go na stałym poziomie, ale by skutecznie redukować go krok po kroku.