9 gru 2009

Po Zwinnym Szkoleniu

Reklamowane przeze mnie jakiś czas temu szkolenie Zwinne Rozwijanie Oprogramowania odbyło się w ostatni piątek / sobotę. Zainteresowanych co dokładnie się działo i jak poszło odsyłam do naszego firmowego bloga - Studio Pragmatists.

16 lis 2009

14 lis 2009

TDD a czysty kod

W miarę ostatnio na blogu Holistycznie o inżynierii oprogramowania pojawił się post o TDD i czystym kodzie. Podstawowym założeniem było, że TDD nie ma wiele wspólnego z czystym kodem, że łączenie tych dwóch rzeczy przez Roberta C. Martina zwanego czasem Wujem Bobem jest manipulacją i próbą wylansowania się. I choć Wuj faktycznie jest medialny i ma parcie na LCD, to myślę, że autor jednak trochę się tu zagalopował. Pod koniec autor zamieścił co prawda wytłumaczenie o co mu chodziło w poście, ale cyniczne żarty w jego treści spowodowały, że czyta się go jak tyradę przeciw testom jednostkowym. Co więcej wskazującą na brak praktycznego doświadczenia w takim tworzeniu oprogramowania przez jej autora, więc niestety dla praktyki tej krzywdzącą.

A że internet to wolne medium, zdecydowałem się zamieścić własny pogląd na TDD i jego rolę w zapewnianiu czystości kodu, tyle, że bazującą na paroletnim doświadczeniu tworzenia w ten sposób oprogramowania.

Czysty kod to kod czytelny. Wprowadzony przez Donalda Knuth'a termin/idea/paradygmat Literate Programming oznacza prawie to samo. Kod powinien dać się czytać jak literaturę (na tyle na ile to możliwe), powinien być jak najbardziej przejrzysty dla programisty pracującego z nim. Tylko wtedy będzie można zapewnić niski poziom błędów (wiemy co robi i rozumiemy jak to robi) i wysoką utrzymywalność (kod zrozumiały dla każdego kto go czyta). To oczywiście platoński ideał kodu, ale właśnie czysty kod, tak jak opisuje go Uncle Bob w książce Clean Code zdaje się być temu ideałowi najbliższy (przynajmniej w javie). To kod, który mówi dokładnie co robi. W metodach wyższego poziomu (zwykle o szerszym dostępie) jest kod bardziej odpowiadający na pytanie "co?" a czym niżej, tym bardziej idzie w kierunku "jak?".

Czytelny kod rzadko powstaje przy pierwszym podejściu. To naturalne, że mamy jakiś pomysł - nasze przemyślenia i doświadczenie podpowiadają nam rozwiązanie i jest ono zwykle w mało czytelnej formie. Czasem jest to paręnaście/parędziesiąt linii odzwierciedlających nasz pomysł na rozwiązanie w formie jakiegoś algorytmu. I to jest nasze pole prób i błędów. Niestety bardzo często kod pozostaje na tym etapie. Tzn. działa, realizuje to o czym akurat myślał jego autor, ale nie komunikuje jasno do czego służy. Potem dodajemy kolejne metody i klasy współpracujące z tą pierwszą i tak powstaje nieczytelna aplikacja o rozmytych odpowiedzialnościach klas i metod.

Gdyby jednak nie pozostać na poziomie poligonu, tylko spojrzeć na działający kod pod kątem jego czytelności i nadać mu zrozumiałą formę, byłby on nie tylko poprawny, ale też utrzymywalny. Gdy metody mają 2-3 linijki (czasem może 5-7) dużo łatwiej o lepsze ich rozmieszczenie, o przydzielenie odpowiednim klasom, o naturalne wyłanianie się wzorców projektowych (np. strategia, dekorator czy mediator to wzorce, które same wyłaniają się przy poprawnym rozłożeniu odpowiedzialności). Nie mówię, że pojawią się nawet jak ich nie znamy (w końcu to nie czary), ale dostrzeżenie miejsca dla nich jest dużo prostsze. Stąd też refaktoryzacja jest zupełnie nieodzowną praktyką programistyczną. Uważam, że niemożliwe jest napisanie kodu (o realnej złożoności, nie akademickie przykłady) od razu"na czysto". Najpierw realizujemy więc wymaganie, a potem za pomocą refaktoryzacji (i niskopoziomowej i do wzorców projektowych) dochodzimy do czystego kodu.

Ale zaraz, kod który piszemy realizując jakąś funkcjonalność rzadko żyje sam sobie. Prawie zawsze wchodzi w interakcję z innymi komponentami. Metody nowych klas będą wołane z zewnątrz, same obiekty tworzone w różnych miejscach i na różne potrzeby. Jak więc stworzyć strukturę klasy tak, by była łatwa w użyciu? Jak osiągnąć jednoznaczność wywołań? Jak zapewnić, że nasz kod będzie wygodny w wykorzystaniu z zewnątrz? Hm. Najprościej zasymulować wykorzystanie. Wiemy czego chcemy od obiektów jakiejś klasy, więc to po prostu napiszmy. W ten sposób powstają przykłady.
Piszemy więc np.
email
  .withTitle(someTitle)
  .withBody(someTextInTheBody)
  .shouldBeSentTo(recipient)
  .withReplyToAddress(replyTo);
email.send();
Jakie są szanse utworzenia takiego API za pierwszym razem, bez tworzenia takiego przykładu? Oczywiście można pozostać przy setterach i getterach, ale o ile łatwiej taki kod czytać i zrozumieć o co autorowi chodziło!
A skoro już piszemy takie przykłady, to dlaczego nie napisać tego najpierw. Najlepiej rozpocząć myślenie o klasie właśnie przez pryzmat jej wykorzystania. Każde środowisko programistyczne pozwala automatycznie wygenerować puste definicje metod, więc kod kompiluje się od razu. A jak będziemy mieli gotowy szkielet klasy, wypełniamy ją kodem. W ten właśnie sposób prowadzimy projektowanie przykładami. Dzięki temu nasze klasy mają większe szanse na właściwe rozmieszczenie odpowiedzialności między nimi (bo przykłady definiują właśnie odpowiedzialności) a do tego metody publiczne od razu mają zrozumiałe nazwy. A po zakodowaniu ich "wnętrzności" jeszcze trochę refaktorowania i również wewnętrzna struktura klas będzie przejrzysta.

Teraz mamy już kod, który powstał dzięki przykładom. No to przykłady można wywalić i pisać nowe... Zaraz! A może za miesiąc będzie trzeba trochę to przerobić... Może będzie trzeba do emaila dodać pole BCC albo formatowanie HTML. To lepiej te przykłady zostawmy - przydadzą się za miesiąc. Dodamy nowe wywołania, zmienimy nazwy tych, które zmienią znaczenie (tak, to też refaktoryzacja). Takie przykłady będące na bieżąco są do tego dość dobrą dokumentacją tego co kod potrafi i jak to osiągnąć. Może za miesiąc do zespołu dojdzie nowy programista i z satysfakcją powiemy mu RTFE ;)

To jak już mamy te przykłady i utrzymujemy je na bieżąco, jak już definiują nam one wywołania, to dlaczego nie sprawdzać przy okazji czy te wywołania poprawnie działają? Wtedy gdy tylko ktoś zmieni implementację bez modyfikacji przykładu, ten przestanie odzwierciedlać rzeczywistość i automatycznie zgłosi to programiście. Co więcej pozwolą nam one weryfikować świadome zmiany w kodzie w przyszłości. W końcu gdy tylko będzie trzeba zmienić istniejącą funkcjonalność - powiedzmy wszystkich odbiorców maila z tej samej domeny umieścić w CC a z poza w BCC - przykłady nie spełniające tego wymagania zgłoszą nam błędy. Wystarczy je wtedy zaktualizować i kod dalej jest spójny. Albo lepiej - najpierw napisać przykład, który weźmie pod uwagę nowe zmiany, zweryfikuje poprawność ich implementacji, a potem już pewni poprawności uaktualnimy niedziałające przykłady.

I tak nasze przykłady stają się automatycznymi testami. W większości jednostkowymi, częściowo akceptacyjnymi, gdzieniegdzie integracyjnymi. W połączeniu ze środowiskiem ciągłej integracji dają z niczym nie porównaną pewność poprawności oprogramowania. Przez cały czas.

W ten sposób od kodu spisanego jak notatka, draft zawierający co prawda meritum, ale w niezrozumiałej dla nikogo oprócz autora formie, dochodzimy do programistycznego dzieła literackiego. Od cowboy-coding do metodycznego rozwijania oprogramowania. Od amatorskiego "żeby działało" do profesjonalnej pewności poprawności. To jest właśnie clean code.

Mówiąc krótko: czysty kod jest wynikiem poprawnego stosowania TDD - definicji przykładu, potem implementacji a na koniec refaktoryzacji.  TDD pewnie nie jest jedynym sposobem uzyskania tego efektu, ale nie spotkałem się przykładami dużych projektów o czystym kodzie tworzonych bez TDD.

13 lis 2009

Studio Pragmatists

Ostatnio dość mało tu treści, a dużo reklam :) Postaram się, żeby był to ostatni raz.

Jako Pragmatists postanowiliśmy (wzorem najlepszych) zrobić firmowy blog, na którym będziemy umieszczać aktualności dotyczące naszej działalności, ale co ważniejsze również techniczne artykuły i refleksje dotyczące naszej pracy. Blog nazywa się Studio Pragmatists (http://blog.pragmatists.pl).

Pierwszy wpis dotyczy organizowanego przez nas szkolenia, a dokładniej tego, że przed nami ostatni tydzień rejestracji.

5 lis 2009

Zwinne szkolenie taniej :)

Właśnie się zorientowaliśmy, że szkolenia nie podlegają opodatkowaniu VAT, więc szkolenie nagle stało się tańsze i kosztuje okrągłe 2^10zł :)

Mamy już zarezerwowaną prawie połowę miejsc, więc wszystkim chętnym polecam się pospieszyć!

1 lis 2009

Ci, którzy odeszli

Te święta to dobra okazja nie tylko na refleksję nad sobą, swoim życiem i jego celowością, nad własną rodziną i innym, którym zawdzięczamy to kim jesteśmy, gdzie żyjemy, czy jakie mamy warunki życia. Dla nas - ludzi zajmujących się oprogramowaniem - to może też być czas na refleksję nad tymi, dzięki którym jesteśmy tu gdzie jesteśmy - czas na chwilę wspomnienia wielkich matematyków i informatyków, którzy swoje zawodowe życie (a często i prywatne) oddali temu, co dziś jest naszym zawodem i naszymi narzędziami. By to co kiedyś nieosiągalne i skrajnie trudne mogło dla nas być codziennością.

Kiedyś już polecałem poniższy film, tym razem polecam przerzucić dokładnie do 74 minuty i oddać dziś tym właśnie ludziom chwilę.

31 paź 2009

Zwinne szkolenie

Zeszłoweekendowa warsjava była chyba pierwszym większym Warszawskim spotkaniem ludzi zainteresowanych zwinnymi metodykami. Jej sukces (ponad 100 osób z tego co wiem i z tego co widziałem) pokazuje, że zwinne tematy są nie tylko ciekawe, ale przede wszystkim ważne. Idąc za ciosem zdecydowaliśmy się jako Pragmatists podzielić się naszą wiedzą i doświadczeniami dojścia do tego co osiągnęliśmy w większych szczegółach. A osiągnęliśmy myślę, że dość niezwykłą rzecz. Jesteśmy zespołem, który realizuje to o czym mówi: krótkie iteracje, TDD, programowanie w parach, prawdziwy shippable product na koniec każdej iteracji,  i wiele innych zwinnych praktyk wcielonych w życie. Ale przede wszystkim jesteśmy ukierunkowani na przynoszenie wartości, na pokonywanie barier i problemów, na dawanie swojego czasu, wiedzy, doświadczenia i zaangażowania naszym klientom. Staramy się skupiać na poprawianiu swojej efektywności i jakości.
Zdecydowaliśmy się więc zorganizować otwarte dwudniowe szkolenie ze Zwinnego Rozwijania Oprogramowania. Szkolenie planowane jest dla 15~20 osób zainteresowanych pogłębieniem swojej wiedzy w tym zakresie i zdobyciem nowych doświadczeń. Szkolenie prowadzić będę ja wraz z Krzyśkiem Jelskim, który na warsjavie i ostatniej javarsovii zajmował się tematem TDD oraz ma za sobą dobre parę lat doświadczenia pracy w zwinnych zespołach. Obiecujemy nie tylko masę wartościowej, konkretnej wiedzy, ale również dobrą zabawę pomagającą utrwalić i zweryfikować nowe umiejętności.

