blog.testowka.pl

CI

Coding Dojo – TDD Kata

opublikowany przez 31, Mar, 2014, w kategoriach Agile, Automatyzacja, CI, TDD, XP

Kanji-shu-ha-ri507
Kilka tygodni temu miałem przyjemność poprowadzić dla jednego z moich klientów krótki warsztat w formie Coding Dojo. Na codzień z tymi zespołami pracujemy nad wdrożeniem Continuous Delivery oraz innych praktyk związanych z Agile. Dojo nie było częścią regularnego Agile Coachingu, ale pomyśleliśmy, że może być dobrą formą oderwania się od codziennej pracy, wyjścia ze strefy komfortu i nauczenia się czegoś nowego. I tak też chyba się stało.

Po pierwsze czym jest Coding Dojo?

Dojo to termin pochodzący z języka japońskiego oznaczający „miejsce treningu”. Odnosił się on do trenowania sztuk walki takich jak Kendo czy Aikido. Nie jest to zwykła sala treningowa, gdyż obowiązują na niej pewne zasady, wchodząc do Dojo zgadzamy się postępować zgodnie z obowiązującymi tam normami.

Coding Dojo to również miejsce gdzie chcemy potrenować nie tyle sztuki walki co sztuki tworzenia kodu. Tutaj też obowiązują pewne zasady, o których za chwilę…

Podczas Coding Dojo zgromadzeni programiści próbują rozwiązać określony problem. W naszym przypadku podeszliśmy do problemu „String Calculator” – prosty kalkulator obliczający sumę parametrów podanych na wejściu. Backlog do tego zadania udostępniłem tutaj.

Zasady

Dojo zorganizowaliśmy w formie TDD Kata oraz podzieliliśmy je na 4 iteracje. Każda iteracja to 30 minut kodowania, 6 minut na code review i 6 minut na wspólną retrospekcję.

Uczestnicy programowali w parach. Po każdej iteracji zmiana pary, poniżej więcej na ten temat.

W przeciwieństwie do typowego Code Retreat po każdej iteracji nie usuwaliśmy wytworzonego kodu tylko kontynuowaliśmy poprzednio zaczętą pracę.

Przebieg Coding Dojo

Pierwsza iteracja to dla większości uczestników pierwsze kroki w TDD (niektórzy mieli już pewną wprawę, niemniej jednak przypomnienie zasad każdemu się przydało) – programujemy zgodnie z zasadą Red -> Green -> Refactor. Co 3 minuty zmienia się osoba przy klawiaturze – to wymuszało komunikację. TDD Kata to ćwiczenie służące do doskonalenia swoich umiejętności.

Po blisko pół godziny tworzenia kodu przyszedł czas na Code Review, ze względu na to, że było około 20 osób postanowiliśmy, że w ramach Code Review wymieszamy pary, praca każdej pary musiała zostać przejrzana przez kogoś z innej pary. Ta praktyka pozwoliła bardzo szybko pokazać różnice pomiędzy parami i poszczególnymi uczestnikami, a następnie pozwoliła wymienić się zdobytą wiedzą i spostrzeżeniami.

I oczywiście wspólna retrospekcja, podczas której rozwiewaliśmy wszelkie wątpliwości i omawialiśmy problemy oraz spostrzeżenia.

W drugiej iteracji wprowadziłem dwie zmiany – po pierwsze postanowiliśmy stosować TDD ping-pong. Jest to metoda programowania w parze polegająca na tym, że pierwsza osoba pisze test, druga pisze funkcjonalność spełniającą ten test, pierwsza refaktoryzuje, druga pisze test itd. To ćwiczenie pozwoliło uczestnikom lepiej dostrzec różnice pomiędzy poszczególnymi etapami w pętli TDD, oraz to czym się różni TDD od Test-First.

Drugą zmianą (nie do końca w mojej ocenie udaną, powinienem zostawić to na trzecią iterację) był zakaz rozmawiania podczas kodowania. Znacznie lepiej było by, gdybyśmy wprowadzili to w następnej iteracji, po tym jak już uczestnicy opanowali podstawy pętli TDD. Celem wprowadzenia tej zasady było wymuszenie pisania na tyle czytelnego kodu by druga osoba mogła łatwo zrozumieć intencje. Niemniej jednak było to całkiem zabawne nawet pomimo tego, że jeszcze nie wszyscy zrozumieli dobrze zasady TDD w tej iteracji. Braki nadrobiliśmy w kolejnej.

Trzecia iteracja po Code Review i Retrospekcji była podobna do poprzedniej z tą różnicą, że teraz można było już rozmawiać. To zaowocowało ożywionymi dyskusjami, oraz dzieleniem się pomysłami, które nagromadziły się w poprzedniej iteracji, oraz podczas Code Review. W efekcie było dużo refactoringu.

