Z długiem czy bez długu

Z tematu dług technologiczny zrobił się całkiem spory cykl. Mimo, że nie wszystkie aspekty zostały poruszone, to myślę, że poruszone zostały wszystkie najważniejsze jego aspekty zatem nadszedł czas na odpowiedzenie sobie czy da się realizować projekty bez długu.

Odpowiedź krótka brzmi NIE.

Jeśli w każdym aspekcie będziemy korzystali z wszystkiego NAJ to przy dzisiejszym tempie rozwoju okaże się, że nie robimy nic oprócz zmian wersji narzędzi, bibliotek, framework-ów, wrzucaniu coraz to nowych lepszych języków i innych tego typu wynalazków. Projekt sam w sobie nie będzie szedł za bardzo do przodu – o ile w ogóle. Cała sztuka z długiem technologicznym polega na tym, że po pierwsze primo MUSIMY być świadomi takiego zjawiska a po drugie primo MUSIMY nim jakoś rozsądnie zarządzać. Tak jak banki zarządzają długiem, tak jak kraje zarządzają długiem tak i my programiści powinniśmy zarządzać swoimi długami.

Nie wszystko co najlepsze na rynku możemy wykorzystać, podstawowy hamulec to nasi klienci. Jeśli kilku strategicznych klientów korzysta ze starych systemów operacyjnych, na których nasze nowe zabawki nie będą działać to chcąc nie chcąc nie przeskoczymy tego.

Jeśli goni nas termin (czy to prezentacji czy wdrożenia czy też po prostu kończą się pieniądze na projekt) to nie ma sensu wrzucać nowych wynalazków.

Jeśli nie mamy odpowiednich ludzi, którzy są wstanie w miarę rozsądnie rozpoznać nową technologię, to nie ma sensu w nią wchodzić.

W każdym momencie projektu warto natomiast wiedzieć gdzie jesteśmy do tyłu i wiedzieć dlaczego. W każdej chwili powinniśmy być również przygotowani na zmianę jeśli znikną nasze hamulce.

W przypadku bibliotek zewnętrznych i kontrolek oraz wszelkich kanałów komunikacji (programu) ze światem zewnętrznym wydaje się idealnie sprawdzać architektura a ’la Robert Martin. Mianowicie, co nie zależy w 100% od nas powinno być odizolowane – zapakowane w odpowiednie pudełko. To pozwoli nam zmienić zawartość tych pudełek w sytuacji gdy elementy blokujące nas znikną. Myślę, że zarówno nasz klient jak i kierownik czy prezes będą woleli usłyszeć że wymiana bazę danych na inną będzie wymagała miesiąca pracy zamiast 2 lat – lub w skrajnej sytuacji przerobienia całego projektu od nowa.

Wiedza o długu pozwala nam również go zaciągać świadomie. Moim zdaniem nie wolno, pod karą chłosty klawiaturami, iść na skróty w częściach korowych systemu ale już w detalach np. interfejsu użytkownika (KTÓRY NIE POSIADA LOGIKI APLIKACJI I LOGIKI BIZNESOWEJ!!!), możemy sobie czasem pozwolić na gorszą jakość. Pójście na skróty w pierwszym przypadku będzie miało brzemienne skutki dla całego systemu, w drugim bardziej lokalne. Jeśli w obu sytuacjach pójście na skróty oznacza zysk 5h to gdzie lepiej zostawić małe lokalne piekiełko?

Od tej pory bardziej świadomi powinniśmy podejmować bardziej świadome decyzje. Jeśli macie jakieś przemyślenia na ten temat, zapraszam do dyskusji.

Brak testów to brak pewności

Jednym z ostatnich rodzajów długów jakie chcę poruszyć to testy. Testy we wszelakiej postaci, od jednostkowych, przez integracyjne, specyfikacji, smoke, white i black box do obrzydliwych ręcznych. Brak testów to najgorsza rzecz jaką możemy zrobić.

W każdej normalnej branży (a nie takiej, gdzie większość to pryszczaci kolesie z problemami interpersonalnymi Smile with tongue out ) czyli takiej o solidnych podstawach ukształtowanych przez lata praktyki, normą są testy.

Budowlańcy robią testy wytrzymałości betonu, mieszanek, nawet próbki z każdej partii wylewanego betonu. Testują rozciągliwość stali. Testują przenikalności cieplne i milion innych rzeczy.

Przemysł motoryzacyjny jest pełen testów od – znowu – wytrzymałości poszczególnych elementów, stopów i kompozytów. Przez jazdy testowe trwające długie miesiące zanim pojazd trafi do salonów aż na testach zderzeniowych kończąc.