Szczegóły dotyczące szkolenia są tu: http://pragmatists.pl/szkolenie. W razie wątpliwości / pytań piszcie.
Zapraszam!

26 paź 2009

Po WarsJawie

W ten weekend była warsjawa. Kto nie był niech (raczej) żałuje. To chyba było pierwsze większe spotkanie w Warszawie dotyczące zwinnych metodyk. I na pierwszy rzut oka widać było, że brakuje takich spotkań. Po pierwsze - było chyba ponad 100 osób, co od razu wskazuje na to, że temat jest ciekawy. Po drugie - to, że trzeba takie spotkania organizowa widać po pytaniach. Masa dotyczących estymacji (o co w ogóle chodzi), długości i zawartości spotkań, itp. Czyli dość podstawowe.
Oczywiście świetnie, że ludzie chcą się uczyć i poznawać, z drugiej szkoda, że jest jeszcze tak mało doświadczeń.
Tak czy siak, Łukasz (wraz z resztą organizatorów): wielkie dzięki za zorganizowanie tego spotkania!

Jeśli ktoś chce przejrzeć moją prezentację (zanim organizatorzy wrzucą ją na parleys'a), to jest ona tu: 
I Twój zespół może być zwinny.

I jeszcze jedno. Strasznie mnie wkurza zwrot "w agile'u" (wielokrotnie słyszałem go na warsjawie). Po angielsku słowo agile jest przymiotnikiem. To tak jakby powiedzieć "w zwinny'u używamy iteracji" :-P
Zresztą większość zdań tego typu jest i tak nieprawdziwych - manifest zwinnego rozwijania oprogramowania bardzo mało mówi o konkretnych praktykach. Zamiast tego lepiej powiedzieć "w scrum'ie" albo "w XP" gdy odnosimy się do praktyk konkretnej metodyki.
Z drugiej strony, utożsamianie zwinnego myślenia (jako podejścia, filozofii) z konkretną metodyką jest również błędem (szczególnie częste jest to w przypadku scrum'a, i szczególnie nieprawdziwe) - metodyki definiują procesy i praktyki, nie skupiają się raczej na wartościach (no, poza XP oczywiście).

21 paź 2009

Wprowadzenie do zwinnego rozwijania oprogramowania

W związku ze zbliżającą się konferencją warsjawa (na której będę opowiadał o transformacji zespołów na zwinne metodyki), oraz brakiem czegoś podobnego w języku polskim, poświęciłem parę wieczorów i napisałem wprowadzenie do zwinnego rozwijania oprogramowania (zwykle nazywane jest to agile introduction).

W czasie warsjawy nie będzie żadnej prezentacji wprowadzającej do zwinnego myślenia i zwinnych metodyk, więc jeśli ktoś nie miał z nimi dotychczas praktycznego kontaktu, nieskromnie polecam!

http://pragmatists.pawellipinski.com/Downloads/Pragmatists%20-%20Zwinne%20Rozwijanie%20Oprogramowania.pdf

14 paź 2009

Przedwczesna optymizacja

Przeglądając stronę wikipedii dotyczącą eXtreme Programming znalazłem wśród praktyk programistycznych następujące zalecenie: 'leave optimization till last'. No i faktycznie zdaje się, że jest to dość uznana zasada. Przywołuje na myśl znane zdanie Donalda Knutha
Premature optimization is the root of all evil.
Z drugiej strony zwinne metodyki zalecają utrzymywanie projektu przez cały czas jego rozwoju w stanie gotowości do wdrożenia. Scrum nazywa to potentially shippable product - na koniec każdej iteracji przygotowujemy więc wszystko co niezbędne, żeby produkt tej iteracji można było wdrożyć (nie dlatego, żeby to faktycznie co iterację robić, tylko dlatego, by utrzymywać taką możliwość i dać klientowi prawo do decyzji co do tego kiedy chce aplikację wdrożyć, zaprezentować użytkownikom itp. Poza tym robiąc to jesteśmy pewni, że projekt jest cały czas zintegrowany, i że nasz system CI i proces tworzenia oprogramowania są dobre. No i jeszcze kwestia morale zespołu, gdy wie, że projekt jest non-stop w stanie jakościowo gotowym na produkcję).

Wracając do zostawiania optymizacji na koniec, zupełnie nie zgadzam się z tym. To klasyczny przypadek zostawiania gdzieś praktyk fazowych (waterfall'owych). Jak można nazwać aplikację potencjalnie gotową do wdrożenia, jeśli nie jest wystarczająca wydajnościowo?
Tak więc optymizujmy kod w czasie jego rozwijania. Może nawet warto dodać do Definition of Done zadań zespołu warunek akceptowalności pod względem wydajnościowym.

Oczywiście we wszystkim należy zachować umiar. Ważne jest, by wydajność była akceptowalna / zadowalająca. To może oczywiście oznaczać bardzo rygorystyczne warunki np. dla systemów RT. Szczegółowy fine-tuning wydajności jakichś elementów aplikacji można jednak faktycznie zostawić na później. To znaczy jak się już okaże, że faktycznie jest problem wydajnościowy i będzie wiadomo dokładnie gdzie. Oczywiście nie sądzę, żeby było dobrym pomysłem czekanie aż okaże się to w środowisku produkcyjnym - dla aplikacji dla których wysoka wydajność jest ważna można ją kontrolować stale w czasie rozwoju aplikacji w przeznaczonym do tego środowisku CI z automatycznymi testami wydajnościowymi. Natomiast powinniśmy dbać o nieograniczanie (głównie architektonicznie) naszych możliwości optymizacji.

Z resztą powyższy cytat z Knuth'a nie jest kompletny (a większość programistów zna tylko ten jego fragment). Kompletny brzmi tak:
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.

21 wrz 2009

Agileee retrospective

This post is in english, since the conference was mainly in english (and maybe some agileeestas would like to read it.)

So, the conference was simply great. Maybe not perfect, but great.
I even had a little influence on its shape by having a talk there (the slides are here...)

So let me summarise it:

WHAT WAS GOOD:
  • venue
  • lunch
  • keynotes (Jutta Eckstein with her 'distributed agile' talk - have heard her already twice with a similar subject before, but there's always something new; David Hussman with a really touching closing talk underlining how important it is to follow what you believe in)
  • many talks
  • speaker's night out (and I thought that Poles drink a lot...)
  • lots of nice, clever and experienced people
  • ratio: cost / value 


WHAT COULD BE BETTER:
  • coffee and water not always available 
  • some talks really for agile-beginners (maybe there should be some 'experience level' mark by each talk, so that you go to the level you're at)
  • stage in Russian (I mean I could be better and speak Russian, there must have been a lot of valueable stuff, otherwise unavailable for a non-ex-soviet-country guy)


PLANS TO HAVE A BETTER AGILEEE IN FUTURE:
  • learn Russian
  • don't loose the conference schedule and read in advance where you plan to go to
  • meet even more people on corridors


So in total I'd say 8/10 (just to leave the organisers some place for kaizen...)
I really hope to be there next year, and wish it to you all!

7 wrz 2009

Pierwszy release

Dziś w Pragmatists mieliśmy pierwsze wdrożenie pierwszego projektu. To taki miły moment, kiedy jest wreszcie chwila na zastanowienie się jak poszło. Na przemyślenie co można następnym razem lepiej. Jak nie popełniać pewnych błędów, jak pomagać klientom nie wpadać w pułapki.

Projekt był hybrydowy - z naszej strony staraliśmy się robić wszystko zwinnie - zaczęliśmy klasycznie od ustalenia z klientem o co mu chodzi, utworzyliśmy masę User Stories, zrobiliśmy estymacje, backlogi, pracowaliśmy w parach, TDD, itp. itd. Z drugiej strony nasz klient, choć z grubsza chętny do zwinnego podejścia, jednak z własną historią i przyzwyczajeniami (bardzo niezwinnymi). I do tego z projektem, który miał już wyznaczony termin wdrożenia, choć jeszcze nie wiadomo było dokładnie co ma być zrobione. I to jest pierwsza pułapka i chyba bardzo częsta. Terminy rzadko ustala zespół. To często element negocjacji, jakby niemerytoryczny - ustalany między managerami i salesami. W naszym przypadku nasz klient naprawdę nie wiedział czego chce ich klient (który jest użytkownikiem aplikacji) poza ogólnikami i własnymi domysłami (z resztą błędnymi, więc po pierwszych 2 miesiącach pracy zakomunikowano nam, że trzeba zacząć od nowa i zrobić coś zupełnie innego :)). Myśmy na to przystali (no cóż, nie zdobywa się klientów od stawiania im warunków...) i skutek był taki, że ostatnie 4 tygodnie były zmaganiem z czasem, wymaganiami (prośba o ostatnią zmianę w aplikacji pojawiła się wczoraj, w niedzielę, o 22:30...) i stresem własnym i klienta.
To podstawowa różnica między projektem zwinnym a klasycznym - w zwinnym termin jest funkcją zakresu wymagań i prędkości zespołu. Dlatego staramy się dostarczać aplikacje przyrostowo, wtedy terminy przestają być kluczowe, bo klient zaczyna dostawać aplikację wcześniej i ma realny wpływ na jej zawartość, a więc również na termin kiedy zacznie ją wykorzystywać. A więc:
Action Point 1: zachęcić klienta do spróbowania teraz (po tych średnio miłych doświadczeniach ostatniego miesiąca) "naszego" sposobu - nie tylko pokazywać, ale dostarczać kolejne wersje aplikacji na produkcję co 2 tygodnie. Dzięki temu nie będzie gorączki przedwdrożeniowej, ale stały jasny dla wszystkich proces regularnego dostarczania już-nie-tylko-potentially shippable product'u (mamy zadbane środowisko CI, więc jakby się uprzeć, to z technicznego punktu widzenia moglibyśmy dostarczać nawet codziennie).
Kolejne doświadczenie to komunikacja. Tutaj mamy wciąż dużo do poprawy. To, że nie mamy bezpośredniego kontaktu z użytkownikiem wpływa na naszą niepewność co do tego, czego on może tak naprawdę potrzebować. Jak chciałby pracować z aplikacją. Np. dwa tygodnie przed wdrożeniem dowiedzieliśmy się, że użytkownik lubi na raz pracować z excel'ami o setkach wierszy, bo wtedy "wszystko widzi" - a nasz a'la-excel'owy mechanizm wspiera raczej nie więcej niż 50 wierszy.
Nasz klient jest 1300km od nas i sprawność komunikacji jest kluczowa. Maile nie zawsze starczają a cotygodniowe spotkania iteration review/planning nie dają nam odpowiedzi na kluczowe pytania: czy aplikacja się podoba, czy są zadowoleni (z aplikacji, postępu,itp.)
Action Point 2: Bardziej cisnąć o informacje w czasie planowania - w czasie trwania iteracji MUSIMY wiedzieć dokładnie co mamy robić i czemu to ma służyć.
Zespół = Ludzie + Interakcje. Widzę to codziennie. Są dni, kiedy widać jak na dłoni, że pracując razem stanowimy dużo więcej niż gdyby każdy pracował oddzielnie. Wpadamy na lepsze pomysły (bo dużo dyskutujemy, a więc projektujemy), tworzymy lepszy kod (bo wiele osób go czyta i pisze, a więc musi być zrozumiały), nakręcamy się do pracy (bo to nie tylko nasz chleb, ale głównie nasza pasja), zachęcamy się do nauki i ciągłego ulepszania samych siebie ("kuuuurde, chłopaki, ale książkę znalazłem...", "ej, Kamil, jest tam coś ciekawego w tym artykule?", "Paweł, w mordę, nie pisz takiego szitu, przecież to można ładniej zrobić!") Są dni, kiedy wychodząc z pracy dobrze rozumiem dlaczego moje dzieci płaczą, że nie chcą jeszcze wracać gdy ich odbieram z przedszkola.
Action Point 3: jeszcze więcej pracy w parach, jeszcze więcej dyskusji przy tablicy, jeszcze więcej chłonięcia wiedzy od siebie i wspólnego tworzenia nowej.
Testy, testy i jeszcze raz testy (automatyczne). To jedyny sposób robienia oprogramowania wysokiej jakości. To jedyny sposób żeby być pewnym, że możesz dostarczyć aplikację KIEDY TYLKO KLIENT ZECHCE. To jedyny sposób na uniknięcie długich niezrozumiałych metod i niejasnych rozwiązań. To jedyny sposób na weryfikowanie poprawności twoich pomysłów gdy aplikacja już się rozrosła (inaczej marnujesz długie godziny na klikanie i debugowanie). To w końcu jedyny sposób by móc sobie powiedzieć: zrobiłem (done) i jestem tego pewien. A to jedno z milszych uczuć jakie mamy w naszej pracy. Nie wierzę, że może twierdzić inaczej ktoś kto choć raz brał udział w tak rozwijanym projekcie (jest to zdecydowanie najwyższej jakości projekt w jakim brałem udział w życiu, a mam za sobą ponad 10 lat pracy w developmencie i pewnie ponad 20 projektów różnego rozmiaru za sobą).
Testy dają nam poczucie pewności a naszemu klientowi możliwość prawie dowolnego kształtowania i modyfikowania wymagań - a to przekłada się w prostej linii na jego przewagę nad konkurencją.
Action Point 4: weryfikacja (ciągła) pokrycia funkcjonalności testami (nie pokrycia linii kodu, ale pokrycia funkcjonalności) i dorabianie ich tam, gdzie ich brakuje.

Action Point 5: definiowanie zakresu iteracji za pomocą testów akceptacyjnych (A-TDD) - to dotyczy zarówno kwestii elastyczności wymagań, jakości produktu, jak i (a może przede wszystkim) komunikacji z klientem.
To tak z grubsza większość moich przemyśleń co do tego projektu. Widziałem w tym czasie zaangażowanie, kreatywność i pasję. Obserwowałem (a trochę może też wpływałem na) tworzenie się z grupy ludzi zespołu. Rodzenia się odpowiedzialności i poczucia wspólnego celu. Wiele jeszcze oczywiście przed nami. Ale przecież to zaledwie początek.

27 sie 2009

EA - niepewność? zweryfikuj to w kodzie

Architekci bardzo często podejmują decyzje bazując na swoim doświadczeniu. Często jest ono szerokie i faktycznie wystarczające do podjęcia decyzji. Ale wielokrotnie pewne decyzje podejmowane są w pośpiechu i tak naprawdę na zasadzie "tak powinno być dobrze" albo "to raczej wystarczy". Architekci zdecydowanie zbyt rzadko weryfikują swoje pomysły w kodzie. Nie jest tak, że nigdy - tam gdzie pracowałem jako architekt miałem szczęście, że zawsze była możliwość spędzenia trochę czasu na poszukiwaniach dobrego rozwiązania. Ale często podjąć decyzję w oparciu o "duże szanse, że tak jest ok" jest dużo łatwiej niż zweryfikować ją. Tak z ręką na sercu - często się po prostu nie chce. Gdy plany architektoniczne powstają niezależnie od właściwej aplikacji (przed) zweryfikowanie czegokolwiek w kodzie wymaga napisania często naprawdę dużych ilości kodu.

Przy ewolucyjnym podejściu do architektury, nie dość, że decyzje są raczej mniejsze, bazujące na ciągle rosnącej wiedzy o systemie, ale również są one łatwiej weryfikowalne. Często jakiś pomysł architektoniczny można zweryfikować w oparciu o istniejącą aplikację - mniej kodu do pisania, a i bardziej pewny będzie wynik. Z resztą programujący architekt ma dużą wprawę w pisaniu kodu (nie zapomina jak to jest, a to akurat się dość łatwo zapomina) więc i napisanie odpowiedniego POC'a nie jest dużym problemem.

Bardzo często decyzje wpływające na wydajność aplikacji podejmuje się w oparciu o założenia: że maszyna wirtualna zoptymalizuje kod, że sprzęt jest wystarczający, że to zadanie przecież wcale nie jest takie kosztowne czasowo, itp. Czasem wystarczy też dosłownie pół dnia pracy na zweryfikowanie takich założeń jakimś kawałkiem kodu. Biorąc pod uwagę koszty ewentualnych późniejszych zmian (choćby tylko czasowe, związane z przepisywaniem) raczej warto posiedzieć i sprawdzić czy nasze pomysły naprawdę są aż tak dobre :)

31 lip 2009

Self Organizing Teams

W świecie agile'owym promuje się samoorganizację zespołów. Gdy zespół sam potrafi prowadzić swoją pracę, gdy bierze odpowiedzialność za swoje zobowiązania, ma wszelkie uprawnienia by je realizować i to robi można go nazwać samoorganizującym się. Takie zespoły działają dużo efektywniej niż prowadzone klasycznie metodą poleceń i kontroli ich wykonania. Po pierwsze bardziej im się chce, bo czują się odpowiedzialni za swoją pracę. Po drugie mają większą satysfakcję, ponieważ mają dużo większą wolność. Po trzecie nie czują się traktowani jak dzieci (na temat związku idei agile'owych z wychowaniem dzieci też mam swoją teorię, ale nie o tym ten post :-))

Od samego początku firmy Pragmatists chciałem stworzyć takie środowisko, w którym branie odpowiedzialności, zaangażowanie i samoorganizacja będą czymś normalnym. Środowisko, w którym o wszystkim decyduje zespół mając na względzie dobro swoje (wzajemnie poszczególnych osób w zespole), firmy i klientów. Gdzie zespół uzgadnia swój sposób pracy, co może zrobić i na kiedy, gdzie stoją biurka i jaką chcą pić kawę. Ale również chciałem by było miejsce na zrozumienie i szacunek dla potrzeb innych, dla decyzji klientów, dla możliwości firmy.

I to się udalo - mimo projektu w toku (a jego pierwszej fazy to właściwie pod koniec) wyjechałem na 3-tygodniowe wakacje zostawiając zespół na pastwę projektu i klienta :-). Przez ten czas funkcjonalności były dodawane wcale nie wolniej niż ze mną (niektórzy sądzą, że nawet szybciej...), odbywały się regularnie standupy i retrospekcje, zakupione zostały lepsze klawiatury i mleko do kawy, odbywał się ciągły kontakt z klientem i w końcu nawet potrzeba drobnej regulacji kontaktów z klientem z moim drobnym udziałem.
Był to w jakiejś mierze test realizacji moich oczekiwań (niniejszym patentuję termin Test-Driven Company Development). Teraz oczywiście pora na małą refaktoryzację, by jeszcze poprawić już dobrze działający mechanizm :)