Iteracja czwarta – „porzuć swój kod”. Ponieważ w życiu programisty bardzo często bywa tak, że musi grzebać się w kodzie, którego sam nie tworzył postanowiłem podczas naszego Coding Dojo również zrobić taki eksperyment. Tym razem zasada przy dobieraniu się w pary była prosta – wszyscy wstają od komputerów przy których siedzą i starają się dobrać w pary z kimś z kim jeszcze nie programowali oraz wspólnie usiąść przy komputerze przy którym jeszcze nie siedzieli.

Była to chyba jeden z najciekawszych eksperymentów jaki przeprowadzałem na ludziach (i ich kodzie) ;-). To niesamowite jak bardzo różnie można zaimplementować te same funkcjonalności oraz na jak wiele różnych pomysłów można wpaść w zależności od tego z kim się pracuje. Podczas tej iteracji niektórzy zawzięcie refaktoryzowali (pamiętając o zasadach TDD) inni starali się dodawać nowe funkcjonalności przyjmując zastane konwencje.

W ramach przedłużonego Code Review po tej iteracji poprosiłem by wszyscy wracali do wszystkich komputerów, przy których mieli okazję wcześniej pracować i dokładnie przejrzeli co stało się z „ich” kodem odkąd opuścili to miejsce (nie tylko w ostatniej iteracji ale także w poprzednich).

Wnioski

Pojawiło się kilka ciekawych pytań i wniosków oto kilka z nich:

„Tworząc kod w ten sposób widzę, że implementując w sumie prosty problem poprzez stosowanie TDD wydłużył się czas implementacji oraz tak na prawdę spadła jakość tego co zrobiłem – gdybym nie stosował TDD to zrobił bym to szybciej i lepiej”. 

Moja odpowiedź brzmiała: Tak! Gdybyś dzisiaj to zaimplementował bez TDD to zrobił byś to ładniej (i może nawet napisał byś testy). Niemniej jednak w TDD Kata chodzi właśnie o dążenie do perfekcji i doskonalenie swoich umiejętności. Tego typu ćwiczenia wykonujemy właśnie po to, by za którymś razem napisać ten kod jeszcze lepiej i szybciej używając TDD, gdyż ta metoda ma wiele innych zalet, które warte są poświęcenia czasu potrzebnego na jej dobre opanowanie.

„TDD jest proste, gdy tworzymy nową funkcjonalność – a co jeśli mam już sporo kodu i to nawet z testami i nagle przychodzi zmiana wymagań. Co jeśli mam na przykład 5000 testów? Mam teraz szukać i zmieniać wszystkie testy zanim wprowadzę zmiany?”

Moja odpowiedź: jeśli wiesz, które testy trzeba zmienić i możesz to w miarę prosto i szybko zrobić to to zrób. Jeśli nie wiesz to… napisz nowy test, wprowadź testowaną zmianę w zachowaniu aplikacji, odpal testy i zobacz które testy przestały przechodzić. Potem zastanów się czy testy, które przestały przechodzić zrobiły to. bo zmieniło się zachowanie, czy dlatego, że coś zepsułeś. Następnie przemyśl, czy poprawiając testy przypadkiem nie duplikujesz tych, które przed chwilą napisałeś. Jeśli nie, to zastanów się, czy zmiana w funkcjonalności, którą wprowadziłeś nie była przypadkiem zbyt duża i nie miała jeszcze innych niechcianych konsekwencji. Jeśli wszystko jest ok, to powtórz powyższą pętlę aż wprowadzisz wszystkie zmiany.

Moja ogólna rada – nie bójcie się czerwonych testów. One właśnie po to są by często nie przechodziły i pokazywały Wam status systemu. Jeśli zastosujecie zasady z Clean Code oraz dobrze przemyślicie swoje testy to analiza tego dlaczego nie przechodzą będzie łatwa i przyjemna, a co za tym idzie wniesie to znaczną wartość do Waszej pracy.

Reasumując

Coding Dojo w tej formie jest w mojej ocenie bardzo fajnym ćwiczeniem wartym polecenia każdemu zespołowi. Przedmiotem Dojo wcale nie musi być TDD Kata – możecie spróbować zmienić się z zupełnie innym problemem nawet niekoniecznie programistycznym. Najważniejsze jest to, by wykonywane ćwiczenia wnosiły jakąś wartość i pozwalały Wam poszerzyć wiedzę.