Lekarze i ich wynalazki są tak dokładnie testowane, że wprowadzenie nowego leku trwa latami zanim zacznie się testy na ludziach, a to dopiero początek drogi do wprowadzenia na specyfików na rynek. Zresztą sami lekarze po odebraniu dyplomu nie idą od razu do pracy aby np. operować – swoją drogą to by były jaja, “może mi ktoś zszyć pacjenta bo na zajęciach ze zszywania mnie nie było”.

Dziwnym trafem jeśli chodzi o programowanie, to często okazuje się, że testy są albo niepotrzebne albo sprowadzają się do “przeklikania” aplikacji przez 5 minut po skompilowaniu ostatnie najświeższej najlepszej wersji – czasem nawet te 5 minut to za dużo. Czy jesteśmy tacy genialni? Obawiam się, że nie. Większość z nas to po prostu ignoranci. Brak testów to nic innego jak brak pewności czy system robi to co powinien. Prosty program to setki a czasem nawet tysiące małych założeń. Każde takie założenie ma wpływ na końcowy produkt więc wypadało by każde założenie zweryfikować czy jest poprawne. Bez automatycznych testów zdajemy się na los. Praktyka wykazuje, że w większości przypadków to się sprawdza. Jednak użytkowników oprogramowania najbardziej denerwuje tych kilka przypadków gdzie są błędy. Nikt nie doceni genialności Twojego programu, jeśli mały błąd będzie powodował, że aplikacja będzie się sypać co 5 minut.

Jakoś wszystkie branże potrafią zgodzić się, że testy są kluczowe a pryszczaci kolesie z problemami interpersonalnymi  programiści ciągle mają z tym problem.

Żeby odciążyć siebie z pamiętania wszystkich założeń i odciążyć się z ręcznego klikania po aplikacji należy stosować testy automatyczne. Testy automatyczne mogą się wykonywać cały czas, w kółko. Nie kosztują nas więcej niż prąd do komputera. Nie masz komputera do testów? Użyj komputera na którym programujesz. Postaw maszynę wirtualną i niech przez całą noc kiedy nie używasz komputera (albo dzień w zależności od preferencji) niech on pracuje dla Ciebie. Niech testuje.

Testy automatyczne powinny trwać możliwie krótko aby szybko dawać informację czy wszystko jest okej oraz powinny być łatwe i tanie w utrzymaniu bo przecież nikt nie będzie tygodniami pisał/poprawiał/zmieniał testów. Należy też pamiętać, że testy to narzędzie a nie cel sam dla siebie – aha…. “przeklikanie” aplikacji to nie są testy aplikacji.

“Ale my nie mamy czasu na testowanie”

Czy aby na pewno? Ile czasu tracicie na poprawianie błędów? Ile czasu tracicie na poprawianie powracających błędów? Gdyby zainwestować kilka dni pracy na przygotowanie sensownego, automatycznego zestawu testów okaże się, że zyski będą większe niż początkowa inwestycja a z czasem ten zysk będzie rósł. Dobry zestaw testów pozwoli szybko wyłapać błąd a im szybciej wyłapiemy błąd tym taniej go poprawimy. Dla przykładu: jeśli po zrobieniu błędu 5 minut później otrzymamy o tym informację to poprawiamy to momentalnie. Jeśli błąd pojawi się tydzień później to trzeba odszukać źródło błędu (debug i krok po kroku, krok po kroku) a potem poprawić. Zazwyczaj to trwa dużo dłużej niż w sytuacji początkowej (5 minut po napisaniu błedu). Najgorsza sytuacja to taka, gdzie klient nam zgłasza błąd. Oprócz procedury odszukiwania źródła błędu dochodzi jeszcze czas klienta, który musi to zgłosić i wytłumaczyć co się dzieje, czas supportu, który musi klienta wysłuchać a potem bład zarejestrować i pewnie jeszcze czas kogoś kto musi błąd potwierdzić a po poprawieniu sprawdzić czy na pewno został on naprawiony. Całkiem spora rozrzutność. Brak jakichkolwiek testów powoduje, że najczęściej mamy sytuację jak w ostatnim przykładzie. I jak teraz wygląda wymówka “my nie mamy czasu na testowanie”? Nie macie czasu na testowanie bo robicie błędy i nic innego nie robicie tylko w kółko poprawiacie błędy – które mogły by być wychwycone przez dobry zestaw testów.

Monolity to też dług technologiczny

Monolity – wielkie projekty składające się z setek klas ściśle powiązanych ze sobą. Czasem to nawet nie muszą być przesadnie wielkie te projekty jednak wystarczy, że klasy są ściśle powiązane ze sobą.