Mój Drogi Zespole, bardzo Wam dziękuję!

EA - KISS

Keep It Simple, Stupid! mówi starobrytyjskie porzekadło. Jak jest ono prawdziwe wie każdy, kto choć raz przedzierał się przez gąszcz klas, chaszcze metod i bagna logiki przesadnie zaprojektowanych aplikacji. Chęć przewidzenia przyszłych wymagań pcha nas często w pułapkę używania skomplikowanych konstrukcji tam gdzie dużo prostsze były by wystarczające. A skomplikowane struktury, gdy kod nie komunikuje jasno ich celu, rozwijają się szybko w totalnie niezrozumiałe monstra. Wystarczy dorzucić do nich jednego if-a tu, i jeszcze dwa tam, i początkowo śliczna implementacja wzorca przeradza się we włoskie danie z sosem bolognese.
W zwinnych projektach staramy się bronić przed takimi sytuacjami przez ciągłe projektowanie i refaktoryzację aplikacji (TDD). Praca w parach pozwala nam na ciągłe weryfikowanie wprowadzanego kodu niejako z zewnątrz - przez kolegę, który być może trzeźwiejszym okiem oceni nasze zapędy.
Ale to techniki które można stosować na poziomie kodu. A co z architekturą? Przecież cała idea definiowania architektury to przewidywanie przyszłości. Przecież to właśnie dzięki dobrym wstępnym decyzjom aplikację będzie można np. prawie liniowo skalować, czy umożliwić jej łatwą integrację z korporacyjnym ESB.

Żeby nie wpaść w pułapki podjęcia błędnych decyzji, lub roztrząsania zupełnie nieistotnych kwestii posługujemy się terminem last responsible moment. Jest to ten moment przed którym podjęcie decyzji może być przedwczesne (niewystarczające/niepełne informacje mogą spowodować podjęcie błędnej decyzji), a po którym jej niepodjęcie może powodować jakieś straty (np. zbyt wczesne podjęcie decyzji o wyborze Oracla a nie MySQL może narazić nas na ogromne koszty, zbyt późne podjęcie decyzji może oznaczać konieczność przepisania fragmentów aplikacji). Staramy się więc powstrzymywać od podejmowania decyzji w kwestiach które nas aktualnie nie dotyczą, ale bardzo pilnie obserwować i dyskutować te które mają wpływ na naszą aktualną pracę.

Aby móc pracować w taki sposób utrzymujemy naszą architekturę maksymalnie prostą, ale zawsze w takim stanie, by umożliwić jej modyfikację. Bo gdy założymy, że architektura ewoluuje wraz z rozwojem systemu, możemy też założyć, że ma ona wspierać wyłącznie te własności które są niezbędne TERAZ i których wymagalności jesteśmy absolutnie pewni (YAGNI). Z resztą czym prostsza architektura tym łatwiej ją komunikować w ramach zespołu i tym łatwiej zapanować nad nią zespołowi. A czym większy zespół tym bardziej jest to prawdziwe.

Podobnie jak w przypadku projektowania na niższym poziomie, staramy się cały czas patrzeć kiedy prostota techniczna rozwiązanie zaczyna przeszkadzać w utrzymaniu prostoty wyrazu. Taką sytuację można zwykle poznać po liczbie bezpośrednich zależności - jeśli jest ich za dużo, prostota techniczna naszego rozwiązania przestaje być wartością - trzeba więc poszukać rozwiązania bogatszego technicznie i poprawiającego komunikatywność, zrozumiałość naszego pomysłu. Powinniśmy cały czas utrzymywać balans między prostotą techniczną i prostotą wyrazu.

Dbamy więc symetrię, modularność (high cohesion, low coupling), rozdzielenie warstw, oddzielenie dziedziny od elementów infrastrukturalnych, uniezależnienie od zewnętrznych bibliotek / frameworków, itp. Jeśli pomyślimy o tych cechach w kontekście prostoty, to to jest to, co one faktycznie promują. Kod podzielony na małe moduliki jest prostszy od monolitycznej masy. Każdy z modułów z dobrze wydzieloną pojedynczą odpowiedzialnością jest prostszy niż masa if-ów i switch'y weryfikujących poszczególne przypadki.

Pilnowanie i dbanie o prostotę rozwiązań oraz zachowywanie zasady wysokiej wewnętrznej spójności i małego poziomu uzależnienia od zewnętrznych elementów (na każdym poziomie), ułatwia tworzenie zwinnych architektur pozwalających na reagowanie na zmiany wymagań.

21 lip 2009

EA - zespół który koduje system musi go projektować

Poprzednim razem pisałem ogólnie o architekturze w projektach zwinnych. Generalna zasada jest taka, że nad architekturą staramy się zastanawiać nie (wyłącznie) na początku, ale w każdej iteracji. Ale żeby móc coś takiego osiągnąć zespół który koduje system musi go projektować. By decyzje architektoniczne były świadome a nie tylko pobożnymi życzeniami muszą być podejmowane na podstawie danych - faktów. A te ujawniają się w dużej mierze w czasie developmentu. Również informację o kosztach (w sensie czasowym) decyzji i zmian architektonicznych zna tylko zespół. Tak więc to zespół jest najbliżej wszystkich danych niezbędnych do podejmowania decyzji.

Podejmowanie decyzji architektonicznych przez zespół ma poza tym wpływ na jego zaangażowanie w projekt i wzmaga odpowiedzialność za projekt. Przekazywanie władzy "w dół" (team empowerment) to dobry sposób budowania zespołu. Kto nie chciałby pracować przy projekcie ze zmotywowanymi i zaangażowanymi współpracownikami? Jak poza tym pomagać mniej doświadczonym członkom zespołu zwiększać swoją wiedzę jak nie przez angażowanie ich w takie dyskusje?

Architektura projektu w przypadku większych rozwiązań czy firm o zdefiniowanej polityce w tym zakresie może wymagać uczestnictwa architekta. To on dba o zachowanie zgodności z polityką architektoniczną firmy, ale również może służyć zespołowi swoim doświadczeniem - ale do tego celu musi on być obecny w zespole przez cały czas developmentu. Czasem wymaga to zmiany funkcjonowania organizacji - gdy w firmie jest dział architektury, który zajmuje się tworzeniem dokumentacji architektonicznej przed rozpoczęciem developmentu, może być ciężko zaangażować architekta w działania zespołu - najpewniej ma on już kolejne systemy do projektowania.

Oczywiście w przypadku dużych projektów nie ma możliwości by cały zespół projektowy (np. parędziesiąt osób) podejmowało wszystkie decyzje. Wtedy możliwe są rozwiązania takie jak zespół architektoniczny złożony z programistów z poszczególnych podzespołów. Pozwala to na zachowanie spójnej architektury całego systemu przy zachowaniu decyzji co do jego kształtu na właściwym poziomie (dalej robią to programiści aktywnie zaangażowani w development). Rotowanie osób w tym zespole pomaga dodatkowo rozprzestrzeniać wiedzę o produkowanym systemie oraz zwiększać ogólną wiedzę i doświadczenie w zespole.