Dojo, które przeprowadziłem z zespołem B&T Skyrise miało na celu przede wszystkim poszerzenie wiedzy na temat TDD oraz nabranie wprawy w praktykach Code Review. Myślę, że cel został osiągnięty.

PS: Niezmiernie ciekawi mnie, czy któryś z uczestników spróbował później podejść do samodzielnego rozwiązania jakiejś TDD Katy… Sprawdzę to przy najbliższej okazji.

1 komentarz więcej...

Komentarz do: „Błąd Arystotelesa w IT”

opublikowany przez 26, Lut, 2014, w kategoriach Agile, CI, Scrum, XP

[W ostatnim wydaniu Computerworld pojawił się artykuł autorstwa Bogdana Berezy zatytułowany „Błąd Arystotelesa w IT„, który wręcz domagał się komentarza. Nie chodzi tyle o jakieś rażące błędy merytoryczne ale raczej o niedomówienia i drobne naginanie faktów, które troszeczkę zaciemniają prawdziwy obraz. Komentarz jednak trochę urósł więc zrobiła się z tego też notka na blogu – poniżej… zapraszam do dyskusji]

Wygląda na to, że autor pisząc o testowaniu w Agile jako o czymś co jest w 85% takie samo jak „zwykłe” testowanie chyba nie do końca zdaje sobie sprawę z tego czym faktycznie testowanie w Agile różni się od testowania w procesach „tradycyjnych”.

Pamiętam jak kilka dobrych lat temu zmieniłem pracę przenosząc się z organizacji pracującej wg tzw. Modelu Kaskadowego opatrzonego normami ISO i certyfikatami CMMI do prawdziwie zwinnego zespołu pracującego w Scrum… Różnica była zasadnicza!

Oczywiście zgadzam się, że samo testowanie to testowanie i nie różni się niczym w obydwu podejściach… Smutny jest fakt, że wielu „testerów” w tym obszarze ma nadal poważne, podstawowe braki dotyczące technik testowania i projektowania testów, pomimo nawet uczestnictwa w wielu certyfikowanych szkoleniach, które częściej bardziej uczą jak zdać egzamin, niż jak faktycznie coś dobrze przetestować (oczywiście zdarzają się wyjątki)…

Wracając do różnic pomiędzy Agile a nie-Agile… Już na przykład: rola testera, jego miejsce w zespole i organizacji, wartość i sposoby komunikacji, zasadnicza przewaga bezpośredniej współpracy wewnątrz zespołu wskroś-funkcjonalnego ponad przerzucanie się zgłoszeniami bugów pomiędzy działem testów i działem programowania, częstotliwość testowania, feedback dla programistów, proporcje pomiędzy testami regresyjnymi a testami akceptacyjnymi zupełnie nowych funkcjonalności, rola automatyzacji testów i jej wsparcie w procesie testowym i procesie zapewniania jakości oprogramowania, ciągła integracja i ciągłość testowania to tylko niektóre z różnic, o który autor zdaje się zapominać… Jest tego trochę więcej niż by mogło się, a przynajmniej na tyle więcej że potrafimy nad tym pracować przez ponad 80% czasu na naszych warsztatach z Testowania w Agile na które serdecznie autora zapraszam.

Samo omawianie od podstaw metod zwinnych w 2014 roku jest już raczej zbyteczne.
Agile nie jest już żadną nowością – 13 lat praktyki od Manifestu Agile popartych wieloma sukcesami to przy obecnym tempie naszego życia i rozwoju chyba już całkiem sporo. A początków Agile można się doszukiwać w okolicach 1991 roku, a jak twierdzą niektórzy (np. Tom Gilb) jeszcze jakieś 20 lat wcześniej…

Co do samej terminologii stosowanej w Agile to… „Jeśli chcesz coś zmienić to musisz coś zmienić” – terminologia i używany język są dobrym punktem startu. Ma to głębokie uzasadnienie zwłaszcza, gdy potrzebujemy zmienić istniejącą organizację.

Podsumowując – nie rozumiem co autor miał na myśli krytykując realnie działające (przynajmniej na podstawie moich kilkuletnich doświadczeń) metody jakoby były one zupełnie nienaukowymi „modnymi bzdurami”. Tym bardziej, że metody te również mają podstawy w nauce – może bardziej w psychologii i naukach humanistycznych niż inżynierii ale jednak…

Przypominam, że „Waterfall” również kiedyś był modną bzdurą, która z siłą wodospadu miała popychać projekty IT do przodu…
Zmieniła się jednak rzeczywistość i mechanizmy współczesnego wytwarzania oprogramowania przez co dawne metody przestały działać… Już widać, że Scrum powoli przestaje być wystarczający na współczesnym rynku, a wydania raz na dwa tygodnie wywołują uśmiech na twarzach praktyków Continuous Delivery pracujących w organizacjach tworzących produkty według zasad Lean Startup (gdzie nikt nie wspomina nawet o rzeczach z ich perspektywy tak archaicznych jak inżynieria wymagań)