Co to znaczy ściśle powiązane ze sobą? Jeśli w jednej klasie użyjemy słowa new, żeby stworzyć obiekt innej klasy to właśnie ściśle powiązaliśmy te dwie klasy. Jedna bez drugiej żyć nie może, nie da się jednej z nich przenieść do innej biblioteki bez odpowiednich referencji. Jeśli chcemy zlecić komuś napisanie jakiegoś modułu do nasze aplikacji, to musimy dostarczyć całą implementację wszystkich zależnych klas. Jeśli mamy ciąg 3 ściśle powiązanych klas taki, że A używa B a B używa C i mając każdą z tych klas w osobnym assembly, nie jesteśmy wstanie zmienić klasy B na inną (np B’) wykonania stosownych poprawek w klasie A. I o te poprawki się w głównej mierze rozchodzi. W obecnych czasach chyba nikt nie ma wątpliwości, że software się zmienia. Jeśli się zmienia to trzeba wprowadzać zmiany. Jeśli zaś wprowadzać zmiany to łatwo i szybko a nie tak, że zmiana w klasie B powoduje potrzebę wprowadzenia zmian w klasie A. Jak to zrobić? Ano całkiem prosto. Klasy powinny zależeć od abstrakcji – czyli Dependency Inversion Principle. Jeśli zależą od abstrakcji to możemy je podmieniać w miarę łatwo. Jeśli użyjemy jakiegoś kontenera DI – takiego co konfiguracje ma w XML-u to poszczególne kawałki systemu możemy podmieniać na inne bez rekompilacji kodu. Czyż nie jest to piękne?

Monolity mają jeszcze jedną wadę. Jeżeli wszystko jest zależne od wszystkiego, klasy ściśle powiązane a każda zmiana wymaga przeorania setek miejsc w kodzie, żeby to to ruszyć w ogóle, to metody postępowania są dwie. Pierwsza, wprowadzanie jak najmniejszych zmian czyli w praktyce powolne uśmiercanie projektu przez zaniechanie modyfikacji. Druga, dodawanie cudownych rozwiązań w stylu tutaj parametr do funkcji, tam jakiś if a tutaj case. Tym sposobem zachodzi zjawisko powolnego gnicia kodu. Z każdą iteracją takich fantastycznych hack-ów kod staje się coraz mniej czytelny i coraz ciężej rozwijalny. Koniec końców spotka go to samo co w pierwszym przypadku – śmierć.

Modne ostatnio programowanie agile czy też programowanie zwinne oznacza w dużym uproszczeniu ciągłą ewolucję i ciągłe wprowadzanie zmian, tak żeby być elastycznym na potrzeby biznesu. Nie da się być agile budując monolity, nie da się być agile zaciągając coraz to nowe długi technologiczne. W końcu, nie da się być agile jeśli po roku czy dwóch wprowadzanie zmian zaczyna trwać dłużej i dłużej. Jeśli chcemy być agile to musimy bardzo mocno trzymać się zasad dobrego programowania. Musimy tworzyć elastyczne i otwarte konstrukcje zamiast monolitów. SOLID w tym bardzo pomaga chociaż nie jest to jedyna “religia”. Aby to zrobić trzeba poświęcić odpowiednią ilość czasu i potu aby wyprodukować dobry kawałek kodu.

If you want to go fast, go well. — Robert C. Martin

Klepanie na hurrraaa… na wczoraj…. na jutro… zazwyczaj kończy się bardzo źle.

Nie będziesz refaktoryzował – będziesz miał dług

Refaktoryzacja – ot kolejne popularne słowo…. nie zupełnie. Pisząc software nie zawsze dokładnie wiemy jak on będzie wyglądał i co finalnie będzie robił – tzn. w danej chwili (zdefiniowanym kwancie czasu, żeby brzmieć mądrzej) zawsze wiemy co będzie robił, tylko z dalszej perspektywy mentalnej – tj. po dłuższym okresie może się okazać, że robi coś zupełnie innego niż początkowo zakładaliśmy. Oczywiście nie ma w tym nic złego, przecież wszyscy jesteśmy teraz agile jednak nie wiedząc co finalnie będzie robił kod, nie zawsze dobrze możemy zaprojektować jego architekturę. Nie wszyscy nie potrafią, John Skeet potrafi, ale to nie podlega dyskusji.