W niektórych projektach widziałem spotkania w rodzaju "architecture review" podczas których architekt który zaprojektował system siedział z zespołem i przeglądał system pod kątem realizacji jego założeń. Takie spotkania oczywiście zwiększają szansę na to, że system będzie zgodny z założoną architekturą, ale nie koniecznie wpływają na to, że będzie ona zgodna z wymaganiami co do systemu. Poza tym takie przeglądy są zwykle dość powierzchowne. To coś a'la code review - dobrze je robić jeśli się nie korzysta z programowania w parach i TDD. Jeśli jednak uda nam się wprowadzić te techniki, code review jest w większości przypadków zbędne. Co więcej przeglądy architektury (podobnie jak przeglądy kodu) mają formę hierarchiczną: o to architekt sprawdza czy programistyczna masa nie zniszczyła jego dzieła - architektury (no, może trochę przerysowałem...) Praca architektów w zespole ręka w rękę z mniej doświadczonymi programistami dużo lepiej wpływa na pozyskiwanie wiedzy tych drugich i całościową pracę zespołu umożliwiając dużo efektywniejszą jego pracę.
Ciągłe dbanie o utrzymywanie, weryfikowanie i modyfikowanie architektury umożliwia jej lepszą jakość i odpowiedniość wymaganiom, do tego zmiejszając szanse na to, że będzie niezbędna jej gruntowna (kosztowna) zmiana.

Ewolucja Architektury - intro

JAVArsovia minęła (prawie 3 tyg temu...) więc zdecydowałem się zamieścić tu główne idee tematu który prezentowałem, czyli zagadnienie Ewoluującej Architektury, czy szerzej: architektury w zwinnych projektach. Część tych zagadnień bazuje na książce Dean'a Leffingwell'a Scaling Software Agility. Wszystkie zaś związane są z moim doświadczeniem i obserwacjami co do funkcjonowania działów architektury, developmentu i ich wzajemnych relacji.

Achitektura definiowana jest jako te elementy systemu, które powinny pozostać niezmienne. Te które decydują o kształcie i paramaterach niefunkcjonalnych systemu. Z tego względu chcemy zapewnić je jak najwcześniej. Architektura to zwykle również drogowskaz który mówi programistom tworzącym system jak rozwiązać krytyczne jego elementy. Z tego względu tworzona jest ona przez najbardziej doświadczone osoby, które już na początku potrafią przewidzieć okoliczności wykorzystania aplikacji, znają wymagania i mają niezbędne kompetencje techniczne by architekturę zdefiniować.
"Architektura" to termin, który wiele osób stara się zdefiniować, ale trudno wypracować jedną definicję. Są dwa wspólne elementy: pierwszym jest wysokopoziomowy podział systemu na części; drugi - decyzje, które ciężko zmienić.

Martin Fowler, PoEAA
W projektach programistycznych przywykliśmy jednak do tego, że projekty, których wymagania się nie zmieniają w czasie ich rozwoju prawie się nie zdarzają. Że znaczna część wymagań ujawnia się dopiero w czasie tworzenia systemu. Że systemy jednak czasem muszą ewoluować - obsługiwać większe obciążenie, pozwolić na integrację z jakimś systemem zewnętrznym, itp. itd. Stąd u podstaw zwinnych projektów leży:

Welcome changing requirements, even late in 
development. Agile processes harness change for 
the customer's competitive advantage.

The best architectures, requirements, and designs 
emerge from self-organizing teams.

Agile Manifesto, principles #2 & #11
Gdzie więc w zwinnych projektach miejsce na architekturę, na niezmienniki, na stanowisko architekta? Otóż staramy się stosować coś co nazywam ewoluującą architekturą. Jest to takie podejście do architektury, które zakłada, że nawet decyzje na najwyższym poziomie mogą się zmieniać. Np. że względem aplikacji w tej chwili monolitycznej mogą z czasem pojawić się wymagania modularności. Że aplikacja bazująca na współdzielonym stanie może z czasem wymagać modyfikacji na bezstanową. Zakładamy również, że część wymagań co do struktury aplikacji może ujawnić się dopiero na jakimś etapie jej rozwoju - np. nie mogły być przewidziane wcześniej, lub po prostu nie były wzięte pod uwagę (np. na jakimś etapie okazuje się, że aplikacja nie spełni zakładanych wymagań wydajnościowych ze względu na swoją architekturę).

Takie podejście do architektury zakłada, że podobnie jak inne elementy zwinnego projektu (kod, testy, dokumentacja), architektura rozwija się przez cały czas jego trwania - przez cały okres developmentu. Przecież na końcu architektura to nie to co było wstępnie pobożnie zakładane, ale struktura aplikacji - a więc kod. W najbliższych paru postach postaram się przedyskutować podstawowe elementy, które uważam za niezbędne do wprowadzenia i utrzymywania ewoluującej architektury (albo po prostu zwinnej architektury) i rolę architekta w zwinnych projektach.

Z perspektywy zmiany, rola architektury w zwinnym rozwoju oprogramowania staje się zupełnie jasna: dobra architektura jest responsywna i wspiera zwinność; kiepska będzie się opierać i ją ograniczać.

Kevlin Henney

3 lip 2009

Ewolucja Architektury

Zapraszam jutro na mój wykład o ewolucji architektury w zwinnych projektach na javarsovię. Ekipa konferencji nie zamieściła szczegółów które im przesłałem :) więc wrzucam tutaj:

Opis prelekcji:
Ewolucja Architektury
Architektura oprogramowania to ten jego składnik, co do którego
decyzje podejmowane są zwykle na samym początku projektu. Na czym
bardziej zaawansowanym etapie jest projekt, tym bardziej ryzykowne i
kosztowne są jej zmiany. Czasem jednak takie zmiany są niezbędne.
Czasem architektura ma oczywiste błędy. Czasem wymagania co do
funkcjonowania systemu zmieniają się do tego stopnia, że oryginalna
jego architektura nie może im sprostać. Paweł omówi sposoby radzenia
sobie w takich sytuacjach. Opisze jak kwestie ewolucji architektury
podejmowane są w projektach prowadzonych przy pomocy zwinnych
metodyk. Wskaże sposoby tworzenia aplikacji w taki sposób by jak
najmniej wiązać logikę aplikacji z jej architekturą, tak by umożliwić,
a czasem nawet promować jej ewolucję.

O mnie:
Paweł Lipiński jest programistą, architektem, trenerem zespołów. Przez
ostatnie parę lat pracował jako architekt aplikacji i systemowy,
głównie przy projektach klasy enterprise. Aktualnie pracuje w firmie
Pragmatists (będąc przy okazji jej właścicielem) zajmującej się
usługami dla zespołów programistycznych (coaching, szkolenie,
prowadzenie zespołów), pomocą zespołom i organizacjom w transformacji
na Scrum/XP, wyprowadzaniem projektów na prostą oraz tworzeniem
oprogramowania przy użyciu zwinnych metodyk. Posiada certyfikat
Certified Scrum Practitioner oraz niezliczone certyfikaty techniczne
związane z platformą Java/JEE.

5 cze 2009

Praktyki Zwinnego Programisty

Jeśli ktoś nie śledzi Warszawskiego JUGa, to pewnie nie wie, że w najbliższy wtorek będzie (wszystkich zainteresowanych oczywiście zapraszam):

from Jacek Laskowski
reply-to warszawa-jug@googlegroups.com
to Warszawa-JUG
date Thu, Jun 4, 2009 at 11:44 PM
subject [warszawa-jug] 49. spotkanie Warszawa JUG - IncuBIT
rozpoczyna, a po kwadransie "Praktyki Zwinnego Programisty"
Pawła Lipińskiego
mailing list Filter messages from this mailing list
mailed-by googlegroups.com
signed-by googlegroups.com

Cześć!

Warszawska Grupa Użytkowników Technologii Java (Warszawa JUG) [1]
zaprasza na 49. spotkanie, które odbędzie się 09.06.2009 (wtorek) o
godzinie 18:00 w sali 5440 Wydziału MIMUW przy ul. Banacha 2 w
Warszawie.

Temat prezentacji: Praktyki Zwinnego Programisty
Prelegent: Paweł Lipiński

Przed wystąpieniem Pawła o praktykach gościem specjalnym będzie
przedstawiciel firmy IncuBIT.pl - inkubatora innowacyjnych technologii
z zakresu Internet/Mobile współfinansowanym z PO IG 3.1. Jest to
organizacja non-profit, której celem jest wspieranie pomysłowych i
energicznych ludzi w realizacji ich pomysłów. Wystąpienie zajmie 15
minut i jej celem jest przedstawienie możliwości skorzystania z
funduszy Unii Europejskiej do finansowania własnych przedsięwzięć.

Dalsza część spotkania to występ Pawła Lipińskiego z jego tematem
agile'owym. Paweł opowie o wszystkich głównych zagadnieniach
związanych z pracą w zwinnym/agile'owym zespole. Pokaże jak wygląda
zwinny projekt zarówno od strony zarządzania (organizacja zespołu,
pracy, komunikacji z klientem, planowanie i estymacja wymagań) jak i
praktyk programistycznych (TDD, programowanie w parach, ciągła
integracja, itp.) Opowie o różnicach w pracy między klasycznym
projektem i zespołem programistycznym i pracy w środowisku agile'owym.

Paweł Lipiński [2] jest programistą, architektem, trenerem zespołów
programistycznych. Przez ostatnie lata pracował jako agile coach oraz
architekt aplikacji i systemowy, głównie przy systemach klasy
enterprise. Aktualnie pracuje w firmie Pragmatists (będąc przy okazji
jej właścicielem), która zajmuje się usługami dla zespołów
programistycznych (coaching, szkolenia, prowadzenie zespołów), pomocą
przy projektach typu 'death march', oraz tworzeniem oprogramowania z
wykorzystaniem zwinnych metodyk i praktyk. Posiada certyfikat
Certified Scrum Practitioner i niezliczone techniczne certyfikaty
głównie ze świata Java/JEE.

Planowany czas prezentacji to 1,5 godziny, po której planuje się
15-30-minutową dyskusję.

Wstęp wolny!

Zapraszam w imieniu prelegenta i grupy Warszawa JUG!

[1] http://www.warszawa.jug.pl
[2] http://www.pawellipinski.com/

Jacek

--
Jacek Laskowski
Notatnik Projektanta Java EE - http://www.JacekLaskowski.pl

--~--~---------~--~----~------------~-------~--~----~
Otrzymałeś/-aś tę wiadomość, ponieważ jesteś zapisany/-a do grupy "Warszawa Java User Group (Warszawa JUG)" w serwisie Grupy dyskusyjne.
Aby publikować na tej grupie, wyślij wiadomość e-mail na adres warszawa-jug@googlegroups.com
By wypisać się z grupy, wyślij e-mail do warszawa-jug+unsubscribe@googlegroups.com
Więcej opcji na stronie grupy http://groups.google.com/group/warszawa-jug?hl=pl
-~----------~----~----~----~------~----~------~--~---

19 maj 2009

Pragmatyczna Prywata

Nie pisałem nic od półtora miesiąca. Powodów było parę, ale główny oczywiście odwieczny brak czasu. Tym razem jednak nie był to brak czasu w rodzaju 'wdrożenie miałem' ani nawet 'projekt nam się wali'. W sumie wręcz odwrotnie.

Dnia dwudziestego kwietnia rozpoczęła działalność moja firma Pragmatists. Cały wolny czas poświęcałem więc ostatnio lataniu po urzędach, zmaganiem się z biurokracją, itp. Na szczęście udało się to wszystko załatwić i wreszcie mogę skupić się na meritum.

Ta firma to trochę eksperyment mojego "poszukiwania białych nosorożców" dość widocznego we wpisach w tym blogu. Eksperyment stawiania na jakość. Eksperyment szacunku, szczerości względem nas samych i naszych klientów, oraz całkowitej przezroczystości działań. Partnerskiego modelu tworzenia oprogramowania, gdzie dostawca i zamawiający mają wspólny cel.
Stosujemy zwinne metodyki tak na poziomie prowadzenia projektu jak i praktyk programistycznych (wraz z TDD i programowaniem w parach) i na pierwszym miejscu stawiamy jakość.

