Pokazywanie postów oznaczonych etykietą paranoja. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą paranoja. Pokaż wszystkie posty

poniedziałek, 30 marca 2009

Inteligientny projekt



Ile kosztuje słaby i nieprzemyślany projekt?
Na początku wydaje się być kusząco tani - kokodżambo i do przodu.

Mimo iż dużo pisze się o zaciąganiu długu technicznego, o tym, że każda prowizorka i rozwiązania typu "na pałę" lub "byle jak - byle szybko" trzeba spłacić z nawiązką, to jakoś nie zawsze dociera to do świadomości.

Nudzenie o zgubnych skutkach niestosowania się np do paru prostych zasad GRASP czy SOLID jakoś nie jest zbyt przekonujące - chyba jest zbyt abstrakcyjne. Obiektowe filozofowanie typu DDD ustępuje miejsca metodykom RÓB i WHISKY (Why the Hell Isn't Somebody Koding Yet).

Aby zejść z poziomem abstrakcji i unaocznić problem w namacalny sposób zobaczmy na banalnym przykładzie pojazdu ile może kosztować (nie tylko pieniędzy ale i czasu oraz nerwów) słaby projekt.

Weźmy taki oto trywialny problem jak wymiana żarówki przedniego światła mijania w naszym modelowym systemie, którym niech będzie pojazd osobowy pewnej szwedzkiej marki, produkowany w duńskiej fabryce na amerykański rynek lecz użytkowany w centralnej europie.

/*
* Śpieszę wyjaśnić nielicznym paniom
* czytającym tego bloga, czym dla samca
* jest wymiana żarówki w jego własnym samochodzie.
* Otóż jest czymś co robimy SAMI - jest to
* punkt honoru.
* Zwracanie się z prośbą o pomoc do innego samca
* w tej jakże błahej sprawie
* jest czymś tak żenującym
* jak prośba o podtarcie tyłka.
* (Metafora może niezbyt elegancka, ale niestety
* nie znajduję w swej prostackiej głowie
* niczego co lepiej oddawało by wagę problemu)
*/


Zaczyna się od tego, że system logów wyświetla na konsoli informację sygnalizującą błąd lewego światła mijania. Hmmm mamy system autodiagnostyczny - pewnie zaszyli testy integracyjne w bootloaderze. Naajz, zapowiada się bułka z masłem.

Rozpoczynamy podejście do problemu jak rasowy informatyk. Nie informatyk, informatyk to obraźliwe słowo - coś jak konował albo znachor. Jak programista, tudzież developer lub inżynier oprogramowania. No więc zaczynamy od dokumentacji.

Dokumentacja jest co prawda nieco lakoniczna w temacie interesującej nas wymiany żarówek, ale w sumie to dobrze. Tu nie może być przecież wielkiej filozofii; cieszymy się wręcz, że nie mamy zbyt wiele do czytania - czarno na białym, jeden rysunek, jedno zdanie. Proste. Taką dokumentację to ja lubię... ukazuje sedno problemu, bez zbędnego kontekstu. Heh to musi być proste jak instalacja windowsa.

Pierwsze podejście do pracy na systemie. Dokonujemy wstępnych oględzin interfejsu użytkownika i już po paru próbach bezbłędnie lokalizujemy reflektory - na których to (jak wynika z dokumentacji) mamy za zadanie wykonać prace związane z maintenance. Właściwie to gwoli ścisłości interfejs użytkownika jest w środku. Na zewnątrz jest... hmmm powłoka (shell) mająca na celu separację użytkownika od podmuchów wiatru tudzież strug wody, która nie może się oprzeć sile grawitacji. Powłoka ma również na celu wywieranie wrażenia na blacharach - "osobnikach płci żeńskiej, dobierający sobie partnerów do prokreacji przez pryzmat samochodu jakim się poruszają" (że pozwolę sobie zacytować za nonsensopedią).

Powłoka spełnia zatem zadanie anticorruption layer (dosłownie chroniąc wnętrze przez rozkładem polegającym np na rdzewieniu) jak i warstwy servisów.

Zajrzyjmy jednak pod maskę, czyli warstwę niżej. Pięknie...cóż za separacja modułów. Niestety tylko z prawej strony. Coś co na pierwszy rzut oka wydawało się eleganckim warstwowym systemem z separacją modułów, po dokładnych oględzinach okazało się chaotycznym układem.

O ile instancja modułu oświetlenia zainstalowana prawej strony wydaje się być otwarta na prace utrzymaniowe to z lewej natomiast mamy kłębowisko spaghetti w okolicach modułu wyświetlania fotonów w kierunku jazdy. Do tego stwierdzamy silny coupling interesującego nas modułu z modułem chłodzenia - a dokładnie chyba z filtrem powietrza.

Co do cholery ma filtr powietrza z reflektorem?!? Kto scalił je tak blisko, że nawet mała rączka junior programera nie wcisnęłaby się aby sięgnąć do interfejsu żarówki - że o jej implementacji nie wspomnę! Spójrzmy jeszcze raz do dokumentacji. O ironio! Radość sprzed paru akapitów wywołana prostotą dokumentacji obraca się w rozpacz. Na tych cholernych rysunkach nie ma pudła z filtrem obok reflektora! Jak się dobrać do żarówki? Teraz przydało by się nieco więcej kontekstu w dokumentacji. Co robić? Aaaaa!

No ale może da cię coś obejść. Przecież nie pojadę z tym do warsztatu. Wyśmieją mnie. Minęło już 15 minut, czas na wunderwaffe - dekompilator, któremu nic się nie oprze - KOMBINERKI. Wspaniałe niskopoziomowe narzędzie, które obejdzie wszystkie zabezpieczenia. Metoda brute force. Wyciąganie flaków z core silnika aby zrobić nieco miejsca na ręczne prace utrzymaniowe. Hmm na moje oko jednak aby odłączyć moduł filtru powietrza należy iść dalej po zależnościach i wymontować połowę silnika. A przecież obok jest tyle miejsca. Nie możliwe aby zrobili to perfidnie - przecież to taka pożąda firma;)