Architektura powoli ewoluuje i żeby nie skończyć z kupą gruzu, należy o nią dbać. Tym zajmuje się refaktoryzacja właśnie. Tzn. nie tylko tym ale między innymi. Jeśli nie dbamy o kod, nazewnictwo, strukturę i cały ten bajzel to powoli małymi kroczkami zbliżamy się do katastrowy. Ten proces niestety jest bardzo powolny – niestety ponieważ przez to łatwo go zaniedbać. Wymówki w stylu “poprawię później”, “później to zmienię”, “później wydzielę do osobnej klasy” częściej pozostają w sferze NIGDY niż kiedykolwiek się materializują a ponieważ nic się nie dzieje z tego powodu to nie ma czym się martwić. Do czasu aż uświadomimy sobie, że grzebanie w tym konretnym kodzie to straszne bagno ale wtedy jest już za późno. Dlatego o kod trzeba dbać, trzeba być jak skaut, zawsze zostawiać kod odrobine lepszym niż się go zastało.

W refaktoryzacji bardzo pomagają testy i to takie testy, które dają nam jedyne słuszne pokrycie kodu – 100%. Jeśli mamy 100% pokrycie kodu testami, to (teoretycznie) wiemy w 100% co robi nasz kod. Jeśli zaczniemy zmieniać jego strukturę, dostaniemy informację czy nasz kod działa tak samo jak przed zmianami czy nie. W sytuacji idealnej możemy zmienić całkowicie architekturę przyspieszając np. działanie programu bez jakiejkolwiek widocznej zmiany dla użytkownika. Jednak aby to zrobić musimy mieć 100%-owe testy. Przez 100%-towe testy rozumiem, takie, które dają 100% pokrycia kodu (czyli nie ma jakiejkolwiek niesprawdzonej linii kodu) i dają 100% bezpieczeństwo, że psująca zmiana zostanie wyłapana. Mając takie testy mamy komfort wprowadzania zmian. Możemy całość przerobić na CQRSy czy inne wynalazki i mieć pewność, że nic nie zepsuliśmy.

Jeśli nie mamy takich testów to może się okazać, że przy refaktoryzacji coś przypadkiem zepsuliśmy. Jeśli to będzie się zdarzać często to coraz rzadziej będziemy refaktoryzować. To zaś będzie prowadziło do psucia się kodu. Ot koło zamknięte.

Jak programiści zaciągają dług technologiczny

W ostatnim poście marudziłem na management. Zwalanie na management ma tą zaletę, że zwalnia nas programistów z odpowiedzialności. To jest ICH wina, to ONI doprowadzili do takiego a nie innego stanu i tak dalej i tak dalej. Pułapką takiego zachowania jest “wymówka”. Wymówka przenosi naszą odpowiedzialność na kogoś lub coś innego. Wymówka jest świetna bo wybiela nas. Na konferencji 33 degree trafiłem ciasteczko z wróżbą o takiej treści:

W życiu ma się albo wymówki albo wyniki

Dlatego pora zmienić coś na lepsze i przestać mieć wymówki a zacząć mieć wyniki. Żeby zaś mieć wyniki to zobaczmy co my programiści robimy źle nieświadomie zaciągając dług technologiczny.

Happy path – sprawdzenie tylko działającej ścieżki w programie. Mechanizm często wygląda tak, pracując pod silną presją czasu spieszymy się aby napisać działające minimum. Wiele rzeczy nie sprawdzamy po drodze, nie sprawdzamy warunków brzegowych ani wyjątkowych. Odpalamy program – klikamy – działa!!! Ponieważ pracujemy pod presją czasu to wrzucamy kod do repozytorium i lecimy do następnego zadania. Co się stanie jak nie będzie istniał katalog roboczy albo plik będzie zabezpieczony przed zapisem, albo baza nie będzie istniała albo nie będzie internetów czy innych intranetów? No cóż, program się pięknie wyp….. ee poinformuje użytkownika o nagłym zamknięciu. Dług polega na tym, że to zadanie wróci do nas w postaci błędów a im później do nas wróci tym więcej czasu będziemy nad nim siedzieli.

To się nigdy nie wydarzy – jeżeli piszemy mały program, taki na szybko, prawie model na jakieś demo, coś co później naprawimy – to częściej niż rzadziej okazuje się, że spędzimy nad tym mnóstwo czasu poprawiając i zmieniając. Wielokrotnie więcej niż gdybyśmy od razu napisali to tak jak powinno – zgodnie ze sztuką. Wielokrotność jest wielokrotnością czasu jaki upłyną od czasu kiedy stwierdziliśmy “to się nigdy nie wydarzy” i od ilości użytkowników.

Dorobię to później – najlepszy chyba przykład trace-y czy logowanie błędów. Dopisze się to później – jak będzie potrzebne. Problem polega na tym, że jak będą potrzebne bo pojawi się jakiś dziwny błąd to ich nie będziemy mieli. Co więcej dopisując je będziemy musieli przerzucić sporo kodu, żeby umieścić w każdym wymaganym miejscu – a i tak zrobimy to gorzej niż gdybyśmy dodali to od razu.