6 komentarzy więcej...

A mury runą, runą, runą…

opublikowany przez 13, Lip, 2012, w kategoriach Agile, Automatyzacja, CI, Programowanie, Scrum, Testowanie, XP, Zarządzanie

…i pogrzebią stary świat…

Tak wiem, że Kaczmarski miał niewiele wspólnego z wytwarzaniem oprogramowania. O jaki mur chodzi? Mur, który od lat budowaliśmy pomiędzy tzw. programistami i tzw. testerami. Często powstawały nawet fizyczne mury i bariery – chociażby odległość liczona w tysiącach kilometrów.

Od dawna już wiadomo, że zarządzanie poprzez konflikt (tutaj konflikt między testerami, a programistami) jest jedynie (o ile w ogóle) efektywne w krótkich okresach czasu – długofalowo niszczy motywacje i powoduje mnóstwo problemów wynikających z komunikacji. Programowanie (wytwarzanie oprogramowania) jest grą zespołową – zespół to nie tylko programiści. Zespół to też testerzy, zespół to także klienci, to czasem też docelowi użytkownicy – stawianie murów pomiędzy nimi powoduje jedynie wzrost złożoności i zwiększoną ilość problemów.

Cały ruch Agile czy metodyki takie jak Scrum, XP, Crystal heroicznie walczą z podziałami i sprowadzają się do zapewnienia odpowiednich interakcji pomiędzy ludźmi wytwarzającymi oprogramowanie. W efekcie powstają produkty wysokiej jakości.

Dlatego proszę oprócz fizycznych, technicznych i psychologicznych barier usuńmy też wszelkie inne spory i kłótnie. Męczą mnie już dyskusje na temat tego jakie testy powinien pisać/wykonywać tester a jakie programista? Czy to  na prawdę ma znaczenie?! Jeśli będziemy pracować razem, to prawdopodobnie osiągniemy dużo lepsze rezultaty. Co złego się stanie jeśli programista siądzie w parze z testerem i razem coś zakodują, albo przetestują?

Testowanie (czy to manualne, czy automatyczne) jest nieodłącznym elementem programowania. Bez testowania (jakiegokolwiek) oprogramowanie najprawdopodobniej nigdy by nie powstało. Bez programowania nie było by co testować. Cała idea inkrementalnego wytwarzania oprogramowania opiera się o nie dzielenie procesu wytwarzania oprogramowania na testowanie i kodowanie – robimy te rzeczy równolegle i ciągle.

Pisanie testów automatycznych jest równoznaczne z tworzenie tzw. kodu produkcyjnego. Gdy byłem na szkoleniu z Clean Code u Uncle Bob’a (Robert C. Martin) z wielkim oburzeniem mistrza spotkało się moje stwierdzenie, że rzadko tworzę kod produkcyjny, gdyż zajmuję się głównie automatyzacją testów. Testy automatyczne są kodem produkcyjnym – często dużo ważniejszym i bardziej wartościowym niż kod funkcjonalności aplikacji.

Nie stawiajmy więc sztucznych barier pomiędzy testowaniem a programowaniem – obydwie te czynności mają na celu dostarczenie produktu wysokiej jakości i na tym powinniśmy się skupić.

2 komentarze więcej...

Dlaczego automatyzujemy testy?

opublikowany przez 21, Cze, 2012, w kategoriach Agile, Automatyzacja, CI, Programowanie, Testowanie, XP

Mamy różne rodzaje testów. Jeśli chodzi o testerów to najczęściej zajmują się oni automatyzacją testów funkcjonalnych GUI czy też testami typu end-to-end.

Warto się zastanowić po co tworzymy takie testy automatyczne? Jeśli robimy to wyłącznie by przyspieszyć testowanie, to nie jest to najlepsza odpowiedź. Celem tworzenia takich testów powinno być poprawianie jakości – może nie bezpośrednio ale ogólnie testy automatyczne powinny przyczyniać się do poprawy jakości. Robię to poprzez dostarczanie szybkiej – dużo szybszej niż tester manualny informacji zwrotnej na temat działania produktu, a przede wszystkim na temat tego czy programista wprowadzając zmiany niczego nie zepsuł. Jest to niezwykle istotne zwłaszcza, gdy mamy do czynienia z systemami typu legacy – kaszana bez wyraźnej, przejrzystej architektury i testów jednostkowych. Systemy tego typu maja to do siebie że bardo trudne, o ile wręcz niemożliwe jest przetestowanie ich przy użyciu testów jednostkowych. Tworzenie unit testów do już istniejącego kodu, napisanego przed testami jest bardzo czasochłonne (swoją drogą to jeden z powodów dla którego niektórzy mówią, że pisanie testów jest strasznie kosztowne i czasochłonne). Kod który nigdy nie był pisany z myślą o testach jednostkowych, które będą go testowały często jest po prostu nietestowalny.