Kto to projektował?!? Inżynier praktykant?

Sam tego nie zrobię, potrzeba guru od modułów oświetlenia z biegłą znajomością architektury (chyba burdelu) silników. Co za wstyd... może by tak wysłać żonę...

Ale na guru trzeba pewnie czekać w kolejce. I koszt będzie 10 razy większy niż samej żarówki. Ale jak mus to mus - przecież żadna blachara nie zainteresuje się systemem z niesymetrycznym UI.

Guru w pocie czoła przedarł się przez splątane niby-warstwy i po godzinie wymienił instancję żarówki oraz skompilował wszystko i zrobił deploy. Jeszcze tylko szybki test jednostkowy (szkoda, że po deployu:P) - tak dla formalności i bugfix można uznać triumfalnie za zakończony. Czas na commit faktury... Cholera - czerwony pasek! Yyyy to znaczy szara ściana - tzn żarówka nie świeci. Ponowna dekompilacja. Na szczęście udało się wykryć buga - spalona kostka. Szkoda, że w systemie występuje ona w egzotycznej wersji HB4 - jesteśmy uzależnieni od jednego dostawcy:/

Podsumowując
problem: wymiana 1 żarówki
czas: 2h
koszt: przemilczę
przewidywane problemy: wymiana 3 innych żarówek w lewym reflektorze w najbliższym czasie oraz związane z tym poczucie beznadziejnej bezradności:)


//===========================
Oczywiście nie wymagam absurdu polegającego na tym, że każdy kawałek systemu będzie niezależnym modułem czekającym na łatwą podmianę. Core to core - niektóre kawałki są silnie zespojone bo taka jest natura problemu - jestem w stanie to zrozumieć. Nie potrzebuję przecież zbyt często wyjmować skrzyni biegów, ale żarówka - przecież to podstawa.

Nie wymagam też niczego szczególnego typu otwartość na rozbudowę (np możliwość wymiany reflektorów na lasery służące do strącania pojazdów poruszających się nieskrajnie prawym pasem;). Chodzi o zwykłe i podstawowe czynności maintenance.

Chyba, że biznes polega na serwisie:)))

piątek, 5 września 2008

Wszechświaty równoległe