Tak więc przy okazji pozwolę sobie na mały anons:
public class Pragmatists {
public String miasto = "Warszawa";
public String rejon = "Metro Wilanowska";
public String kontakt = "contact@pragmatists.pl";

private Session chetniDoPracy;

List znajdzProgramiste() {
Programista takiJakMy = new Programista();

takiJakMy
.lubiSwojZawod(true)
.uwielbiaSieUczyc(true)
.dbaOJakosc(true)
.wieCzymJestAgile(true)
.znaJezyki(Jezyk.JAVA, Jezyk.Angielski)
.znaFrameworki(Framework.HIBERNATE, Framework.SPRING, Framework.JEE)
.maDoswiadczenie(Calendar.YEAR, Criteria.MIN, 3);

return chetniDoPracy
.createCriteria(Programista.class)
.add(Example.create(takiJakMy))
.list();
}
}

Sapienti sat! :)

1 kwi 2009

Nadszedł czas... jakości


Na jednym z blogów (dokładnie to tu) znalazłem taki wpis:
The biggest challenge for me personally was essentially mourning for the death of “Programmer Man”. Programmer Man is how I think of myself when I’ve got my headphones in, speed metal blaring in my ears, and I’m coding like a motherfucker. My fingers can’t keep up with my brain. I’m In The Zone. For most of my career, this image is what I’ve considered to be the zenith. When I come home and was in Programmer Man Mode most of the day, I feel like I’ve had a good day.
Pair Programming undeniably killed Programmer Man. This was a tough adjustment, since I’ve considered that mode to be my favorite for so long. I now see, however, that Programmer Man was, without me knowing it, Technical Debt Man.
'Coding like a motherfucker' od razu przypomniało mi nasze polskie 'Nadszedł czas napi**lania':




Nie wiem kto kiedy dlaczego i jak na to wpadł (to z tym czasem) ale naprawdę dobrze odzwierciedla to częste podejście do realizowania projektów informatycznych. Jakość nie gra roli - ważne jest dotrzymanie terminu. Co będzie po 'przebiciu piłki przez siatkę' nie interesuje dostawcy - niech się z tym buja klient.

Ciekawa jest analogia między tymi dwoma tekstami. Jak czytam tego gościa, to przypominają mi się moje postawy w projektach parę lat temu. Takie napi**lanie kodu. Samotność w sieci.
Ale tak fajnie jest tylko na początku. Po paru miesiącach takiego developmentu wracamy do domu zmęczeni i sfrustrowani. To jest nasza normalność. Nasza szara rzeczywistość. I niczym matka-polka szorująca gary i piorąca brudne skarpety swojego popijającego piwo przed TV męża (ekscytującego się, że 40 milionowy kraj może wygrać w piłkę z San Marino...) godzimy się na takie życie. Na początku przynosi nam to zadowolenie, że dopisaliśmy kolejne stosy kodu. Potem jest już tylko frustracja i niecierpliwe czekanie na coś nowego. Na wyjście z mroku.

Programowanie to sport grupowy. Dlatego właśnie nazwa Scrum odwołuje się do sportu. Bo projekty wymagają współpracy. Ciągłej. We wszystkim. Praca w grupie powoduje podnoszenie jakości. I Twojej i Twojego kodu. Ciągłe. Wspólne.
Ale żeby móc osiągnąć takie środowisko - środowisko ciągłego uczenia się, wzajemnego poprawiania, ekscytacji pracą - niezbędna jest praca zespołowa. Stąd w Scrumie tyle spotkań. Stąd coraz częściej czerpane z XP praktyki z TDD i programowaniem w parach na czele. Bo to nic innego jak ciągłe poznawanie innych i siebie, w tym co robimy. To przeglądanie się jako programiści w kodzie swoim i innych jak w lustrze. To nabywanie doświadczeń przez nieustanną obserwację innych przy pracy. I ciągłe, nieustanne czytanie kodu. Bo Twoim klientem jako programisty nie jest jakiś bank, czy portal, nie ubezpieczyciel czy operator komórkowy, tylko drugi developer. Ten który będzie czytał twój kod i próbował go zrozumieć. To do niego kierujesz kod.

Panie i Panowie - nadszedł czas jakości!

PS. Nie wiem jak tam mecz. Ja jestem po whisky, ciesząc się świeżym certyfikatem CSP właśnie czytam hipershitowy kod. I żałuję, że jego autor nie czytał zanim go napisał tego co tu właśnie spłodziłem :)

31 mar 2009

To zależy...

Rozwijanie oprogramowania to zajęcie inheretnie intelektualne. Taki truizm mi się nasunął :)
Ale zdaje mi się, że wielu programistów o tym zapomina. Myślę, że są ku temu dwa powody - przyzwyczajenia i błędne założenia.
Klasyczny wodospadowy model produkcji oprogramowania zakłada, że najpierw tworzona jest architektura i projekt oprogramowania (i tu jest myślenie - od tego jest Architekt i Projektant, którzy za pomocą Drogiego Narzędzia CASE/UML tworzą Architekturę) a potem trzeba to po prostu zakodować. Tak jak z budynkiem, tylko cegły się ciężej układa, więc przydadzą się studia. I programiści w jakimś stopniu to kupują i programują tak jak im się akurat przydarzy.
Ale w wielu firmach, szczególnie mniejszych software house'ach nie ma działu architektury. Tam ludzie przyzwyczajeni są - jeszcze może z czasów studiów - że myśli to się o algorytmach, o drzewach czerwono-czarnych, o ray-tracingu, może nawet o szeregowaniu zadań w systemie operacyjnym, ale przecież nie w aplikacji webowej. Tę każdy wie jak napisać - tu nie ma o czym myśleć.
Programiści oczekują gotowych rozwiązań. Że książka, blog, prezentacja dadzą im konkretne odpowiedzi. Że zasugerowane w czasie wykładu, rozmowy wzorce będą dobre na każdą sytuację.

Ale oprogramowanie to nie budynki (choć nic nie wiem o tym, może tam też trzeba myśleć...) Tu odpowiedzi są ogólne i niepewne. Bez drobiazgowej znajomości wymagań i reszty aplikacji nie sposób dać stu procentowej odpowiedzi. Nad każdym szczegółem, najdrobniejszym detalem powinno się posiedzieć. Przemyśleć. Zbadać siły działające w tej sytuacji. Przemyśleć jak dany kod będzie wykorzystywany. Ani reguły SOLID ani wzorce GoF ani jakieś cechy języka nie zastąpią myślenia.
Czasem trzeba brać pod uwagę kwestie merytoryczne - wydajność jakiegoś rozwiązania, bezpieczeństwo, itp. Czasem percepcyjne - jak zorganizowany kod jest czytelniejszy, co łatwiej jest zrozumieć. Czasem w końcu organizacyjno-psychologiczne - że baza danych zależna jest od kogo innego i często się zmienia, że większość zespołu woli jakiś inny sposób formatowania, itp.
Dlatego właśnie Big Design Up Front nie może dać dobrej aplikacji (tzn. może się komuś uda, ale generalnie...) Bo nie da się bez kodu i zespołu przemyśleć tego wszystkiego odpowiednio dokładnie.

Zapytał mnie niedawno kolega z pracy jakie rozwiązanie jest lepsze (zainspirowany moim ostatnim wpisem) - mieć oddzielne DAO czy metody operacji na danych bezpośrednio w encjach (Active Record). I nie ma tu dobrej odpowiedzi. W jakimś stopniu jest to kwestia estetyki, w jakimś designu reszty aplikacji. Może tez wchodzić w grę doświadczenie programistów, wybór technologii do przechowywania danych itp. itd.
Czasem używanie tych samych obiektów jako dziedziny (wraz z zachowaniami) i reprezentantów bazy danych (ORM, DTO) jest ok, a czasem (no dobra, częściej) warto trzymać swoje obiekty oddzielnie i nie wystawiać wszystkich ich pól do publicznego dostępu (w ten sposób wiążąc implementacje innych elementów kodu z wyłącznie naszą implementacją dziedziny) a dla Hibernate'a wystawiać DTO (inna reprezentacja danych w bazie a inna w dziedzinie, zmieniająca się struktura bazy, itp.)

Na wiele pytań nie ma jednoznacznych odpowiedzi na samym początku. Na wiele nie ma ich też później, dlatego robimy oprogramowanie tak, by na bieżąco dostosowywać je (refaktoryzować) do zmieniających się warunków (a przecież warunki się ciągle zmieniają w czasie developmentu, bo dodawane są kolejne funkcjonalności). Oprogramowanie nie jest z kamienia ani żelbetu, tylko jest realizacją naszych (elastycznych) myśli.
Tylko od Ciebie zależy jak je budujesz.

19 mar 2009

Repozytoria w DDD