By móc poprawiać jakość naszego produktu musimy być w stanie poprawiać kod i jego jakość. Niestety bez szybkiej informacji na temat tego czy kod działa  – na przykład w postaci unit testów nie mamy nigdy pewności czy nasze zmiany niczego nie popsują, a co za tym idzie brakuje nam  odwagi by cokolwiek zmieniać i poprawiać. Pętla się zamyka – nie możemy zmienić nie testowalnego kodu tak by był testowalny bez obawy o to, ze czegoś nie popsujemy. Błędne koło się zamyka a dług technologiczny ciągle rośnie.

Jednym ze sposobów jest wdrożenie testów typu end-to-end które traktują testowany system jak „black box” lub „gray box”, do którego mają dostęp tylko w odpowiednich miejscach. Takie testy mają niestety kilka wad – na przykład są wolne i trudne w utrzymaniu.

Powyższe problemy to jeden z wielu powodów stosowania w projektach tzw. piramidy testów, która zakłada, że będziemy tworzyć jak najwięcej testów jednostkowych, trochę mniej testów funkcjonalnych i akceptacyjnych, a testów typu end-to-end nie będziemy tworzyć wcale, lub będą to tylko skrajne przypadki.

Takie podejście sprawdza się idealnie, rozpoczynamy projekt od zera i od samego początku postępujemy według wyżej wymienionych zasad. Niestety rzeczywistość jest inna. Na początku projektów informatycznych mało kto przejmuje się automatyzacja w ogóle, o testach jednostkowych i TDD już nawet nie wspomnę. Testy automatyczne wdraża się przeważnie dopiero, gdy zaczyna się już robić na prawdę niedobrze, a dług technologiczny daje o sobie znać na każdym kroku. W ten sposób powstają właśnie systemy typu legacy.

Gdy kilka miesięcy temu na konferencji 33rd degree podczas jednej z prezentacji Uncle Bob powiedział coś w stylu: „Nie piszcie testów end-to-end – one są złe”, a ja zobaczyłem bezmyślnie przytakujące głowy setek programistów siedzących na sali pomyślałem sobie: „Cholera – ostatnie 2 lata prowadzonej przez nas indoktrynacji w dziedzinie automatyzacji testów w naszym kraju właśnie trafił szlag…”. Miałem o to spory żal do Bob’a, który zresztą na niego wylałem w postaci bardzo długiej dyskusji na temat legacy code etc. Bob oczywiście na myśli miał projekty typu green-field, gdzie zaczynamy od zera. Oczywiście bardzo dobrze, że skorzystał ze swojego autorytetu i może przynajmniej kilka osób do tego przekonał, dzięki czemu w przyszłości będzie znacznie mniej systemów przepełnionych legacy code. Ale co z projektami, które już istnieją i które nadal trzeba rozwijać? To właśnie w takich projektach pracuje większość testerów (domyślcie się dlaczego).

Łatwo znaleźć rozwiązanie dla systemów już istniejących, gdy uświadomimy sobie, że celem testowania nie jest tylko testowanie samo w sobie ale też poprawa jakości. Podstawowym celem wdrożenia testów automatycznych jest zapewnienie możliwości wprowadzania bezpiecznych zmian – podobnie jak celem testów regresyjnych (które przypadkiem powstają przy wdrożeniu automatyzacji) jest dostarczenie informacji zwrotnej na temat tego, czy nasze zmiany nie wprowadziły błędów w istniejącej już funkcjonalności. Aby móc cokolwiek zmieniać tworzymy takie testy które dadzą nam przynajmniej ogólna informację na temat tego czy funkcjonalność która zmieniamy nie uległa popsuciu z punktu widzenia użytkownika.