Wszechświaty równoległe...
Starożytna idea, która jakiś czas temu została wskrzeszona dzięki M-teorii. Teorii gdzie w 11, 12, 13 albo nawet 26 wymiarowej przestrzeni strun są rozpięte membrany. Brany "wiszące" w hiperprzestrzeni mogą w szczególności być dobrze znanymi nam 4-wymiarowymi wszechświatami rządzącymi się odrębnymi prawami przyrody. Brany mogą się zderzać/zlepiać niczym mokre gacie suszące się na równoległych sznurkach. Takie okresowe spotkania gaci wiążą się z pewnymi ciekawymi (i dosyć istotnymi z punku widzenia nagich małp biegających po trzecim kamieniu od słońca na peryferiach jakiejś prowincjonalnej galaktyki) zdarzeniami: zetknięcie dwóch bran to kolaps wszechświatów a następujące po nim oderwanie - wielki wybuch. I tak nieustannie... "A skądże to, jakże to, czemu tak gna?"

Mimo, że wiedzeni ciekawością póki co nie możemy (i być może nigdy nie będziemy mogli) zobaczyć membran rozpiętych na wielowymiarowej przestrzeni to jednak nie wszystko stracone...

Przecież nie ma takiego kuriozum, którego nie można by uświadczyć w programowaniu:)


Ostatnio było o kolejności. Kontynuując temat złej kolejności prowadzącej do paranoi czas na "GUI driven development". Co się dzieje, gdy zaczniemy od projektu ekranów (zwanego dumnie analizą)? Rozdzielenie ekranów pomiędzy programistów i wydanie rozkazu: RÓB (mylić z RUP) skutkuje tym, że każdy drąży swoją własną "studnię". Studnię, która schodzi w dół przez wszystkie warstwy: warstwę prezentacji (formularz i logika widoku), warstwę logiki biznesowej aż do persystencji i bazy danych (którą możemy niestety już mieć).

Czym to skutkuje? Każda studnia jest niezależnym wszechświatem równoległym - z własnym stylem, własną estetyką i własnym kodem biznesowym.

Kodem biznesowym, który jest powielany, ponieważ każdy jest tak zajęty kopaniem swojej studni, że nie ma czasu aby ogarnąć kod studni drążonych obok. Nie ma czasu ani co gorsza zasobów mentalnych - ograniczeń pojemności poznawczej nie przeskoczymy. Klapki na oczy, kilof w dłoń, kokodżambo i do przodu - yy właściwie to do dołu. Oczywiście zmiany w jednej studni są niespójne z działaniem innej więc mamy bugi. Te najbardziej paskudne - biznesowe, bo techniczne można łatwo zdiagnozować (działa albo nie).

GUI oczywiście się zmienia więc spaghetti nieuchronnie rośnie ponieważ ekrany nigdy nie są całkowicie niezależne. Logika biznesowa zmienią się nie mniej zatem mamy wiele miejsc na wykazanie się w refaktoringu tego samego:)


Po latach błędów i wypaczeń z odsieczą przychodzi na szczęście Domain Driven Design. Racjonalna kolejność nakazuje aby najpierw zająć się modelem domenowym. GUI jest jedynie zestawem perspektyw, które prezentują jakieś aspekty modelu domenowego. Więc zamiast studni mamy różne "okulary" przez które patrzymy na to samo - bez powielania.


//=========================
Analizowanie poprzez projektowanie ekranów jest oczywiście kuszące. Nie wymaga praktycznie żadnego skila (UML jest dla nerdów). Narysować ekran potrafi każdy. Zrozumieć też - klient szczególnie.

czwartek, 4 września 2008

Życia nie oszukasz

Kolejność to podstawa.
Jako przykład weźmy tę oto prozaiczną sytuację:
Wchodzisz do toalety, podnosisz deskę, siadasz, defekujesz, spuszczasz wodę, ściągasz spodnie.

Chyba oczyma wyobraźni widzimy jak zgubne w skutkach może być pomieszanie kolejności faz w nawet tak prostym procesie. A co dopiero musi się dziać w procesach bardziej złożonych... np podczas wytwarzania oprogramowania?
"... mnie oszukasz, przyjaciela oszukasz, mamusię oszukasz ale życia nie oszukasz ... "