W czasie prezentacji o DDD (albo zaraz potem) zadał mi ktoś pytanie o realizację Repozytoriów w javie. Jeśli traktować Repozytoria jako klasyczny wzorzec DAO, to realizacja jest prosta. Robimy generyczne DAO, który obsługuje wszystkie zwyczajne przypadki (CRUD) a tam, gdzie potrzebujemy jakichś szczególnych (bardziej biznesowych) metod, doimplementowujemy to w klasie dziedziczącej po tymże DAO. DAO można zrobić np. tak jak w przykładzie na stronach Hibernate. To rozwiązanie niestety wymaga podawania explicite tego DAO, wszczepiania go tam gdzie chcemy go użyć itp. Oddzielność tej warstwy jest ewidentna (można dyskutować czy to dobrze czy to źle, ale to głównie estetyka). Niektórzy wolą więc zarządzanie obiektami dziedziny w sposób a'la Grails/GORM:
public class RepositoryInsideDomain {

@Test
public void checkSaveFindDelete () {
Author author = new Author();
author.setName("Pawel");

author.save();
assertTrue(Author.isStored(author));

assertNotNull(Author.findById("Pawel"));
assertEquals(author, Author.findById("Pawel"));

author.delete();
assertFalse(Author.isStored(author));
}
}
Co niestety wymaga albo pobrudzenia obiektów dziedziny dodaniem odpowiednich metod z DAO i delegacji do nich (trywialne, nieutrzymywalne i nieładne, więc nawet nie będę pokazywał, bo ktoś powie że to promuję :)),  albo pobawieniem się aspektami. I to rozwiązanie może być ciekawe, więc je tu pokażę (zrealizowane za pomocą aspectj'a). Do tego potrzebujemy zrobić Repozytorium (tu strywializowane, ale oczywiście może być dowolnie bardziej złożone):
public class Repository {
static Map storage = new HashMap();

public Repository() {
}

public static Object findById (Object id) {
return storage.get(id);
}

public void save () {
storage.put(this.toString(), this);
}

public void delete () {
storage.remove(this.toString());
}

public static boolean isStored (Object o) {
return storage.containsKey(o.toString());
}
}
i jeszcze aspekt który pomiksuje to wszystko razem:
public aspect RepositoryInjector {
declare parents: com.lipinski.domaininjection.domain.* extends Repository;
}
Tak niewiele trzeba, by odpowiednie metody repozytorium były dostępne bezpośrednio z klas dziedziny. Dzięki temu nie musimy wszczepiać nigdzie repozytoriów, operujemy wyłącznie na klasach domenowych. Z drugiej strony na poziomie kodu mamy ładną separację dziedziny od infrastruktury. jedyna wada tego rozwiązania, to że nie jest to pure java, i póki się nie zajrzy do tego aspektu to nijak nie wiadomo skąd te metody się znalazły. Ale jeśli to podejście stosujemy konsekwentnie w całej aplikacji, to nie powinien być problem.

18 mar 2009

Po speechu z DDD

Na wczorajszym speechu z DDD w Warszawkim JUGu zgromadziło się 70 osób, co wg. organizatorów jest dość dużo (mi też się tak wydaje...) To bardzo cieszy, bo wskazuje na to, że jednak może kryzys w oprogramowaniu nie jest aż tak wielki, albo przynajmniej, że jest trochę ludzi, którzy chcą pisać dobry soft. Którym zależy na jakości.

Wszystkim obecnym chciałem podziękować za miłe przyjęcie. Fajnie jest widzieć zainteresowanie i zaangażowanie.

Prezentacja dla chętnych do ponownego obejrzenia dostępna jest na google docs.

Do tego jest dość aktywny wątek poprezentacyjny na stronach JUGa: tu.

16 mar 2009

Manifest Rzemieślnika Oprogramowania


W czasie gdy ja w miarę bezrefleksyjnie oddawałem się swojej drugiej pasji (na obrazku), powstał appendix do manifestu agile. Oryginalny manifest mówił o tym na co w zwinnym podejściu do rozwoju oprogramowania kładziony jest głównie nacisk, a co jest 'tylko' wartościowe. Były to: 
Osoby i Interakcje nad Procesy i Narzędzia
Działające Oprogramowanie nad Szczegółową Dokumentację 
Współpraca z Klientem nad Negocjacjowanie Kontraktu
Odpowiadanie na Zmiany nad Działanie Zgodnie z Planem.

Robert C. Martin już od jakiegoś czasu postulował dodanie jeszcze czegoś, co definiowało by podejście do jakości w projektach/zespołach agile'owych. Sam nazwał to w zeszłym roku 'Craftsmanship over Crap', ale zdaje się, że to jednak nie pasuje (w końcu elementy po prawej miały też przedstawiać jakąś wartość, tyle że mniejszą...) i nie brzmi zbyt poprawnie politycznie. Stąd twórcy appendixu zwanego Manifesto for Software Craftsmanship ubrali różne myśli krążące od pewnego czasu po książkach i blogach agile'owych w ładne słowa i powstało:
Nie tylko Działające Oprogramowanie, ale również Niezła Robota (moje domorosłe tłumaczenie 'well-crafted')
Nie tylko Odpowiadanie na Zmiany, ale również Ciągłe Dodawanie Wartości
Nie tylko Osoby i Interakcje, ale również Społeczność Zawodowców
Nie tylko Współpraca z Klientem, ale również Produktywne Partnerstwa

Manifest mówi, że do osiągnięcia elementów po lewej niezbędne jest istnienie tych po prawej. No więc po kolei:
Żeby oprogramowanie Działało, spełniało swoje zadania i nie wymagało Szczegółowej Dokumentacji, musi być to Niezła Robota - wysokiej jakości oprogramowanie.
Żeby móc odpowiadać na zmiany, trzeba móc ciągle dodawać wartość. A do tego warunkiem sine qua non jest kod łatwo modyfikowalny, o czystej architekturze, zrozumiały.
Osoby odpowiedzialne za projekt tworzą się, kształcą poprzez interakcję z innymi. Z lepszymi od siebie, ale ze słabszymi od siebie również. Do tego niezbędna jest społeczność osób związanych ze sobą zainteresowaniami, pracą, pasją. Stąd potrzeba społeczności profesjonalistów.
W końcu produktywne partnerstwo jako efekt ale i warunek dobrej współpracy. Jeśli firmy szanują się nawzajem, jeśli ufają swojemu profesjonalizmowi i mają jasne, przezroczyste reguły współpracy, takie partnerstwo (czyli równość stron) może być produktywne. Obie firmy mogą osiągnąć swój cel - wzajemne przynoszenie sobie wartości.

Ten manifest, zupełnie jak ten oryginalny, jest idealistyczny. Zakłada dobre chęci, profesjonalizm, budowanie wzajemnego zaufania. To nie środki, ale cel. Coś do czego powinniśmy dążyć całym sobą, ucząc się ciągle i poprawiając. Na początek na naszym małym podwórku jednego projektu, jednego zespołu, jednej aplikacji. Z czasem może uda nam się zarazić tym innych, firmę, społeczność. To dzięki takim właśnie postawom mamy dziś cały ruch agile.

5 mar 2009

Speech z DDD

Tym razem trochę czystej prywaty. W następny wtorek, 17 marca, zapraszam na mój speech w ramach Warszawskiego JUGa. Tematem będzie Domain Driven Design. Oczywiście nie obędzie się bez ewangelizacji zwinnego podejścia do tworzenia projektów, czyli po prostu edżajla.

Kto nie będzie mógł przyjść, będzie mógł obejrzeć sobie slajdy, ale jak mawiała moja nauczycielka Polskiego "Lipiński, lepiej gadasz niż piszesz", więc pogadanka będzie pewnie bardziej wartościowa niż suchy PDF.

2 mar 2009

SOLID - Liskov Substitution Principle

Pisanie naprawdę obiektowego kodu nie jest popularne. Tak naprawdę nie widziałem zbyt wielu projektów pisanych obiektowo. A w javie piszę już 10 lat. W tzw. aplikacjach enterprise nie ma prawie dziedziczenia, nie ma hierarchii. Aplikacje są totalnie płaskie, proceduralne, z niejasną strukturą, z czasem bardzo trudne do utrzymania.

Wartość dziedziczenia przy strukturyzowaniu kodu pokazywałem już w poprzednich postach, w szczególności przy okazji zasady zamknięcia-otwartości. Natomiast zasada o której teraz trochę napiszę dotyczy nie tego jak ma wyglądać struktura, ale co ma być w klasach dziedziczących. Zasada podstawiania Liskov brzmi mniej-więcej tak:
Jeśli twój kod oczekuje jakiejś klasy, to zamiast niej powinieneś móc podstawić dowolną klasę z niej dziedziczącą bez zmieniania żadnych oczekiwanych zachowań.
Oznacza to tyle, że jeśli przeciążamy jakąś metodę w podklasie, to musi ona zachować semantykę odpowiedniej metody nadklasy.

Ta zasada mówi jedną bardzo ważną rzecz: jeśli przeciążasz metodę, napisz ją tak, by użyta polimorficznie działała poprawnie. Wyobraźmy sobie taką sytuację: mamy aplikację składającą się z modułów. Są tam zwykli użytkownicy oraz administratorzy modułów. Mamy użytkownika:


public class User {
protected String login;
protected String password;
protected List modules;

public void addAccessToModule(Module module) {
modules.add(module);
}

public boolean canAccess(Module module) {
if (modules.contains(module))
return true;
return false;
}
}

Mamy też administratora:

public class Admin extends User {
private List administeredModules;

public boolean canAccess(Module module) {
if (administeredModules.contains(module))
return true;
return false;
}
}


Ja widzimy metoda canAccess jest przeciążona w klasie Admin i sprawdza czy dany użytkownik jest administratorem podanego modułu (programista zaimplementował w ten sposób wymaganie, że administrator ma dostęp do wszystkich modułów jako zwykły użytkownik, a jako administrator do pewnego ich podzbioru). Wyobraźmy też sobie, że moduły emitują zdarzenia, na których otrzymywanie użytkownicy mogą się zapisać:

public void subscribeToEvents(User user, Module module) {
if (! user.canAccess(module)) {
user.addAccessToModule(module);
}
module.sendEventsTo(user);
}

Na pierwszy rzut oka nie widać żadnego błędu - jeśli ktoś jeszcze nie ma dostępu do jakiegoś modułu, to jest on mu przyznawany i dopisywany jest on do listy użytkowników notyfikowanych o zdarzeniach. Ale niestety przez to, że programista nie zastosował się do zasady podstawiania, kod ten jest poprawny tylko dla zwykłych użytkowników. Wielokrotne wywoływanie tej metody dla obiektów klasy Admin spowoduje ciągłe dodawanie tych samych modułów do do kolekcji modules klasy User, choć metoda canAccess w klasie Admin w ogóle jej nie sprawdza.

Nie zachowywanie zasady podstawiania Liskov powoduje powstawanie błędów często bardzo trudnych do wykrycia. Błąd jest prawie zawsze w klasie, której w kodzie wołającym w ogóle nie widać - w końcu są to wywołania polimorficzne. Przy bogatszych hierarchiach i duplikacji w kodzie szukanie błędów może się zmienić w wielogodzinną walkę. Co więcej, jeśli nie dbamy o jakość naszego kodu (właściwe hierarchie, duplikacje, właściwe umiejscowienie metod), nawet szczegółowe testy jednostkowe ze 100% pokryciem kodu może nic nie pomóc (w powyższym przykładzie wystarczy, by był jeszcze jakiś inny sposób na dodanie modułu do kolekcji modules, by testy jednostkowe nie wychwyciły tego problemu).

24 lut 2009

SOLID - Open-Closed Principle

W oryginalnej wersji zasada otwartości-zamknięcia brzmi tak: Elementy oprogramowania (klasy, moduły, funkcje) powinny być otwarte na rozszerzanie ale zamknięte na zmiany.

Hm, zwykle gdy chcemy dodać jakąś funkcjonalność po prostu dopisujemy odpowiedni kod. Weźmy przykład:

public class User {
private String login;
private String password;

private UserRepository repo;

public void delete () {
repo.delete(this);
}

public User findByName(String name) {
return repo.findByName(name);
}
}

public class UserRepository {
private EntityManager em;

public void delete(User user) {
em.remove(user);
}

public User findByName(String name) {
return (User) em.createQuery("from User u where u.name=:name")
.setParameter("name", name)
.getSingleResult();
}
}

Klasy proste, śliczne (oczywiście troszkę uproszczone :)). I nagle przychodzi wymaganie: użytkownik który ma uprawnienia administratora ma mieć flagę deleted która oznacza, że tak naprawdę nie ma być usuwany, tylko zaznaczony jako usunięty i nie prezentowany już potem w GUI. No to pierwsze podejście pewnie będzie takie:

public class User {
private String login;
private String password;
private boolean admin;
private boolean deleted;

private UserRepository repo;

public void delete () {
repo.delete(this);
}

public User findByName(String name) {
return repo.findByName(name);
}
}

public class UserRepository {
private EntityManager em;

public User findByName(String name) {
return (User) em.createQuery("from User u where u.name=:name and deleted=false")
.setParameter("name", name)
.getSingleResult();
}

public void delete(User user) {
if (user.isAdmin()) {
user.setDeleted(true);
em.persist(user);
} else {
em.remove(user);
}
}
}

No, gotowe! Tylko, czy to jest dobry design? Już if w metodzie usuwającej wskazuje, że dzieje się tam więcej niż jedna rzecz. Co więcej, zasada pojedynczej odpowiedzialności dużo nam tu nie pomoże, możemy co jakwyżej osiągnąć coś takiego:

public class User {
private String login;
private String password;
private boolean admin;
private boolean deleted;

private UserRepository repo;

public void delete () {
if (! admin)
repo.deleteNonAdmin(this);
else {
repo.deleteAdmin(this);
}
}

public User findByName(String name) {
return repo.findByName(name);
}
}

public class UserRepository {
private EntityManager em;

public User findByName(String name) {
return (User) em.createQuery("from User u where u.name=:name and deleted=false")
.setParameter("name", name)
.getSingleResult();
}

public void deleteNonAdmin(User user) {
em.remove(user);
}

public void deleteAdmin(User user) {
user.setDeleted(true);
em.persist(user);
}
}

Zwiększyliśmy trochę ilość kodu (twój manager już jest zadowolony - kolejna wskaźnik postępu zwiększony!), może trochę lepiej widać na czym polega różnica między usuwaniem administratora i nie-administratora, ale tak naprawdę kod wiele się nie poprawił. Jeśli będą dochodziły kolejne wymagania jak to, nasza metoda delete zmieni się wkrótce w wielki ciąg if-else. I tu właśnie jest miejsce do zastosowania zasady otwartości-zamknięcia. 
Kod powinien być otwarty na rozszerzanie a zamknięty na modyfikację.


Co to znaczy w praktyce?
Dziedziczenie.

Jeśli przypomnimy sobie, że w językach obiektowych możemy dziedziczyć, okaże się, że ten kod jest jeszcze do uratowania :)

public class User {
protected String login;
protected String password;

protected UserRepository repo;

public User findByName(String name) {
return repo.findByName(name);
}

public void delete() {
repo.delete(this);
}
}

public class AdminUser extends User {
boolean admin;
private boolean deleted;
}


public class UserRepository {
protected EntityManager em;

public User findByName(String name) {
return (User) em.createQuery("from User u where u.name=:name")
.setParameter("name", name).getSingleResult();
}

public void delete(User user) {
em.remove(user);
}
}

public class AdminUserRepository extends UserRepository {
public User findByName(String name) {
return (User) em.createQuery("from User u where u.name=:name and deleted=false")
.setParameter("name", name)
.getSingleResult();
}

public void delete(AdminUser user) {
user.setDeleted(true);
em.persist(user);
}
}

Teraz if-else w ogóle nie istnieje. Kod wołający metody findByName i delete dalej nie musi wiedzieć, że dana instancja klasy User jest administratorem, ale nowa struktura kodu wyeliminowała potrzebę tworzenia warunków (zwykle było by ich więcej niż tylko w jednym miejscu, więc jeśli w kodzie masz gdzieś ten sam warunek sprawdzany parę razy w różnych metodach, to prawdopodobnie powinieneś zastanowić się nad dziedziczeniem). Kod jest teraz dużo bardziej przejrzysty (np. od razu wiadomo gdzie szukać jeśli coś jest nie tak z administratorem). 

Ta zasada wydaje się zupełnie oczywista. Nie trzeba doktoratu z informatyki, żeby wpaść na takie rozwiązanie. Jednak jakże często dodajemy funkcjonalności do kodu bez większego zastanowienia. Po prostu dodajemy kod. Pilnowanie zasady otwartości-zamknięcia jest kolejnym sposobem na nie zwiększanie długu technicznego (nie pogorszanie jakości kodu) wraz z dodawaniem funkcjonalności.