W skrócie tworzymy odwróconą piramidę testów – z dużą ilością testów end-to-end i funkcjonalnych. Dzięki temu możemy zmieniać kod aplikacji i poprawiać jego jakość (refaktoryzować, zmieniać architekturę etc.) sprawiając, że staje się on testowalny także na niższych poziomach. Za każdą zmianą idzie dopisywanie nowych testów jednostkowych (a nawet to zmiany sterowane są pisaniem testów jednostkowych jak w TDD). Po pewnym, czasie mamy testowalny system z przejrzystą architekturą, w której każdą warstwę możemy przetestować oddzielnie, a GUI i logika w nim ograniczone są do minimum. To jest dobry moment by zacząć pozbywać się naszych testów typu end-to-end. Tak! Właśnie tak! Cały czas tworzyliśmy testy typu end-to-end z myślą o tym, że za chwilę je usuniemy. Największym kosztem podczas utrzymywania testów są duplikacje – więc jeśli tworzymy testy jednostkowe, które w przybliżeniu testują to samo co nasze testy end-to-end to oczywistym staje się potrzeba usunięcia tych, które są bardziej kosztowne i mniej wartościowe – end-to-end. W ten sposób stopniowo obracamy naszą piramidę testów i zaczyna ona powoli wyglądać tak jak powinna.

Celem całej naszej pracy nie jest zwiększenie pokrycia testami – co w podanym przykładzie mogło by doprowadzić do pokrycia nawet powyżej 100% (oczywiście zależy jak liczonego) – pytanie czy taka metryka cokolwiek nam mówi poza tym, że straciliśmy dużo czasu? Prawdziwym celem jest poprawa jakości, jakości na najniższym poziomie, zapewnienie poczucia bezpieczeństwa podczas wprowadzania zmian, wydzielenie odpowiednich modułów i domen w naszej aplikacji, odseparowanie modelu o danych, logiki od interfejsu użytkownika, itd.

Powyżej opisałem jeden (jak dotąd z pośród sprawdzonych przeze mnie w praktyce, jedyny działający) z kilku sposobów radzenia sobie z długiem technologicznym.

4 komentarze więcej...

Zwinne środowisko testowe – webinarium.

opublikowany przez 18, Paź, 2010, w kategoriach Agile, Automatyzacja, CI, Programowanie, Scrum, Testowanie

Zapraszam na webinarium które poprowadzę w najbliższą środę (20-10-2010) o godzinie 13.00 w ramach cyklicznych e-seminariów Polskiej Grupy Scrum.

Zapisy na www.scrum.org.pl.

Temat spotkania ponownie związany z automatyzacją testów. Postaram się przybliżyć samą idee automatyzacji oraz zademonstrować kilka dobrych praktyk ułatwiających życie każdego testera. Nie zabraknie również rozwinięcia podstawowych zasad Continous Integration, oraz wskazówek jak testować w projektach Agile’owych.

2 komentarze więcej...

Continuous integration – i po co to wszystko?

opublikowany przez 19, Sty, 2010, w kategoriach Agile, Automatyzacja, CI

Na wstępie chciałbym w skrócie przedstawić podstawowe zasady CI, a może raczej ACI (Automated Continuous Integration):

Trzymaj kod w repozytorium
A nawet nie tyle trzymaj co commituj swoje zmiany jak najczęściej, dzięki temu każdy będzie miał możliwość integrowania swoich zmian z Twoimi. Do tego należy także pamiętać o tym aby także updateować jak najczęściej swoje lokalne repozytorium na którym się pracuje aby integrować swoje zmiany z najświeższymi zmianami kolegów.
Cechy dobrego repozytorium to przede wszystkim:
– przejrzysty widok ostatnich zmian
– możliwość tworzenia rozgałęzień i automatycznego łączenia ich
– system powiadomień o zmianach
– łatwa możliwość odwrócenia ostatnich zmian
Osobiście używam GIT i SVN, jak na razie obydwa spełniają większość moich oczekiwań.

Automatyzuj buildy
Buildy powinny być odpalane automatycznie. Do automatyzacji buildów polecam Hudson lub CruiseControll. Warto też wspomnieć o tym co taki build powinien robić, mianowicie powinien:
– odpalać testy jednostkowe
– odpalać inne testy (jeśli są)
– generować raporty z pokrycia kodu testami
– generować raporty z wynikami testów
– wysyłać powiadomienia, zwłaszcza gdy testy nie przechodzą
– powinien być zintegrowany ze środowiskiem RC do którego commity powinny trafiać jedynie, gdy build przechodzi
– powinien generować inne artefakty, które są w danym projekcie potrzebne.

Stosuj TDD
Tak, by to wszystko miało sens potrzebne są testy do kodu, który piszemy. Jak najwięcej testów. Najlepszą praktyką w tej dziedzinie jest TDD – pisanie testów przed napisanie właściwego kodu (ale o tym może innym razem).

Zasada nie zabierania zakiszonego kodu do domu
Każdy programista commituje przynajmniej raz dziennie. Im częściej tym lepiej.