Rebecca Brock zajmująca się "przeglądem" systemów i architektur w swej prezentacji dziwi się jak często na pytanie o analizę biznesową czy projekt domenowy zostaje jej radośnie przedstawiona baza danych.
J. B. Rainsberger jest bardziej radykalny. W swym felietonie opisuje doświadczenia z projektu, w którym zdecydował się na projektowanie bazy na koniec procesu wytwórczego.

/*
 * O ile zbytnio nie zachęcam do oglądania
 * prezentacji Pani Brock,
 * to felieton Rainsbergera polecam.
 */


Hmm czyli to, co od jakiegoś czasu podpowiada intuicja nie jest objawem jakiegoś schorzenia - inni też na to wpadli:)
Bo do czego prowadzi przedwczesne projektowanie bazy? Jeszcze nic nie wiemy o przyszłym systemie, wymagania wciąż się zmieniają a my już inwestujemy ogromną ilość czasu (zasobów w ogólności) w projekt bazy. Zaraz po tym piszemy zapytania (SQL, HQL, Criteria API), które po zmianach schematu bazy są do wyrzucenia albo przynajmniej gruntownego przepisania. Oczywiście o wiele taniej jest zmieniać model klas niż model klas, i bazy, i zapytania.

Co gorsza - z czasem gdy baza osiągnie masę krytyczną nikt nie odważy się na podjęcie decyzji o zmianie schematu! Przecież zbyt wiele DAO byłoby do przepisania! Zatem mamy piękną paranoję pod tytułem "DB driven development" - naciąganie rzeczywistości do istniejącego schematu bazy - schematu, który powstał gdy prawie nic nie wiedzieliśmy o wymaganiach; schematu, z którym po prostu musimy nauczyć się żyć:)

Rainsberger podaje jakże prostą i oczywistą metodykę: use a "dummy" persistence mechanism. Czyli jak długo się da (najlepiej do release) odwlekamy stworzenie bazy. Posługujemy się Mockowatymi implementacjami DAO albo Repozytoriów. Czyli najpierw (np. iteracyjne) rozwijamy model domenowy. Z czasem, gdy model domenowy okrzepnie możemy wreszcie przygotować tabele, które go będą utrwalać.


//======================


Rainsberger opisał sukces projektu opartego na Domain Driven Design. Czyli podejścia gdzie jesteśmy na takim poziomie świadomości, że rozumiemy iż Model Domenowy jest "sercem" systemu. To tam zawarte są reguły biznesowe, które sprawiają najwięcej problemów z utrzymaniem i rozbudową. Model domenowy modeluje byty biznesowe i interakcje pomiędzy nimi; baza jest tylko strukturą danych utrwalającą jakąś część modelu.

Pustkę po dobrym modelu domenowym można wypełnić tylko jednym: spaghetti:)
Spaghetti to wężowisko procedur (zapakowanych w klasy i dumnie nazywanych metodami) oplatających istniejącą strukturę bazy danych. Procedur, które powielają swą logikę, mieszają odpowiedzialności i zazwyczaj charakteryzują się wieloma zależnościami. Z czasem nikt nad nimi nie panuje, nie próbuje nawet refaktoryzowac; po prostu dolepia się kolejne:)
Spaghetti może i działa w wersji 1.0 ale z czasem osiągnie masę krytyczną uniemożliwiającą zmiany w jednym miejscu bez zniszczenia miejsc innych.

Przedstawione przez Rainsbergera podejście oczywiście nie sprawdzi się, gdy musimy często pokazywać działające wersje klientowi, który nie zadowoli się tym, że na jednym ekranie coś dodał/edytował a na ekranie z listą nie widzi zmian. No ale jeżeli klient nie potrafi unieść się na lekko wyższy poziom abstrakcji to może nie warto z nim robić interesów - zapowiadają się same problemy;P

Schemat bazy jakże często jest mylony z analizą czy modelem domenowym. Na schemacie widzimy co najwyżej strukturę danych, brak tam obrazu odpowiedzialności (dynamiki) czyli modelu biznesu.

I jeszcze jeden szczegół: dlaczego przechowywać byty ze świata obiektowego w relacyjnej bazie danych zamiast w obiektowej? :P