Później to zmienię – piszemy szybką sklejkę kilku linijek, które jako tako działają – tak na MacGyver-a z założeniem że później to zmienimy. Praktyka wskazuje jednak na coś innego. Taki kod nie zostanie zmieniony. Będzie zatruwał projekt do czasu aż będzie tak uciążliwy, że ktoś to przerobi – i mieć tylko nadzieję, że będzie się to dało jeszcze przerobić.

Miejsc i sposobów na zaciąganie długu technologicznego przez programistów jest jeszcze więcej, dużo więcej. Generalnie każda droga na skróty powoduje, że pozostawiamy mniejszą lub większą dziurę, w którą my sami pewnego dnia wpadniemy.

Jak management zaciąga dług technologiczny

O długu technologicznym ciąg dalszy. Tym razem będzie o managemencie czyli o wszelkich kierownikach, dyrektorach, leadach, dev leadach, pm-ach, project i product managerach i wszystkich innych, którzy mają pozycję decyzyjną. Przez to rozumiem wszystkie te osoby, które, między innymi, powinny wiedzieć, kiedy będzie nowa wersja i co w jej zakres wchodzi.

Wydawało Ci się, że to tylko lenistwo programistów wpędza zespoły w długi? Otóż nie. Nie mały wkład w to ma management (zarząd w j. polskim ma trochę inne znaczenie a nic innego nie przychodzi mi do głowy).

Podstawowym grzechem jest bezsensowne naciskanie na programistów:

M(anager): Funkcjonalność X musi być dostarczona za 2 dni (bo jest prezentacja dla klienta)*

P(rogramista): Potrzebujemy na to 5 dni (w domyśle 2 tygodnie to minimum)

M: 2 dni i koniec!!! (w domyśle co te sieroty chcą pisać przez 5 dni)

P: Nie da się

M: Zróbcie ile się da (w domyśle zróbcie wszystko, super dokładnie i super elegancko)

P: Postaramy się (w domyśle nie ma bata żeby to zrobić, powiem postaram się to się odwali, jak nie napiszę testów, jak oleję to i tamto i zrobię tylko happy path i posiedzę do 20-stej kosztem życia prywatnego to będzie to jako tako działo)

M: OK (w domyśle OK bo M(anager) usłyszał że zostanie to zrobione)

I w tym momencie zaczyna się jazda. Czyli wszelkie dobre zasady odstawia się na bok i napierdala po klawiaturze jak Bruce Willis w jakiejś szklanej pułapce – aby tylko uratować świat.  Po 2 dniach Żona Cię nienawidzi (i nie to że ma jakiś uraz – przecież jest przyzwyczajona, chodzi o to ze rzeczywiście Cię nie widziała od 2 dni), Twój organizm Cię nienawidzi (chciałby wreszcie spać i ma dość tej ohydnej korpokawy), testerzy Cię nienawidzą (bo szczerze powiedziawszy napisałeś gówno) ale myślisz jestem super hero, zrobiłem, zdążyłem, jestem bogiem/lordem/czy czym tam chcesz.

Rzeczywistość jest bardziej smutna.

Generalnie wyjścia są dwa:

albo prezentacja się nie odbyła i Twoje poświęcenie delikatnie mówiąc było gówno warte

albo prezentacja się odbyła ale ponieważ było to odwalone ja było  to coś tam się wyspało, coś tam nie zadziałało i generalnie klapa.

Hola Hola, jest też trzecie wyjście. Prezentacja się odbyła, wszyscy są zadowoleni a my w swojej wspaniałości (przecież uratowaliśmy świat) zapominamy jak poszyty kod napisaliśmy (przecież działa) i lecimy znowu ratować świat.

Trzecie wyjście to chyba najgorsze z najgorszych bo ten kiepsko-marny kod zostaje bez zmiany. W tym momencie przydało by się go mocno zrefaktoryzować i doprowadzić do ładu. Jeśli tego nie zrobimy to dług który zaciągnęliśmy na potrzeby prezentacji zaczyna generować odsetki (zazwyczaj bardzo szybko) w postaci błędów.

Powyżej przedstawioną sytuację pomimo, że lekko podkolorowaną to miałem okazję przeżyć…. nie raz. Jeśli jednak myślisz, że piszę takie tam na potrzeby bloga to sięgnij do książki Clean Coder Roberta C. Martina – pisze on o podobnych sytuacjach a ma trochę większą renomę i więcej doświadczenia niż ja – może Cię przekona.

*) potrzeby prezentacji/wdrożenia/termin u klienta/przetarg/demo/cośtam cośtam, niepotrzebne skreślić

Odziedzicz a będziesz mój