Każdy commit odpala build
Po każdej zmianie kodu powinny być odpalane testy w celu jak najszybszego wykrycia błędów i ich poprawy,a także w celu łatwiejszego wykrycia przyczyny błędu (zazwyczaj należy jej szukać tylko w ostatnim commicie).

Utrzymuj build szybkim
Buildy powinny być jak najszybsze, by niepotrzebnie nie marnować czasu na czekanie, aż build przejdzie. Obecnie rozwiązuje się ten problem w sposób sprzętowy np stosując chmury obliczeniowe do odpalania testów.

Środowisko testowe powinno być bliźniacze do środowiska produkcyjnego
Chociażby dlatego by uniknąć błędów wynikających z różnicy w tych środowiskach.

W każdej chwili powinieneś mieć dostęp do ostatniej stabilnej wersji oprogramowania
Zgodnie ze wspomnianą zasadą Agile, w każdej chwili powinniśmy mieć pod ręką jakąś stabilną wersję oprogramowania teoretycznie gotową do publikacji.

Każdy powinien mieć dostęp do wyników buildów
Na przykład Hudson ma wbudowany ciekawy plugin, który umożliwia graficzna prezentację wyników ostatnich buildów. Tego typu aplikacje mają zazwyczaj także api, które sprzyja tworzeniu własnych rozwiązań do prezentacji efektów testów etc. My w firmie używamy właśnie Hudsona i specjalnego monitora który stojąc w widocznym dla każdego miejscu prezentuje efekty ostatnich commitów.

Automatyczny deployment
Fajny ficzer zwłaszcza w fazie maintenance projektu, gdy zmiany są dosyć często wgrywane na serwer produkcyjny. Istnieją gotowe narzędzia pozwalające na automatyczne deployowanie aplikacji.

Żeby lepiej zrozumieć na czym polega CI należało by się pierw zastanowić po co właściwie coś takiego jak Ciągła Integracja jest nam potrzebne? Najprościej będzie gdy wrócimy do jednego z podstawowych założeń zwinnego zarządzania projektami a mianowicie: „w dowolnym (odpowiednio zaawansowanym) momencie trwania projektu powinniśmy być w stanie dostarczyć ‚jakiś’ produkt, który spełnia pewne założenia i udostępnia pewną funkcjonalność, produkt ten teoretycznie powinien być gotowy do wypuszczenia na rynek”. Idąc dalej tym tropem można łatwo wywnioskować, że aby produkt był gotowy do publikacji musi spełniać pewne kryteria jakości, które powinny zostać przetestowane. By jeszcze lepiej zrozumieć potrzebę ciągłej integracji w projekcie należałoby się zastanowić nad tym w jaki sposób integrować pracę wielu programistów, którzy pracują nad często zazębiającymi się fragmentami kodu. Może aby lepiej zobrazować strukturę problemu posłużę się przykładem w którym zespół projektowy nie wykorzystuje aspektów CI.

Wyobraźmy sobie zespół składający się z pięciorga programistów i dwojga testerów. Zespół ma dostarczyć jakąś określoną aplikację, która ma trzy podstawowe funkcjonalności. Tutaj pojawia się pierwszy problem – jak podzielić pracę? Może niech każdy programista zajmie się pojedynczą funkcjonalnością, a na koniec spróbują zintegrować to wszystko ze sobą (uwierzcie mi są firmy w których taki model wytwarzania oprogramowania jest stosowany na co dzień). No tak tylko, że funkcjonalności jest 3 a programistów pięcioro. Poza tym są jeszcze testerzy, którzy przez większość czasu będą się nudzić. No dobrze – klasyczny model Waterfall zakłada, że testy powinny być przeprowadzane na końcu, więc niech tak będzie. Żeby jeszcze lepiej wszystko zobrazować dodajmy trochę matematyki. Załóżmy, że wykonanie pierwszej funkcjonalności zajmie około 30 roboczogodzin, drugiej 60 roboczogodzin, trzeciej 15 roboczogodzin. Do tego przetestowanie około 1/3 czasu potrzebnego na implementację czyli odpowiednio 10, 20, 5 roboczogodzin (dość optymistyczne, ale realne założenia). Dobrze – prace ruszyły pierwsza po 15 godzinach zostaje ukończona funkcjonalność nr 3, programista który nad nią pracował teraz odpoczywa. Po kolejnych 15 godzinach została ukończona funkcjonalność nr 1, teraz programiści mogą rozpocząć prace nad integracją funkcjonalności 1 i 3, mają na to około 30 godzin. Po 60 godzinach od rozpoczęcia projektu dostarczono funkcjonalność nr 2, którą teraz trzeba jeszcze zintegrować, na tym etapie okazuje się że popełniono kilka błędów w założeniach, czegoś nie ustalono dokładnie etc. wiec integracja potrwa kolejne 20h. W sumie po 80 godzinach pracy dostarczono produkt do testów. Testowanie wraz z poprawkami zajmie około 40h.