Pisanie i projektowanie aplikacji poprzez stosowanie TDD powoduje, że czas na zastanowienie się nad najlepszym rozwiązaniem staje się nieodłączną częścią procesu rozwoju oprogramowania wykonywaną z resztą co parę minut, co oczywiście ma bezpośredni wpływ na jakość kodu który po sobie pozostawiamy. Istnienie testów powoduje, że nie mamy obaw związanych z wprowadzaniem większych zmian do oprogramowania - np. tak jak w powyższym przykładzie z wymianą warunków na dziedziczenie.

Od tego w jaki sposób wprowadzamy rozszerzenia do naszego projektu zależy bardzo dużo. Czas developmentu, ryzyko błędu, może nawet w końcu przepisywanie projektu. Wszystko to sprowadza się do marnowania dużych ilości pieniędzy naszego pracodawcy / klienta, może nawet osłabić jego konkurencyjność, a przecież nikt nie dał nam do tego prawa. I jeśli Twój project manager czy klient ciśnie Cię terminami, to może zamiast bezwolnie poddawać się tej presji lepiej dokładnie mu to wytłumaczyć. To nie jest Twoja rola, żeby podejmować decyzje o stałym obniżaniu jakości kodu przez byle jakie wrzucanie nowych linii do istniejącego kodu, więc zrzuć to na osoby które takie decyzje są władne podjąć. Niech twój PM podpisze się własną krwią pod zdaniem: 
Tak, chcę doprowadzić do obniżenia jakości oprogramowania. Bardziej niż na jakości zależy mi na terminach. Jestem skłonny raczej w przyszłości mieć ciągłe problemy z ukańczaniem funkcjonalności i błędami niż zmienić termin tego releasu.
Jeśli to zrobi (co niestety się zdarza), przynajmniej będziesz miał czyste sumienie. I nikt nie zarzuci ci braku profesjonalizmu i nieodpowiedzialności.

19 lut 2009

SOLID - SRP część 2 - klasy

Ostatnio pisałem o Regule Pojedynczej Odpowiedzialności skupiając się na metodach. Ale ta reguła dotyczy w równym stopniu strukturyzowania klas. Tym razem będzie mniej kodu, bo skupię się tylko na sygnaturach pokazując o co chodzi.

Reguła Pojedynczej Odpowiedzialności brzmi w tym przypadku tak: powinien być dokładnie jeden powód do dokonywania zmian w klasie. Oznacza to, że kod powinien mieć taką strukturę, żeby każda klasa miała dokładnie jedną odpowiedzialność: np. wyświetlanie komponentu, komunikacja z bazą danych, logika biznesowa.

Najbardziej klasycznym przykładem zastosowania tej reguły jest wzorzec MVC. Oddzielenie od siebie danych, mechanizmu wyświetlania i obsługa operacji UI.

To przecież jasne! Każdy to wie, nie?

No to zobaczmy czy napewno. Mamy sobie taką klasę:

@Stateless
@Local
public class UserManagerBean {

@PersistenceContext(type = PersistenceContextType.TRANSACTION)
private EntityManager em;
private LoginManager loginManager;

public User findUserByName(String name) {
return (User) em.createQuery("from User u where u.name=:name")
.setParameter("name", name).getSingleResult();
}

public boolean isUserValid(User user) {
if (user.getNumWrongLogins() > 3
&& (System.currentTimeMillis() - user.getLastLoginTrial() < 5000))
user.block();

if (user.isBlocked() || user.isAccountExpired())
return false;
return true;
}

public String login(User user) {
boolean result = loginManager.login(user.getName(), user.getPassword());
if (result)
return "You logged in successfully!";
return "Logging in failed";
}
}

Ten kod na pierwszy rzut oka wygląda ok. Gdyby to był poprzedni wpis, przyczepiłbym się tylko metody isUserValid, bo robi dwie rzeczy zamiast jednej (weryfikuje czy blokować Usera i ew. blokuje, oraz sprawdza czy User może się logować). Ale z punktu widzenia Reguły Pojedynczej Odpowiedzialności klas ten kod jest zupełnie do niczego... a dokładniej to do wszystkiego :)  (jak z resztą większość EJB na tym świecie, ponieważ ten model promuje takie programowanie). Ta klasa w jednej metodzie komunikuje się z bazą danych, w drugiej zawiera logikę, w trzeciej zwraca tekst do wyświetlenia w UI. Brrrrr. Straszne.

W przykładzie klasy, która ma 35 linii może jeszcze to tak nie kłuje w oczy, ale taka klasa mogła by mieć 30 różnych metod i wtedy przestaje być jasne za co taka klasa jest odpowiedzialna, co gdzie należy zmienić jeśli chce się wprowadzić nową funkcjonalność lub zmodyfikować istniejącą. Przy dużej liczbie takich komponentów aplikacja przestaje być utrzymywalna. Odpowiedzialności mieszają się ze sobą, obiekty wołają nawzajem różne swoje metody tworząc sieć zależności i oczekiwań. W w dużych aplikacjach ze skomplikowanymi procesami brak poprawnego i dokładnego rozdzielenia odpowiedzialności powoduje, że zmiany w jednej metodzie powodują często powstawanie błędów w wielu innych miejscach aplikacji.

Jak więc należało by ten problem rozwiązać? Ja proponowałbym kod bazodanowy wyprowadzić do odpowiedniego modułu/warstwy (DAO, repozytorium, etc.) a dostęp do niego zrealizować np. jako metodę samego obiektu User (np. a'la Grails'owy GORM: User.findByName(name)). Weryfikacja poprawności użytkownika albo do obiektu walidacji, albo do klasy User (jako User.isValid()). Kod zwracający treść do wyświetlenia przerzucić na stronę wyświetlającą (albo jako tag czy fragment) a samo wywołanie logowania (linia wołająca obiekt loginManager) jest już odpowiednie dla kontrolera UI. 
I w ten sposób... nie ma już więcej UserManagerBean'a :) (tak, to taki sprytny sposób podprogowego przekazania myśli, że EJB nie powinny być wykorzystywane do metod które nie korzystają bezpośrednio z transakcyjności, bezpieczeństwa czy remotingu)
Jeśli będzie zmiana w strukturze danych w bazie albo zmieni się wymaganie co do zapytania zwracającego użytkownika, jest jasne gdzie tego szukać - w klasie reprezentującej repozytorium obiektów User. Jeśli będziemy chcieli dodać i18n, będzie to w miejscu odpowiedzialnym za wyświetlanie. Jeśli będzie trzeba zwiększyć dostępną liczbę nieudanych prób logowania Użytkownika, to też łatwo będzie ustalić gdzie jest odpowiedni kod.

Przy definiowaniu przynależności metod do klas można wykorzystać heurystykę częstotliwości odwołań do różnych klas w ramach metody. Jeśli w jakiejś metodzie większość wywołań metod dotyczy jednej klasy, to z dużym prawdopodobieństwem ta metoda powinna w tej klasie się znaleźć (zmniejszając ilość kodu o odwołania do obiektu i poprawiając jego logiczne rozłożenie).
Jeśli dbamy o to, by metody były krótkie, tych odwołań nie będzie dużo i łatwiej o dobry (sprawny) design, stąd jeszcze jeden powód do pilnowania pojedynczej odpowiedzialności metod. W ten sposób dużo łatwiej jest osiągnąć Święty Graal projektowania obiektowego: High Cohesion with Loose Coupling

Choć w sumie Zasada Pojedynczej Odpowiedzialności dotyczy zupełnie czego innego w przypadku metod niż w przypadku klas, wykorzystywanie jej w obu (rozdzielone małe metody o jednej odpowiedzialności umieszczone w odpowiednich klasach) ma niebagatelny wpływ na jakość naszego kodu a więc na łatwość jego zrozumienia przez innych oraz jego utrzymywalność. 

13 lut 2009

SOLID - Single Responsibility Principle - część 1 - metody

Pisałem już wcześniej o zasadach SOLID, ale pomyślałem, że tak blisko oddają one ducha zwinności designu, że przejdę przez nie po kolei, pokazując jak można zmienić kod napisany przeciętnie w kod napisany dobrze. Co więcej takie zmiany poprawiają czytelność (a więc jakość) kodu ogromnie, a kosztują zwykle bardzo mało. Ubocznym efektem jest łatwiejsze wykrywanie błędów logicznych i duplikowania kodu.

Pierwsza na tapecie jest zasada pojedynczej odpowiedzialności. Jedna metoda odpowiedzialna za jedną funkcję. Jedna klasa reprezentuje jedną rzeczywistość (nie znalazłem lepszego słowa...)
Jako przykład wziąłem metodę z jednego z projektów w których kiedyś brałem udział (zastanawiałem się nad napisaniem kawałka kodu specjalnie pod ten tekst, ale takie przypadki są zwykle nierzeczywiste, więc wziąłem coś co jest z prawdziwego projektu). System realizował workflow, w którym każdy element mógł być na jednym z paru etapów (klasa Stage w kodzie). Ta metoda służyła do przejścia do kolejnego etapu. W czasie aktywowania jednego etapu trzeba było zdezaktywować poprzednio aktywny, ustawić datę aktywowania, deadline dla jego zakończenia, komentarz itp. A oto i kod oryginalnej metody, która to robiła:

/** Activates stage
* SAVES TO DB
*/
void activateStageDeactivatingCurrentlyActive(Stage forActivation)
{
Assert.assertNotNull(forActivation);
Stage forDeactivation = null;

if (forActivation.getDirection() == Stage.IN_DIRECTION)
forDeactivation = findActiveStageIn();
else if (forActivation.getDirection() == Stage.OUT_DIRECTION)
forDeactivation = findActiveStageOut();
else
throw new IllegalStateException("Please supply proper direction");

//if activated stage is not null and is not the same as previous active one
if (!forActivation.sameAs(forDeactivation)){
if (forDeactivation != null) {
forDeactivation.deactivate();
if (forActivation.isRemoved()) {
forActivation.setComment(forDeactivation.getId().toString());
}
}

// fix: #372 update start_date,
// set only when planed started was not set
if (forActivation.getStartDate() == null) {
if (forDeactivation == null){ //when no active stage present before set sysdate
forActivation.setStartDate(
forActivation.getDeadline() == null ? new Date() : forActivation.getDeadline()
);
} else {
forActivation.setStartDate(forDeactivation.getDeadline());
}
}

if (null != forActivation) {
forActivation.activate();
}
}
}

Pierwszy podstawowy warunek dobrego kodu spełniony - mieści się na jednej stronie ;) Generalnie ta metoda nie jest jakaś strasznie długa (na pewno każdy pisał w życiu dłuższe), a pewnie wielu pomyśli, że jest nawet całkiem ok pod względem zawartości i czytelności. Ale ja sądzę, że ta metoda robi jednak o parę rzeczy za dużo, co więcej trudno powiedzieć na pierwszy rzut oka co robi, więc po kolei postaram się doprowadzić ten kod do porządku.

Najpierw usunę nic nie mówiący komentarz i zmienię nazwę metody na samo activateStage. W klasie Stage można napisać, że możliwa jest aktywność tylko jednego, więc w nazwie metody to nie jest potrzebne. Następnie usuwam weryfikację, czy parametr metody nie jest null'em. Takie coś jest na pewno w paru miejscach w kodzie, więc zrobię aspekt, który nie dopuści w ogóle do wywołania tej metody z nullowym parametrem (w ogóle bardzo się zdziwiłem, że w produkcyjnym kodzie jest takie odwołanie do junita...) Moja metoda wygląda więc teraz tak:


void activateStage(Stage forActivation) {
Stage forDeactivation = null;

if (forActivation.getDirection() == Stage.IN_DIRECTION)
forDeactivation = findActiveStageIn();
else if (forActivation.getDirection() == Stage.OUT_DIRECTION)
forDeactivation = findActiveStageOut();
else
throw new IllegalStateException("Please supply proper direction");
.......

Aspekt tu ominę, bo nie o niego tu chodzi. Ma być before execution i tam pewnie wyjątek jeśli null.

Teraz cały fragment odpowiedzialny za wybranie ostatnio aktywnego etapu wywalam do oddzielnej metody (findPreviouslyActiveStage), co mi daje:

void activateStage(Stage forActivation) {
Stage forDeactivation = findPreviouslyActiveStage(forActivation);

//if activated stage is not null and is not the same as previous active one
if (!forActivation.sameAs(forDeactivation)){
if (forDeactivation != null) {
......


Teraz zmieniam warunek otaczający całą resztę tej metody na odwrotny (bez negacji, za to wcześniejszy return), co daje taki kod:

void activateStage(Stage forActivation) {
Stage forDeactivation = findPreviouslyActiveStage(forActivation);
if (forDeactivation.sameAs(forActivation))
return;

if (forDeactivation != null) {
......


Następny etap to ekstrakcja do nowej metody (deactivateStage) fragmentu odpowiedzialnego za deaktywację poprzedniego etapu:

void activateStage(Stage forActivation) {
Stage forDeactivation = findPreviouslyActiveStage(forActivation);
if (forDeactivation.sameAs(forActivation))
return;
deactivateStage(forActivation, forDeactivation);

// fix: #372 update start_date,
// set only when planed started was not set
if (forActivation.getStartDate() == null) {
......

Kolejny etap to ustawienie daty początkowej nowego etapu (jeśli nie była ona ustawiona wcześniej), które również zasługuje
na własną metodę:

void activateStage(Stage forActivation) {
Stage forDeactivation = findPreviouslyActiveStage(forActivation);
if (forDeactivation.sameAs(forActivation))
return;
deactivateStage(forActivation, forDeactivation);
updateStartDate(forActivation, forDeactivation);

if (null != forActivation) {
forActivation.activate();
}
}

No i teraz łatwo mi się zorientować, że forActivation nie może nigdy być null, więc mogę opuścić tę weryfikację przy aktywacji, co daje ostateczną wersję metody:

public void activateStage(Stage forActivation) {
Stage forDeactivation = findPreviouslyActiveStage(forActivation);
if (forDeactivation.equals(forActivation))
return;
deactivateStage(forActivation, forDeactivation);
updateStartDate(forActivation, forDeactivation);
forActivation.activate();
}

No, teraz widać co ta metoda robi. Przede wszystkim sama robi tylko jedną rzecz - aktywuje nowy etap. Cała reszta jest wyciagnięta na do innych metod. Komentarze też nie są tu potrzebne, bo widać co sie dzieje.
Również metody wykorzystywane przez nią zostały poprawione i każda jest odpowiedzialna tylko za jedną rzecz i ew. deleguje inne dalej (już nie będę męczył tu całym refactoringiem...):

private Stage findPreviouslyActiveStage(Stage forActivation) {
switch(forActivation.getDirection()) {
case Stage.IN_DIRECTION:
return findActiveStageIn();
case Stage.OUT_DIRECTION:
return findActiveStageOut();
default:
throw new IllegalStateException("Please supply proper direction");
}
}

private void deactivateStage(Stage forActivation, Stage forDeactivation) {
if (forDeactivation == null)
return;
forDeactivation.deactivate();
setCommentIfStageRemoved(forActivation, forDeactivation);
}

private void setCommentIfStageRemoved(Stage forActivation, Stage forDeactivation) {
if (forActivation.isRemoved())
forActivation.setComment(forDeactivation.getId().toString());
}

private void updateStartDate(Stage forActivation, Stage forDeactivation) {
if (forActivation.getStartDate() != null)
return;

if (forDeactivation != null) {
forActivation.setStartDate(forDeactivation.getDeadline());
return;
}

forActivation.setStartDate(
forActivation.getDeadline() == null ? new Date() : forActivation.getDeadline()
);
}

Pewnie część ludzi woli i tak zapis oryginalny metody :) Do czytania to on może jest lepszy (choć to kwestia opatrzenia się z tak pisanym kodem, którego wszędzie jest pełno), ale zrozumienie tego co robi kod jest bez wątpienia łatwiejsze w zapisie zrefaktoryzowanym. Wystarczy popisać tak parę dni i od razu nawyki się zmieniają. 
Gdy szukamy konkretnego miejsca w kodzie (np. dlatego, że tam jest błąd), łatwiej jest poruszać się po hiearchii małych jednozadaniowych metod, szczególnie jeśli mają łatwo zrozumiałe nazwy.

Ten kod nie jest jeszcze do końca czysty. Trzeba by pewnie pozmieniać parę nazw zmiennych i może przeorganizować logikę, tak by wszystko było na swoim miejscu, ale to już nie jest kwestia pojedynczych odpowiedzialności metod, więc to sobie w tym przykładzie odpuszczam.

Dbanie o zapewnianie pojedynczej odpowiedzialności (a więc unikanie pisania zbyt wiele w jednej metodzie) jest pewnym nawykiem, który trzeba w sobie wyrobić - tak jak unikanie copy-paste, czy za dużo if-else. Oczywiście pisać byle jak jest łatwiej i jasne, że to jest kuszące. Szczególnie gdy koniec projektu blisko, inni nie dbają, stary kodu jest do niczego...  Ale to właśnie te małe wysiłki powodują, że z każdym dniem możesz stać się lepszy w swoim zawodzie. Że to ty możesz zmienić swój projekt, zawstydzić swój zespół, wzbudzić zdrową zazdrość.

Jakość nie ma znaczenia

Ostatnio wśród speców od programowania rozgorzała dyskusja nad jakością kodu/designu versus czas rozwijania.  Rozpoczął ją Joel Spolsky wypuszczając podcast w którym pada zdanie: 
And I find, sadly, to be completely honest with everybody listening, quality really doesn't matter that much, in the big scheme of things...
W tym samym podcast'cie Joel mówi tak:
Last week I was listening to a podcast on Hanselminutes, with Robert Martin talking about the SOLID principles. (...)It's object-oriented design, and they're calling it agile design, which it really, really isn't. It's principles for how to design your classes, and how they should work. And, when I was listening to them, they all sounded to me like extremely bureaucratic programming that came from the mind of somebody that has not written a lot of code, frankly.
No więc co znaczy agile w kontekście oprogramowania? Mniej więcej to samo co w kontekście prowadzenia projektów: reagowanie na zmiany. I z tego punktu widzenia zasady SOLID (o których było parę postów temu) są bardzo zwinne, bo pozwalają z dużo większą łatwością dodawać nowe funkcje do systemu i modyfikować istniejące. Nie bardzo wiem z czym można tu dyskutować. Programowanie które nie jest w tym sensie "biurokratyczne" to programowanie chaotyczne. 
A mówienie o Robercie C. Martinie, Martinie Fowlerze, Kencie Becku i innych ludziach stojących za zwinnością w sensie technologicznym, że nie napisali wiele kodu jest po prostu żenujące.


Pierwszy cytat dotyczący jakości to jasny dowód na to, że jego autor jest PMem...  Dba o TEN projekt, o TĘ kasę, o TE terminy. Dług techniczny zaciągnięty w tym czasie nie jest dla niego ważny, bo przecież później się to posprząta.  Z resztą ten cytat mówi sam za siebie:
The way real software works is that you create these very imperfect things, and they work great. They really do. And then you have a little problem, and you go and you fix the little problem, because it's code, and you have an editor, and you edit it. 
Taaak. Przykładów oprogramowania, które works great jest faktycznie wiele... 
Dług techniczny nazbierany przez lata powoduje, że właśnie nie da się po prostu otworzyć edytora i poprawić kodu. Z resztą zwrot little problem też pokazuje nastawienie pana Spolsky'ego. No ale on robi soft do rejestrowania błędów, więc zależy mu na tym, żeby było ich jak najwięcej - wtedy więcej kopii sprzeda :)

Straszne, że ktoś tak wpływowy w świecie oprogramowania pisze takie rzeczy.


Takie krótkowzroczne spojrzenie jest ze stratą dla wszystkich. Programiści się frustrują i nie chcą potem pracować z takim kodem. Klient dostaje coraz dłuższe terminy (a więc i większe kwoty), albo te co są są niedotrzymywane. PM ma zmarnowane życie prywatne i wrzody żołądka (choć na to to akurat sobie zasłużył :)). 
Jedną z podstawowych wartości ruchu agile jest transparentność i szczerość. I dotyczy to procesów (wiadomo kto, co i kiedy robi), efektów (ukończonych zadań), kosztów. Ale mało kto mówi, że dotyczy to również kodu. I to nie tylko w zespole programistów. Kto informuje swojego klienta o powstającym na skutek jego presji długu technicznym? Kto mówi, że żeby robić release'y na czas wprowadził do systemu taki shit, że robienie nowych funkcjonalności zajmie 20% czasu więcej (a więc 20% więcej będzie kosztować, co może przekona klienta)?
Odpowiedzialny zaspół zgłasza swojemu Product Owner'owi (a ten klientowi) informacje o wpływie jego decyzji na jakość. Jakość jest wartością biznesową a nie techniczną i jako taka powinna być uświadamiana klientowi. Jeśli twój PM nie pozwala Ci dbać odpowiednio o jakość (ciśnie terminami, siedzisz po godzinach, nie testujesz kodu itp.) to działa na szkodę klienta (to on ostatecznie dostanie shit) i na szkodę Twojej firmy. Warto więc uświadamiać w tym zakresie osoby niezaangażowane bezpośrednio w projekt, a mogące podejmować decyzje.

6 lut 2009

Ekstremalna obiektowość

Kto mnie zna, wie, że jestem stuknięty jeśli chodzi o obiektowość. Tak od paru lat. Z każdym projektem w którym uczestniczę (ew. który przeglądam...) coraz bardziej. Od czasu kiedy stworzyłem parę lat temu klasę-potwora - półtoratysiąca linii, z których z 500 było w jednej metodzie i nie dane mi było tej Augiaszowej stajni posprzątać, z wszystkich rzeczy w programowaniu dbam najbardziej o zachowywanie zasady pojedynczej odpowiedzialności. Jeśli miewam w nocy jakieś koszmary, to dotyczą one zawsze refactoringu tej klasy :)

W związku z tym moim małym zboczeniem, z wielką ciekawością przeczytałem (parokrotnie) jeden z rozdziałów w książce, którą aktualnie mam na tapecie - The Thoughtworks Anthology. Rozdział nosi tytuł Object Calisthenics (można go gdzieś w necie znaleźć jako .doc). Jeff Bay proponuje w nim 9 reguł dotyczących tworzenia kodu (przykłady są w javie, ale większość reguł odnosić się może do w miarę dowolnego języka, szczególnie OO). Są to
  • Tylko jeden poziom zagłębienia na metodę
  • Nie używaj słowa kluczowego else
  • Opakowuj wszystkie prymitywy i Stringi (w klasy o specyficznej dla zastosowania nazwie)
  • Używaj tylko jednej kropki na linię
  • Nie skracaj nazw
  • Pilnuj wszystkie encje by były małe
  • Nie używaj klas o więcej niż dwóch polach
  • Klasa której polem jest kolekcja nie powinna mieć żadnych innych pól (opakowywanie kolekcji w klasy specyficzne dla kontekstu wykorzystania)
  • Nie używaj getterów/setterów/własności
Wow - pomyślałem. To jak tu w ogóle pisać? Ale Mr. Bay jest w swoim tłumaczeniu dość przekonujący. Wolałbym co prawda więcej przykładów w artykule, ale już samo przedstawione przez niego rationale przekonuje mnie do większości z tych stwierdzeń. Co więcej, większość z nich i tak bardzo często stosuję (opakowywanie kolekcji i prymitywów, jeden poziom zagłębienia, nieskracanie nazw), ale autor przekonuje, żeby stosować te reguły wręcz fanatycznie. 

Parę rzeczy jest pewnych: 
  • taki kod jest bardziej obiektowy (podział odpowiedzialności, właściwe opakowywanie, itp.)
  • taki kod jest bardziej testowalny (małe metody, proste testy - taki design sam się prosi o TDD)
  • taki kod jest bardziej reużywalny (łatwiej znaleźć wiele zastosowań dla małej metody o dobrze określonej odpowiedzialności niż dla metody, która robi pięć rzeczy)
  • taki kod sam się dokumentuje (krótkie jednoznaczne nazwy metod powodują, że kod się czyta prawie jak zwykły tekst)
  • trudniej zrobić a łatwiej wykryć błąd w metodzie o 5 liniach niż w metodzie o 500 (to akurat wiem dobrze)
Może więc jednak warto naciskać na obiektowość i doprowadzać ją do ekstremum. 

Mimo, że generalnie artykuł Jeffa Bay'a jest bardzo blisko moich własnych praktyk programistycznych, wydaje mi się on zbyt ekstremalny. Póki co do niektórych reguł (a raczej ich fanatycznego stosowania) jestem trochę sceptycznie nastawiony, szczególnie jesli wziąć pod uwagę, że nasz kod zależy czasem od zewnętrznych frameworków/bibliotek które nie pozwalają nam na pełną elastyczność (jak na przykład pogodzić takie rozdrobnienie, brak getterów i setterów itp. z np. mapowaniem hibernate'owym). 
Tak czy siak autor zachęca do zrobienia ćwiczenia: 1000 linii kodu napisanego trzymając się tych reguł. A że w poniedziałek i wtorek mam szkolenie poza Warszawą, przynajmniej połowę z tego wyrobię :)