Producenci kontrolek lubią kiedy kupujemy kontrolki bo mają z tego pieniądze, nierzadko duże pieniądze. Jeszcze bardziej cieszą się jak podziedziczymy po ich kontrolkach. Wtedy jesteśmy ich, na zawsze. Jesteśmy jak narkoman w rękach dilera. Dlaczego? Ano dlatego, że jeśli odziedziczymy coś po jakiejś kontrolce lub bibliotece, a jeszcze lepiej jeśli kawałek kodu, który dziedziczy należy do logiki biznesowej lub logiki aplikacji to późniejsza zmiana kontrolki wiąże się z przerobieniem większości aplikacji i wymaga sporo pracy. Nierzadko może dochodzić do sytuacji gdzie takie wyrugowanie jest nawet niemożliwe (czytaj. nieopłacalne z punktu widzenia projektu). A dlaczego porównanie do dilera? Ano dlatego, że jak znajdziemy się w takiej sytuacji to musimy prawie każdą wersję kontrolek kupować i płacić wielkie ilości siana co roku czy co wersję czy jaki tam inny model krojenia dev-ów mają ee…. model licencyjny się znaczy.

Czy zatem nie powinno się stosować zewnętrznych komponentów? Czy NIH (not invented here) jest zatem dobry? Daleki jestem od tego. Uważam, że zawsze trzeba zrobić kalkulację zysków i strat, sprawdzić co bardziej się opłaca. Często kupno zewnętrznej biblioteki jest dużo tańsze niż wyprodukowanie jej wewnątrz organizacji.

Jak się chronić?

Zakładając, że kupujemy zewnętrzne kontrolki/biblioteki, jak się chronić przed uzależnieniem? Enkapsulować, enkapsulować, enkapsulować. Tak samo jak dane pakujemy w klasy tak funkcjonalność biblioteki zapakujmy do jakiejś klasy. Jeśli stworzymy interfejs IExcelExporter i klasę ExcelExporterFromXXXCompany implementującą interfejs IExcelExporter, to w całej naszej aplikacji wiążemy się jedynie z IExcelExporter. Konkretna klasa ExcelExporterFromXXXCompany jest jedną z możliwych jakie możemy wykorzystać. Dzisiaj może tam enkapsulować kontrolkę z firmy A, jutro z firmy B. Jakkolwiek, zmiana dostawcy będzie wymagała pracy, to z punktu widzenia naszej aplikacji do zmiany będziemy mieli jedną lub kilka pojedynczych klas a nie będzie trzeba przerabiać całości.

Warstwy

Dobrze zrestrukturyzowana aplikacja składa się z warstw. W najprostszej  postaci mamy:

  • warstw dostępu do danych (DAL)
  • logikę biznesową
  • logikę aplikacji
  • interfej użytkownika (UI)

Jeśli potrzebujemy referencji do zewnętrznej biblioteki w każdej warstwie (np. do kontrolek interfejsu użytkownika) to wiedz, coś jest źle…. bardzo źle.

Kontrolki a dług technologiczny

Mam wątpliwości czy ten artykuł wrzucać pod dług technologiczny czy nie, dlatego ocenę tego pozostawiam Tobie. Trzeba jednak pamiętać, że Kupno zewnętrznej biblioteki zamiast tworzenia funkcjonalności samodzielnie z jednej strony kosztuje nasze pieniądze a z drugiej strony pozwala szybko uzyskać w miarę pożądany efekt bez wynajdywania koła na nowo. Jeśli kontrolkę użyjemy mądrze to nie będzie ona stanowiła długu, jeśli źle to się nim stanie. Wybór należy do Ciebie.

A jeżeli dotarłeś do tego miejsca ale ciągle myślisz “o czym ten koleś marudzi” to proszę sobie przypomnieć ile było szumu o to, żeby obiekty z Entity Framework były POCO i nie dziedziczyły po czymkolwiek*

 

*) tak, wiem, wielu z powie że to nie o to chodzi Smile with tongue out

Dług technologiczny–zmiana wersji

W poprzednim artykule jako jeden z pierwszych składników długu technologicznego podałem wersję narzędzi. Dzisiaj będzie trochę szerzej o tym.

Używanie starych narzędzi ma kilka dosyć poważnch skutków.

Odcinanie się od lepszych narzędzi