Po 120 godzinach pracy mamy gotowy produkt. Przypomnijmy, że dla dwóch programistów nie było pracy, ciężko jest pracować nad jednym kawałkiem kodu na dwóch różnych komputerach. Co najwyżej służyli oni jedynie pomocą swoim kolegom.

A teraz podobny przykład (ten sam problem do rozwiązania) z tym że zespół wykorzysta większość aspektów CI.

Ponieważ wiemy ile szacunkowo zajmą pracę nad każdą z funkcjonalności i wiemy, że funkcjonalność nr 2 zajmie najdłużej pracować nad nią będzie aż troje programistów, dodatkowo programista, który będzie pracował nad funkcjonalnością nr 3 po zakończeniu prac nad nią wesprze kolegę pracującego nad funkcjonalności nr 1. Praca zespołowa jest w pełni możliwa dzięki temu, że zespół korzysta z systemu kontroli wersji, który pozwala na łatwą dystrybucję najaktualniejszego kodu. Dodatkowo testerzy dzięki środowisku, które roboczo nazwiemy RC (Release Candidate) mogą w każdej chwili testować dostarczane funkcjonalności i zgłaszać błędy, które są dużo łatwiejsze do poprawienie we wcześniejszej fazie. Należy też zauważyć, że integracja całości odbywa się od samego początku, gdyż wszystkie zmiany wrzucane są do jednego repozytorium. Czas implementacji wydłuży się zapewne o około 1/3 ze względu konieczność pisania testów jednostkowych (nie jest to wymóg jeśli w zespole są testerzy, jednak dzięki temu ich praca powinna się skrócić o około połowę). Zobaczmy jak to teraz wygląda. Planowane czasy implementacji po uwzględnieniu dodatkowego pisania testów jednostkowych: F1 – 40 roboczogodzin, F2 – 80 roboczogodzin, F3 – 20 roboczogodzin. Testy: T(F1) – 7 roboczogodzin, T(F2) – 14 roboczogodzin, T(F3) – 4 roboczogodziny. Projekt startuje. Po 10 godzinach pracy rozpoczynają się testy dla funkcjonalności nr 2 (wykonuje je jeden tester). Po 16 godzinach od rozpoczęcia rozpoczynają się testy akceptacyjne dla funkcjonalności nr 3, po 20 godzinach od rozpoczęcia ta funkcjonalność jest już gotowa i przetestowana, programista, który nad nią pracował pomaga programiście pracującemu nad funkcjonalnością nr 1 (do jej ukończenia zostało 20 roboczogodzin, podzielone na dwóch daje po 10 godzin). Trzy godziny później rozpoczynają się testy funkcjonalności nr 1. Po kolejnych 7 godzinach funkcjonalność nr 1 jest ukończona i przetestowana – minęło 30 godzin od rozpoczęcia projektu. W międzyczasie po około 27 godzinach od rozpoczęcia projektu zostaje ukończona i przetestowana funkcjonalność nr 2. Wszystko powinno działać i być już zintegrowane, dla pewności testerzy sprawdzą wszystko jeszcze raz – po 8 godzin każdy.

W sumie daje nam to 38 godzin pracy nad projektem, których efektem jest gotowy, przetestowany produkt, którego dodatkowym gwarantem jakości są test jednostkowe. Dodatkowo od pewnego momentu w z środowiska RC mogliśmy pobrać stabilną – przetestowaną wersję aplikacji zapewniającą pewne funkcjonalności. To raczej znacznie lepszy wynik niż poprzednio. Powyższe założenia są trochę naciągane i wyssane z palca, ale uwierzcie mi już kilkukrotnie widziałem nawet dużo bardziej zaskakujące efekty wprowadzenia CI do procesu wytwarzania oprogramowania.

Powyższy wywód jest również pewnego rodzaju odpowiedzią na często stawiane pytanie: „Czy pisać testy jednostkowe?”. Postaram się w przyszłości poszukać (być może samemu przeprowadzić) jakichś badań na temat czasu wytwarzania oprogramowania z i bez. Ogólnie już teraz mogę wnioskując z doświadczenia jednoznacznie stwierdzić, że niemal zawsze automatyzacja testów znacząco przyspiesza pracę nad projektem, a zwłaszcza pracę w fazie maintenance.

5 komentarzy więcej...