|
Inne artykuły serwisu "Joel on Software" po
polsku
Inne artykuły serwisu "Joel on Software" po
angielsku
Napisz do autora (tylko po angielsku)
|
|
|
Pożytki z codziennego generowania wersji
|
Joel Spolsky
Przełożył Andrzej Kocoń
Korekta Andrzej Swędrzyński
27 stycznia 2001
Pierwszy komputer IBM-PC trafił do Izraela w 1982 roku. Chociaż miał być
dostarczony do naszego domu, nie mogliśmy się doczekać i sami pojechaliśmy
odebrać go z portowego magazynu. Do dziś nie wiem, w jaki sposób udało
mi się namówić ojca na zamówienie tak w pełni wyposażonej wersji. W skład
zestawu wchodziły dwa napędy dyskietek, 128 kilobajtów pamięci, dwie
drukarki - igłowa do szybkiego drukowania szkiców i głowicowa firmy Brother,
hałasująca głośniej niż karabin maszynowy.
Myślę, że mieliśmy
prawie wszystkie dostępne akcesoria: PC-DOS 1.0, dokumentację techniczną wartą
75 dolarów z kompletnym kodem źródłowym BIOSu, Macro Assembler
i niezwykły monitor monochromatyczny IBM, z 80-ma kolumnami i...
małymi literami! Całość kosztowała około 10.000 dolarów, wliczając srogie opłaty
importowe. Czysta ekstrawagancja!
„Wszyscy” wówczas wiedzieli, że BASIC to język dla dzieci, który wymuszał
pisanie pogmatwanego kodu i przeobrażał umysł w ser camembert.
Wydaliśmy więc dodatkowo 600 dolarów na Pascal IBM, zajmujący trzy dyskietki. Na
pierwszej i drugiej znajdowały się dwa przebiegi kompilatora, a na trzeciej
był linker. Napisałem prosty program typu „hello, world” i skompilowałem
go. Łączny czas jaki upłynął: 8 minut.
Hmm. To dość długo. Napisałem skrypt żeby zautomatyzować ten proces. Zszedłem
do 7,5 minuty. Lepiej. Kiedy jednak próbowałem pisać dłuższe programy, takie jak
moja przebojowa wersja Otella, z którą zawsze przegrywam, większość
czasu spędzałem w oczekiwaniu na wyniki kompilacji. „Tak, tak”, rzekł mi pewien
profesjonalny programista, „zwykliśmy trzymać w biurze sprzęt gimnastyczny
i ćwiczyć podczas kompilacji. Po kilku miesiącach programowania miałem
mięśnie zabójcy”.
Pewnego dnia w Danii ukazał się cudowny program, Compas Pascal, nota
bene kupiony później przez Philippa Kahna i przemianowany na Borland Turbo
Pascal. To był szok. Turbo Pascal potrafił wszystko to, co Pascal IBM, ale
mieścił się w 33 kilobajtach pamięci, wliczając w to edytor tekstowy.
Jeszcze bardziej niezwykły był fakt, że mały program można było skompilować
w niecałą sekundę. Nie byłbym bardziej zdumiony, gdyby nikomu nie znana
firma stworzyła nagle podróbkę buicka LeSabre, zdolnego rozwinąć 1.000.000 mil
na godzinę i objechać kulę ziemską zużywając przy tym tak niewiele paliwa,
że jego wypicie nie zaszkodziłoby nawet mrówce.
Nagle stałem się o wiele bardziej wydajny.
To właśnie wtedy poznałem pojęcie cyklu Czytaj-Wartościuj-Drukuj (CWD), który
opisuje cały sposób działania interpretera lispu: czyta on wyrażenie, oblicza
jego wartość i drukuje wynik. Poniżej pokazano przykład pętli CWD:
interpreter lispu czyta, wartościuje i drukuje wynik tego, co napisałem.
Podczas tworzenia programu mamy do czynienia z wersją makro pętli CWD - pętlą
Redaguj-Kompiluj-Testuj. Poprawiamy program, kompilujemy go i sprawdzamy,
jak działa.
Istotne jest tutaj to, że aby stworzyć program, należy wykonać ten cykl wiele
razy. Dlatego im krócej trwa pętla Redaguj-Kompiluj-Testuj, tym większa jest
wydajność programisty, aż do naturalnej granicy kompilacji natychmiastowej. Oto
formalny, naukowy wręcz powód, dla którego programiści chcą mieć jak najszybsze
maszyny, a twórcy kompilatorów zrobią wszystko, co tylko możliwe, aby
skrócić cykl Redaguj-Kompiluj-Testuj. W środowisku Visual Basic kompilacja jest
bardzo szybka dzięki analizie składniowej każdego wiersza jeszcze w trakcie
jego pisania. W Visual C++ efekt ten uzyskuje się poprzez zastosowanie
przyrostowych kompilacji i linkowania oraz prekompilowanych plików
nagłówkowych.
Ta sama pętla w zwielokrotnionej niczym fraktal postaci pojawia się, kiedy
tylko zaczynamy pracę w większym zespole, składającym się z wielu
programistów i testerów. Tester znajduje błąd w programie
i sporządza zgłoszenie. Programista poprawia defekt. Ile czasu upływa, nim
tester otrzyma poprawioną wersję programu? W niektórych firmach ten cykl
Reklamuj-Popraw-Sprawdź zajmuje nawet kilka tygodni, a to z kolei
oznacza, że całe przedsięwzięcie jest nieproduktywne. Aby proces wytwarzania
oprogramowania przebiegał sprawnie, należy skupić się skupić na skróceniu pętli
Reklamuj-Popraw-Sprawdź.
Jednym z dobrych sposobów jest codzienne generowanie wersji,
czyli automatyczne, codzienne i kompletne generowanie produktu na
podstawie całego drzewa kodu źródłowego.
Automatyczne - dzięki ustawieniu stałej pory kompilacji kodu
każdego dnia przy użyciu usługi cron (UNIX) lub dyspozytora (Windows).
Codzienne - lub nawet bardziej częste. Kuszące byłoby generowanie
ciągłe, ale może być ono niewykonalne ze względu na problemy
z synchronizacją kodu źródłowego, o czym za chwilę.
Kompletne - możliwe, że twój kod ma wiele wersji. Wersje językowe,
wersje zależne od systemu operacyjnego lub od odbiorcy. Proces generowania
powinien obejmować je wszystkie. Powinien też kompilować każdy plik
źródłowy, bez polegania na być może niedoskonałej możliwości kompilacji
przyrostowej.
A oto niektóre spośród wielu korzyści, jakie daje codzienne
generowanie wersji:
- Testerzy szybko otrzymują nową wersję po poprawieniu błędu. Dzięki temu
mogą szybko sprawdzić, czy błąd został rzeczywiście usunięty.
- Programiści mogą być spokojni, że dokonane przez nich zmiany nie spowodują
problemów przy generowaniu jakiejkolwiek spośród 1024 wersji produktu. I to
bez potrzeby posiadania na biurku komputera z systemem OS/2 w
celach testowych.
- Programiści wprowadzają swoje zmiany do repozytorium tuż przed codziennym
generowaniem wersji. Dzięki temu wiedzą, że nie posłali innych w maliny
przez załamanie całego procesu generowania. Nie zrobili czegoś, co
uniemożliwia kompilację każdemu członkowi zespołu. Taki stan to
odpowiednik błękitnego ekranu śmierci dla całego zespołu. Często zdarza się
on, kiedy programista zapomniał wprowadzić do repozytorium nowo utworzony
plik. Na jego maszynie generowanie przebiega poprawnie, lecz kiedy
tylko ktoś zsynchronizuje swoją kopię z repozytorium, otrzyma błąd
kompilacji i nie będzie w stanie dalej pracować.
- Zewnętrzne grupy, które korzystają z niedojrzałego produktu, takie
jak marketing, odbiorcy wersji beta itp., mogą w każdej chwili otrzymać
względnie stabilną wersję i używać ją przez pewien czas.
- Warto też przechowywać wyniki codziennego generowania programu. W
przypadku wykrycia nowego, naprawdę dziwnego błędu, można przeszukać binarnie
archiwum w celu wyśledzenia momentu, od którego błąd zaczął się pojawiać.
W połączeniu z dobrym systemem kontroli wersji, łatwo można ustalić,
która zmiana spowodowała problem. Podobnie kiedy tester zgłasza rzekomo
poprawiony wcześniej błąd, wskazuje także wersję programu, której ten problem
dotyczy. Dzięki temu programista może sprawdzić, kiedy wprowadził poprawkę
i ustalić, czy problem rzeczywiście został usunięty.
Jak
się zabrać do codziennego generowania wersji? Po pierwsze potrzebny jest serwer
- prawdopodobnie najszybsza maszyna, na jaką można sobie pozwolić. Po drugie
skrypt, który będzie pobierał kompletną kopię bieżącego kodu źródłowego
z repozytorium (w projekcie jest używany przecież system
zarządzania wersjami, prawda?), a następnie wygeneruje od zera wszystkie
wersje końcowego produktu. Jeżeli przewidywany jest program instalacyjny, on
także powinien być generowany. Słowem - wszystko, co w przyszłości otrzyma
klient, musi być uwzględnione w procesie codziennego generowania. Wyniki
kompilacji z każdego dnia należy umieścić w osobnym katalogu,
zakodowanym według daty. Skrypt powinien być uruchamiany każdego dnia
o ustalonej porze.
- Wyjątkowo ważne jest, aby wszystko, co składa się na końcowy produkt, było
generowane przez skrypt. Od pobrania źródeł, poprzez umieszczenie odpowiednich
plików we właściwym miejscu serwisu internetowego, przeznaczonego do
publicznego pobierania (mimo, że w fazie tworzenia produktu będzie to
oczywiście serwer testowy). To jedyny sposób, aby zapewnić, że w procesie
generowania nie ma nic, co byłoby „udokumentowane” tylko w czyjejś
głowie. Nigdy nie znajdziemy się w sytuacji, w której nie można
wypuścić produktu, bo tylko Shaniqua wie, jak utworzyć program instalacyjny,
a właśnie potrącił ją autobus. W zespole Juno jedyne, co trzeba było
wiedzieć, aby utworzyć od zera pełną wersję programu, to gdzie znajduje się
serwer i jak kliknąć ikonę codziennego generowania.
- Aby zachować zdrowe zmysły, nie należy nic poprawiać na serwerze, służącym
do generowania wersji. I to nawet jeśli we właśnie przygotowywanym do
publikacji produkcie jest jeszcze tylko jeden mały błąd. Powinno być
żelazną zasadą publikowanie tylko takiego produktu, który powstał
w wyniku pełnego i przebiegającego bez żadnych błędów procesu
codziennego generowania wersji na podstawie kompletnej kopii kodu.
- Należy ustawić najwyższy poziom ostrzegania o błędach przez
kompilatory (-W4 w świecie Microsoftu, -Wall na terytorium gcc)
i wymusić zatrzymywanie się po napotkaniu nawet najbardziej błahego
ostrzeżenia.
- Jeśli proces codziennego generowania wersji zatnie się, może zostać
zatrzymana praca całego zespołu. Należy wówczas zostawić wszystko inne
i tak długo ponawiać proces, aż się powiedzie. Niekiedy powstaje
w ten sposób kilka dziennych wersji produktu.
- Skrypt codziennego generowania wersji powinien informować cały zespół o
błędach za pośrednictwem poczty elektronicznej. Nie jest trudno przejrzeć logi
systemowe programem grep w poszukiwaniu linii zawierających „error” czy
„warning” i dołączyć te informacje do poczty. Skrypt powinien również
dodać informację o wynikach każdego procesu do dostępnej dla wszystkich
strony HTML, aby programiści i testerzy mogli sprawdzić, które próby
zakończyły się powodzeniem.
- W zespole Microsoft Excel stosowaliśmy z powodzeniem pewną
zasadę. Polegała ona na tym, że programista, który spowodował błędy
w procesie codziennego generowania wersji, stawał się od tego momentu
odpowiedzialny za czuwanie nad tym procesem, przejmując tę rolę od
poprzedniego pechowego kolegi. Reguła ta była nie tylko niezłym bodźcem do
tego, aby dbać o poprawność procesu, ale dzięki niej każdy miał okazję
pełnić rolę jego opiekuna i poznać jego szczegóły.
- W przypadku zespołów pracujących w jednej strefie czasowej, dobrą
porą na generowanie wersji jest przerwa obiadowa. Wszyscy wprowadzają swoje
poprawki do repozytorium tuż przed wyjściem, generowanie odbywa się
w trakcie posiłku, a po powrocie wszyscy są na miejscu na wypadek,
gdyby coś się nie powiodło. Gdy tylko proces zakończy się poprawnie, każdy
może pobrać najświeższą wersję kodu bez obawy, że czyjś błąd uniemożliwi mu
dalszą pracę.
- Jeśli zespół pracuje w dwóch strefach czasowych, pora generowania
wersji powinna zostać wybrana tak, aby grupa w jednej strefie nie
blokowała grupy w drugiej. W zespole Juno programiści z Nowego
Jorku wprowadzali poprawki o 19-tej czasu nowojorskiego i szli do
domu. Gdyby stało się coś złego, zespół z Hyderabadu w Indiach,
zbierający się około 20-tej czasu nowojorskiego, przez cały dzień nie byłby
w stanie nic zrobić. Zaczęliśmy więc praktykować dwukrotne generowanie
wersji w ciągu dnia, na około godzinę przed końcem pracy każdego
zespołu. To całkowicie rozwiązało problem.
Dalsza lektura:
- Codzienne generowanie wersji jest na tyle istotne, że stanowi jeden
z 12
kroków ku lepszym programom.
- Wiele interesujących rzeczy na temat (cotygodniowego) generowania wersji
przez zespół Windows NT można znaleźć w książce G. Pascala
Zachary'ego pt. Showstopper.
- Tutaj można się
dowiedzieć, co na temat codziennego generowania wersji pisze Steve McConnell.
- Windows NT Build
Lab.
Oryginał ukazał się w wersji angielskiej pod nazwą Daily Builds Are Your Friend |