Pierwszy to odcinanie się od nowych zabawek, które pozwoliły by tworzyć lepsze produkty. Korzystając z .net 1.1 nie było wsparcia dla typów generycznych dzięki czemu zamiast stworzyć typ generyczny trzeba było czasem nawet pokopiować trochę kodu. Zamiast skorzystać z typów nullable trzeba było je samemu robić, a pisanie delegatów w 1.1 to był lekki koszmar. .net 3.0 przyniósł między innymi WCF który dosyć mocno ułatwił pisanie usług – wcześniej mieliśmy remoting marsharbyref i inne super szybkie wynalazki. Póżniej doszły lambdy i linq i znowu zamiast zgrabnie obsłużyć kolekcję za pomocą 1 czy 2 lambd w starych wersach trzeba było robić to na piechotę za pomocą pętli. Wystarczy sprawdzić ile zajmuje czasu wymyślenie i napisanie takiego samego kodu z i bez lambd. W skali projektu, takie drobne oszczędności potrafią zsumować się do całkiem sporych wartości.

Ślepa uliczka

To jest bardzo poważna sprawa, wyobraź sobie sytuację, że np. .net 1.1 nie działa na Windows 8, albo kontrolki, których używasz a których nie aktualizujesz od 5 lat nie działają z nowym Windowsem. Wyobraź sobie, że nie działają w aplikacji 64bitowej a następna wersja Windows będzie tylko 64 bitowa (nie wiem czy będzie, ale kiedyś nastąpi taki czas, że Microsoft zrezygnuje z wersji 32, z wersji 16bitowych już dawno zrezygnowali). Jeżeli zmieniamy wersje na bieżąco, taka sytuacja nie powinna się zdarzyć, jeśli ociągamy się z aktualizacjami, wówczas prędzej czy później możemy obudzić się z ręką w nocniku. Najgorsze w tej sytuacji jest fakt, że czasem zmiana wersji kontrolek, jakiejś biblioteki czy frameworka nie jest trywialna. I co w takiej sytuacji? Kiedy znajdziemy więcej pomocy na temat migracji .net 1.1 do 2.0 (i aktualizacji sln) wtedy kiedy większość przechodzi i ma te problemy na świeżo czy 10 lat później? To jest tak jak z psującym się zębem. Na początku wystarczy krótka wizyta u dentysty ale nie leczony spowoduje, że skończymy na ciągnącym się leczeniu kanałowym. Zwlekanie tutaj może mieć ogromnie bolesne konsekwencje.

Odpływ ludzi

Jak w każdym zawodzie tak i w programowaniu są dwie grupy ludzi, pasjonaci i hobbyści, ludzie, którzy chcą coś więcej z życia i dają coś w  zamian oraz ludzie wyrobnicy – przyszedłem do roboty, odsiedzę/odstoję/odbębnię swoje i do domu, zero własnego zaangażowania, zero czegokolwiek od siebie. Typ dojnych, krów – jak nie pociągniesz to mleka nie będzie. Jeżeli nasz projekt będzie babrał się w starych technologiach to jest duże ryzyko, że odpłyną ludzie – ci pierwsi. Oni zazwyczaj chcą robić w najnowszych najlepszych najfajniejszych rzeczach. Jeśli projekt nie jest kul to jest ryzyko, że pójdą szukać swojego Mojo w innym miejscu. Projekt zaś? no cóż, projekt zostanie z samymi wyrobnikami – powodzenia PM-ie

Kiedy zmieniać wersję?

Nie wiem. Ja lubię zmienić możliwie szybko, nawet jeśli będzie to beta. Jednak wydaje mi się, że normalne osoby powinny zmieniać gdzieś w połowie okresu wydawniczego. To powinno pozwolić internetom na skumulowanie odpowiedniej ilości wiedzy na temat możliwych problemów. Zapewni nam w miarę nowoczesne narzędzia a jednocześnie nie będziemy spędzać nad tym zbyt wiele czasu (który bety lubią pochłonąć)

Chyba tyle w kwestii używania starych narzędzi, następnym razem na warsztat weźmiemy drogi na skróty w programowaniu.

Dług technologiczny

Dzisiaj będzie o długu technologicznym, czyli o sprytnie ukrywającym się koszcie projektu. Koszcie, który z czasem potrafi zabić najlepsze projekty a nawet firmy.

„As an evolving program is continually changed, its complexity, reflecting deteriorating structure, increases unless work is done to maintain or reduce it.”

Meir „Manny” Lehman 1980

Parafrazując ciągle zmieniający się program zwiększa swoją złożoność o ile nie pochylimy się nad kodem aby ją zmniejszyć. Pisanie programów jest łatwe, wręcz banalnie łatwe, jednak pisanie tak, aby dało się je długo i w miarę tanio utrzymywać jest bardzo trudne. Wynika to z dwóch złożoności. Jedna to złożoność samego problemu, druga to złożoność przypadkowa. Tą przypadkową tworzymy my sami – programiści. Sami komplikujemy soft bardzo często zupełnie niepotrzebnie. Sami idąc na skróty gmatwamy kod tak, że staje się z dnia na dzień coraz droższy w utrzymaniu. To podrażanie utrzymania to właśnie dług technologiczny.

Dług technologiczny, jak każdy dług ma to do siebie, że im dłużej nie spłacany tym więcej będzie kosztować. Każda droga na skróty jest jak zaciągnięcie małej pożyczki, której jeśli nie spłacimy szybko – nie poprawimy kodu za tydzień/miesiąc – może nas zniszczyć samymi odsetkami. Oczywiście nie zniszczy nas od razu. Dług zabija powoli, tak jak rosnące powoli odsetki lub jak tłuszcz odkładający się w żyłach.

Dług technologiczny to nie tylko pójście na skróty podczas kodowania, to również zaniechanie przechodzenia na najnowsze wersje narzędzi programistycznych oraz nie wspieranie najnowszych systemów operacyjnych. Znowu na początku nic takiego się nie dzieje – nawet możemy odczuć zbawienne skutki nieprzechodzenia na nowe wersje w końcu nie marnujemy czasu na zmianę IDE czy dociągnięcie kodu tak, żeby wspierał najnowszy system np. Win8 – jednak z czasem, może się okazać, że jest za późno.

Jak to działa?

Wyobraźmy sobie, że piszemy aplikacje pod .net 1.1 i zamiast przechodzić na kolejne wersje frameworka jak wychodziły cały czas pracujemy z 1.1 Po kilkunastu latach wychodzi Windows X, na którym framework 1.1 Mamy ostatnią szansę, żeby coś z tym zrobić, adaptacja windowsów trochę trwa. Przesypiamy jednak ostatnią szansę (czytaj mamy milion wymówek) i 2 lata później, jak ~50% ludzi korzysta z Windowsa X my dalej nie przeszliśmy na nowszą wersję. Właśnie doszliśmy do sytuacji gdzie odcieliśmy się od połowy potencjalnych klientów. Być może właśnie nasz projekt zaczyna umierać.

Oczywiście można wszystko przepisać ale ile to kosztuje? Można też przejść na nową wersję (W KOŃCU) ale kto pamięta jakie były rozwiązania problemów przy zmianie z strasznie archaicznej na mniej archaiczną?

Wszelkie narzędzia programistyczne, ide, kontrolki, biblioteki, frameworki musimy trzymać w możliwie najnowszej wersji, bo tylko dzięki temu możemy ustrzec się przed powolnym uśmiercaniem projektu.

“Możliwie najnowsza”  czyli kiedy zmienić wersję?

No właśnie, kiedy zmienić wersję? Z doświadczenia widzę, że jak najszybciej bo to właśnie wtedy najłatwiej znaleźć informację jak rozwiązać problemu przy zmianie wersji. Chyba najlepiej przejść z jedną maszyną czy projektem (w zależności czego wersję zmieniamy) aby oszacować możliwe koszty i przetrzeć szlaki a potem ruszamy z resztą. Gdybym miał określić jakieś konkretne ramy czasowe to myślę, że w połowie czasu pomiędzy wydaniem wersji x a wersji x+1 cała organizacja powinna dokonać zmiany.

Źródła długu technologicznego

Wymieniłem dwa źródła długu technologicznego:

  • droga na skróty w programowaniu
  • używanie nieaktualnych wersji

jest ich jednak więcej. Między innymi:

  • droga na skróty w architekturze,
  • brak elastyczności
  • brak testów
  • brak współpracy
  • brak refaktoryzacji
  • wypuszczanie niedokończonych wersji z rzeczami “na później”

Każda z wyżej wymienionych rzeczy (a pewnie i wiele wiecej) jest jak mała pożyczka, którą zaciągamy aby dostarczyć produkt szybciej. Jednak z każdą niespłaconą pożyczką kolejne wersje będziemy dostarczać odrobinę później.

Zerowy dług technologiczny

Czy to jest w ogóle możliwe? NIE. I tu mógł bym skończyć ale słowo wyjaśnienia, bankowcy mają pojęcie zarządzania długiem. Świat IT też powinien nauczyć się tego. Niektóre długi możemy olać, inne możemy niespłacać latami ale są takie, które należy spłacić odrazu a najlepiej ich nie zaciągać. W każdym projekcie będzie to trochę inaczej wyglądało. Każdy projekt będzie miał w innym miejscu złoty środek minimalnego długu ale należy nauczyć się z tym żyć i nim zarządzać.

Tyle na dzisiaj. Temat ledwo co zarysowałem dlatego w najbliższych wpisach spróbuję się pochylić dokładniej nad poszczególnymi elementami tej układanki.