Zawsze aktualna dokumentacja referencyjna

Cel

Potrzeba jest tak oczywista, że nie będę się nad nią szeroko rozwodził. Jeśli robimy coś niebanalnego, potrzebujemy tworzyć biblioteki, pakiety, moduły, klasy, procedury etc (jakkolwiek się to wszystko nazywa w naszym ulubionym języku programowania) - i musimy je dokumentować, by mogły być używane przez osoby inne niż autor (zresztą sam autor miesiąc po napisaniu - by nie mówić o roku - bardzo doceni sensowną dokumentację). Tym bardziej musimy to robić jeśli publikujemy bibliotekę która ma być szerzej używana. A utrzymywanie aktualnej dokumentacji często stanowi problem.

Uwaga wstępna: zajmuję się w tym rozdziale dokumentacją pisaną przez programistów dla programistów (choć niektóre kwestie są obszerniejsze). O tworzeniu dokumentacji administracyjnej czy użytkownika też może kiedyś napiszę ale jest to temat istotnie różny.

Mamy problem

Moje - już w miarę obszerne - doświadczenie zawodowe pozwala mi stwierdzić, że informatycy to ludzie nie cierpiący pisania dokumentacji. Chętnie i z zapałem przyjmują zadania związane z projektowaniem, kodowaniem, administrowaniem maszynami i usługami. Mniej chętnie ale jednak akceptują udział w dyskusjach na temat wymagań klientów, świadczenie wsparcia technicznego, udzielanie rozmaitych konsultacji a nawet rozmaite głupie czynności pomocnicze (jakieś zakupy, porządki, przemeblowania, ...). Natomiast poszukiwania chętnego do napisania kawałka dokumentacji są zawsze drogą przez mękę a "ofiara" na którą padło jest mniej lub bardziej nieszczęśliwa.

Przyczyny tego stanu rzeczy są różnorodne - od zwykłych problemów z wypowiadaniem się na piśmie (studia informatyczne nie stawiają niemal żadnych wymagań pod tym względem), przez poczucie straty czasu (pisząc dokumentację nie tworzę niczego nowego/ciekawego - a przecież mógłbym w tym czasie zaimplementować taką fantastyczną funkcję), po obrzydzenie związane z nieraz bezsensownymi (bo np. zdefiniowanymi przez Wielką Amerykańską Firmę 10 lat temu na zupełnie inne potrzeby i w całkiem innej sytuacji albo wymagającymi wypełnienia 30 stron różnego typu biurokratycznej wody przed przystąpieniem do rzeczy) szablonami/standardami/metodami stosowanymi w firmie.

A największy problem stanowi już nawet nie stworzenie dokumentacji (co tak czy siak można wymusić, choćby przy pomocy listy produktów projektu), a zapewnianie, by była uaktualniana, gdy już powstanie.

Spotkałem się już z sytuacją, w której dla sporego projektu w C++ robionego na systemach Unix/VMS wymagano szczegółowej dokumentacji API w szablonie Microsoft Word. Programiści obsłużyli to następująco:

  • w trakcie rozwijania oprogramowania w ogóle o jakimkolwiek dokumentowaniu nie myśleli;
  • gdy już zostali silnie zmuszeni, wykonali jednorazowe ręczne kopiowanie plików nagłówkowych do Worda po czym procowicie ręcznie przez parę tygodni przerabiali je na dokumentację (robiąc tytuły rozdziałów, dorzucając opisy etc);
  • po oddaniu dokumentu nigdy do niego nie zajrzeli.

Wbrew pozorom nie jest to jeszcze najgorszy wariant - takowym jest sytuacja, gdy autor, po napisaniu dokumentacji, odrzuca wszelkie propozycje zmian - by nie musieć jej aktualizować.

Kilka kluczowych porad

Generujmy dokumentację

Jeśli dokumentacja kodu ma być pisana na bieżąco i poprawiana gdy kod się zmienia - musi być jego częścią (czy też raczej musi być automatycznie budowana na jego podstawie). Każda próba utrzymywania aktualnej dokumentacji kodu poza kodem skończy się niepowodzeniem. Cokolwiek by to było - piękne szablony w jakimkolwiek edytorze tekstów, narzędzie CASE pięćdziesiątej generacji, taka czy inna baza wiedzy. Jak to mówią fanatycy XP (z którymi pod tym względem się na 100% zgadzam) - jeśli jakaś informacja jest zapisana w dwóch różnych miejscach, w jednym z nich wcześniej lub później będzie nieaktualna. A kod jest zawsze aktualny.

Dosyć często pojawiają się opowieści o tym jak to takie czy inne narzędzie CASE pozwala automatycznie generować i regenerować kod etc. Może. Nie znam (osobiście ani elektronicznie) - poza sprzedawcami takich narzędzi - żadnego człowieka który pracowałby z powodzeniem w taki sposób.

Z drugiej strony, aktualnie używane języki programowania nie dokumentują się same. Zrozumienie co robi zadana klasa, funkcja czy metoda bez jakiegokolwiek opisu jest (z wyjątkiem trywialnych przypadków) czasochłonne, trudne lub niemożliwe (przeciwnikom stwierdzenia niemożliwe proponuję lekturę kodu funkcji mnożącej wielomiany przy pomocy szybkiej transformaty Fouriera). Zresztą, z kodu nie wynika, które cechy zostaną zachowane w następnej wspaniałej wersji.

Moim zdaniem istnieje tutaj złoty środek. Należy ustalić i stosować jakiś specyficzny format komentarzy (musi być na tyle prosty, by jego opis - czy lepiej wyczerpujący przykład - mieścił się na kartce papieru). Do tego trzeba wybrać i stosować jakieś narzędzie, które automatycznie wygeneruje dokumentację z tak obkomentowanego kodu (a dostarczy minimum użytecznych informacji nawet z kodu który nie został odpowiednio skomentowany). Takie formaty i narzędzia istnieją (i nic nie kosztują), piszę o nich dalej.

Niech dokumentacja będzie dostępna

Jeśli autorzy mają mieć jakąś motywację do utrzymywania aktualnej dokumentacji a inni do jej używania, powinna być łatwo dostępna. A to oznacza trzy rzeczy:

  • strawny format;
  • sensowny indeks;
  • dobrze znane miejsce.

Strawny format to możliwość szybkiego i wygodnego dostępu z dowolnego miejsca. Na dzisiaj formatem takim jest HTML. Na pewno nie jest nim DOC (ani format jakiegokolwiek innego edytora tekstu) . Raczej nie jest nim PDF.

Nie mam zamiaru toczyć żadnych świętych wojen. Pracuję już w zawodzie trochę czasu i widzę, że dokumentów Wordowych (jak sądzę także WordPerfectowych, StarOfficeowych etc) - nawet gdy leżą w dobrze znanym i łatwo dostępnym miejscu - niemal nikt nigdy nie czyta online (z rzadka zdaża się drukowanie, po czym drukujący trzyma swoją, nieraz mocno nieaktualną kopię). PDFy są o tyle lepsze, że bywają drukowane przez ludzi pracujących na maszynach nie-Windows - ale poza tym zachowują opisane wady. Strony HTML są czytane - i tyle. Jeśli w jakiejś firmie preferencje są inne - proszę bardzo (z tym, że opinie tego typu powinny bazować na obserwacji a nie na deklaracji wpływowego specjalisty czy nawet ankiecie).

Sensowny indeks to albo jakaś ogólna lista wygenerowana wraz z szczegółowymi dokumentami albo - nawet lepiej - po prostu dedykowana ("obcięta" do automatycznie i ręcznie pisanych dokumentów związanych z odpowiednimi projektami) wyszukiwarka.

Dobrze znane miejsce to możliwość zajrzenia do aktualnej wersji dokumentu przy pomocy paru ruchów myszki. Świetnie sprawdza się serwis WWW na komputerze o prostej nazwie. Ważne, by był automatycznie aktualizowany (czy to przez spięcie z repozytorium kodu - gdy zatwierdzenie zmienionej wersji powoduje przegenerowanie opartej na nim dokumentacji, czy to po prostu jako uruchamiana co najmniej raz dziennie usługa aktualizująca kod na podstawie repozytorium i generująca dlań dokumentację).

Przy okazji: na używaniu wersji online zamiast papierowej zależy mi nie tyle ze względu na oszczędność papieru, co na fakt, że wersje papierowe szybko się dezaktualizują.

Bardzo ważna metryczka

Tak w przypadku dokumentów generowanych, jak pisanych ręcznie, bardzo ważne jest by było dostępnych kilka kluczowych informacji:

  • co to za wersja - może być numerek, może być data ostatniej zmiany - ale musi pozwalać na natychmiastowe ustalenie czy wiszące na dwóch stronach telefonu osoby mają na myśli ten sam papier (i musi, naprawdę musi być odświeżane automatycznie, ręczne aktualizowanie takich pól nie działa);
  • co się ostatnio zmieniło - znów, tam gdzie się da należy to generować automatycznie (w przypadku generowania dokumentacji z kodu można załaczyć do niej historię zmian wygenerowaną z repozytorium kodu na bazie komentarzy podawanych przy zatwierdzaniu zmian);
  • gdzie i w jaki sposób należy zadawać pytania oraz zgłaszać błędy i wątpliwości (nieźle się do tego nadają odpowiednio dobrane listy dyskusyjne.

To jednak nie wszystko

Dokumentacja API nie jest pełną dokumentacją biblioteki lub programu (w prostych przypadkach może wystarczyć). Jeśli robimy coś niebanalnego, potrzebujemy jednak trochę więcej:

  • w przypadku biblioteki - podręcznika użytkownika (w stylu samouczkowym) oraz zestawu działających przykładów (często stanowiących zarazem zestaw testów);
  • w przypadku złożonego programu - dokumentu projektowego zawierające przynajmniej opis celu, kluczowych wymagań, podstawowych założeń przyjętych w implementacji, listę najważniejszych funkcji, klas i obiektów i kilka diagramów obrazujących najważniejsze interakcje (tu kłania się wykorzystanie ulubionej metodyki projektowania).

Warto pisząc takie dokumenty = gdzie tylko jest to możliwe - odwoływać się (nawet dosłownie - wykorzystując hiperlinki) do dokumentacji referencyjnej. Wtedy takie dokumenty mają szansę na jako taką aktualność - o ile rozmaite szczegóły się szybko zmieniają, o tyle ogólna koncepcja i architektura, gdy raz zostaną zdefiniowane, są dosyć stabilne.

O konkretnych narzędziach

Ogólny przegląd

Koncepcja łączenia kodu z dokumentacją jest stara co najmniej tak, jak ... Donald E. Knuth, który implementując pierwszą wersję systemu TeX opracował zarazem zestaw narzędzi do łączenia kodu z dokumentacją. Sama idea nosiła nazwę literate programming a jej praktyczna implementacja polegała na tworzeniu plików źródłowych, w których bloki paru-parunastu wierszy kodu przeplatały się z podobnej wielkości fragmentami dokumentacji - po czym jedno narzędzie wycinało z takiego pliku źródłowego wyłącznie kod (oryginalnie Pascalowy) w celu kompilacji a inne wydzielało dokumentację (akurat w celu zTeX-owania ale nie jest to tu szczególnie istotne), dołączając do niej też właściwy kod jako wydruki. Wspomniane narzędzia (WEAVE, TANGLE) istnieją do dzisiaj, choć poza autorem, który ciągle w tej formie utrzymuje bazową wersją TeXa, chyba nie są przez nikogo używane.

Co jest dosyć zabawne (zestawienie autora i produktów mocno formalnych z najbardziej hackerskim językiem programowania), bardzo zbliżoną do koncepcji Knutha ideę i sposób pracy (choć w oparciu o zupełnie inną składnię) realizuje format POD używany do dokumentacji skryptów i modułów perlowych (w jego przypadku nie ma potrzeby generowania kodu, komentarze POD są komentarzami języka).

Ciekawe rozwiązanie jest dostępne dla autorów programów pisanych w Pythonie (notabene język programowania, który bardzo polecam). Odpowiednio sformatowane komentarze dokumentacyjne (zamieszczane na początku klasy/funkcji) są dostępne dla kodu programu, wraz z metaopisem klas czy funkcji (Python zawiera API pozwalające badać w trakcie działania sygnatury obiektów)! Istnieją też korzystające z tej cechy narzędzia do generowania dokumentacji. Zbliżoną funkcjonalność dają też niektóre dialekty Lispu (np. Emacs Lisp).

A naprawdę masowe wykorzystywanie technik automatycznego generowania dokumentacji API przyniosła Java - wprowadzając JavaDoc. To prosty, efektywny i sprawdzający się w praktyce format komentarzy dokumentacyjnych (zresztą stał się on standardem de facto, powielają go omawiane dalej generatory dla C++). Samo narzędzie generujące dokumentację nieco bym poprawił - tworzone strony HTML mogłyby być nieco lepiej zorganizowane (stąd zresztą wynika fakt, że istnieją konkurencyjne dla javadoca generatory dokumentacji dla Javy).

C++

Narzędzia do automatycznego generowania dokumentacji dla programów w C++ "obrodziły" szczególnie silnie po sukcesie JavaDoc-a - i w większości przejęły (co najwyżej lekko go uzupełniając) format JavaDoc-a (istnieją narzędzia o innym formacie - np. perceps - ale chyba sensowniej trzymać się JavaDoca).

Znam trzy poważne narzędzia OpenSource do generowania dokumentacji programów i bibliotek w C++:

  • kdoc - program napisany do generowania dokumentacji dla projektu KDE ale możliwy do stosowania szerzej;
  • doc++ - chyba pierwszy popularny program tego typu, w 1998 roku na dłuższy czas 'zamarzł' ale od paru miesięcy znowu aktywnie się rozwija;
  • doxygen - najnowsze ale aktualnie chyba najbardziej dynamicznie rozwijające się narzędzie.
Wszystkie obsługują generowanie HTML, niektóre także inne formaty (TeX, RTF). Wszystkie potrafią generować hierarchie klas, opisy klas, metod, atrybutów i funkcji. Hierarchia klas i lista metod czy funkcji wraz z sygnaturami jest generowana nawet pod nieobecność komentarza dokumentacyjnego.

Oczywiście różnią się one wyglądem generowanych stron HTML i organizacją ich treści (wypadając zazwyczaj lepiej od JavaDoca) a także drobnymi niuansami w formacie komentarzy dokumentacyjnych. Mniej widoczne ale bardzo istotne są inne różnice:

  • obsługa pełnej listy konstrukcji języka (np. zdarzają się problemy z obsługą template, nieelegancko prezentowane typy wyliczane itp);
  • możliwość dostosowywania do własnych potrzeb (od możliwości zadawania własnych szablonów stron po czytelność kodu i łatwość jego modyfikowania);
  • możliwość łączenia dokumentacji wielu bibliotek i programów (np. połączone hierarchie klas, odsyłacze pomiędzy bibliotekami).

Najsensowniej jest po prostu ściągnąć wszystkie te trzy narzędzia, użyć na jakimś rozsądnie dużym fragmencie rzeczywistego kodu i porównać wyniki. Sam aktualnie używam (trochę poprzerabianego - przy czym główny autor nie wszystkie moje zmiany docenił) kdoc-a - ale wybierałem 2 lata temu (gdy doxygen jeszcze był w powijakach a doc++ przeżywał jakiś kryzys i nie miał maintainera). Dziś doc++ i doxygen wydają się być rozwijane aktywniej niż kdoc i mogą być atrakcyjniejsze.

Na koniec uwaga na temat języka silnie z C++ spokrewnionego - IDL (specyfikacja interfejsów obiektów CORBA). Generowanie dla nich dokumentacji wspierają zarówno narzędzia niezależne (kdoc to robi, niewykluczone że doc++ i doxygen też - choć nie sprawdzałem), jak narzędzia dołączane do niektórych darmowych i komercyjnych implementacji CORBA-y.

Banalny przykład

O ile nie będę tu prezentował przykładowych wyników działania któregokolwiek z wymienionych narzędzi (bardzo dzielnie robią to na swoich stronach ich autorzy), o tyle pozwolę sobie na prościutki przykład kodu dokumentowanego w ten sposób:


/**
 * Klasa obsługująca komunikację przy pomocy protokołu
 * VSP (Very Strange Protocol - udokumentowany w ramach
 * RFC99999). Obsługuje niskopoziomową komunikację
 * - bezpośrednie wysyłanie i odbieranie paczek danych.
 *
 * Typowy schemat wykorzystania tej klasy obejmuje:
 * @li utworzenie obiektu;
 * @li wywołanie jednej z metod @ref Connect - nawiązanie
 *     połączenia
 * @li dowolną sekwencję wywołań metod @ref Send i @ref Recv
 * @li wywołanie metody @ref Close (jest też wołana automatycznie
 *     przy usuwaniu obiektu jeśli nie zrobiono tego wcześniej).
 *
 * Wyższego poziomu komunikację obsługują klasy @ref VspClient
 * i @ref VspServer.
 **/
class VspConnector
{
public:
 
  /**
   * Nawiązanie połączenia z zadanym serwerem. Umożliwia
   * późniejsze odbieranie i wysyłanie komunikatów.
   *
   * Jeśli obiekt jest aktualnie połączony z jakimś serwerem,
   * połączenie to zostaje zamknięte przed próbą nawiązania nowego.
   *
   * Wszelkie problemy są zgłaszane przy pomocy wyjątków (brak
   * wyjątku oznacza poprawnie nawiązane połączenie, wystąpienie
   * wyjątku brak takowego).
   *
   * @param machine maszyna na której działa serwer (nazwa DNS
   *       lub adres IP zapisany tekstowo)
   * @param port port IP serwera (domyślnie standardowy - 22222)
   * @return maksymalny dopuszczalny rozmiar komunikatów
   * @exception TcpException problemy z rozwikłaniem nazwy DNS
   *      lub nawiązaniem połączenia TCP
   * @exception ProtoException serwer nie obsługuje prawidłowo
   *      protokołu VSP
   **/
  unsigned Connect(const string& machine, int port);

  // ...
};

Zastosowanie w projektowaniu

Po wdrożeniu procedury automatycznego dokumentowania API, odpowiedni format i mechanizmy mogą być używane także jako element prac projektowych.

W mojej praktyce programistycznej, zabierając się do pisania programu czy biblioteki (tak w C++, jak w Javie czy nawet Perlu - gdy robię w nim coś większego), niemal zawsze zaczynam od zdefiniowania i udokumentowania API. W przypadku C++ polega to na przygotowaniu podstawowych plików nagłówkowych zawierających deklaracje funkcji, klas i metod oraz komentarze dokumentujące cele poszczególnych klas, operacje realizowane przez funkcje i metody, semantykę poszczególnych parametrów itp. Oczywiście w trakcie rozwijania kodu wszystko się będzie zmieniać ale napisanie kilkulinijkowej dokumentacji nie kosztuje wiele, ma za to istotne zalety:

  • wymusza przemyślenie architektury programu czy biblioteki, zdefiniowanie podstawowych pojęć itp;
  • pozwala na wyłapanie wielu niedoskonałości projektowych przed rozpoczęciem kodowania - a korygowanie API i komentarzy boli mniej niż wyrzucanie sporych gotowych fragmentów kodu;
  • umożliwia szybkie zanotowanie co dokładnie która funkcja ma robić, do czego służą poszczególne klasy itp - i to w miejscu, do którego w trakcie implementacji zawsze i często zaglądam
  • stanowi konkretną i dobrze zrozumiałą podstawę do dyskusji.

Podejście tego typu jest raczej uzupełnieniem ewentualnego projektowania przy pomocy diagramów UML (lub innej metodyki obiektowej, strukturalnej czy jeszcze innej) - sprowadzając wprowadzone na diagramach pojęcia na bardzo konkretny poziom. Zarazem, przynajmniej w prostych zastosowaniach, tego typu podejście może zastąpić całkowicie lub częściowo metody CASE.

Pozwolę sobie jeszcze na dwie, częściowo związane z tematem uwagi.

  • Uważam, że o ile programowanie bez tworzenia niezależnych dokumentów projektowych jest częstą i nieraz uzasadnioną praktyką, o tyle w takich projektach zamodelowanie ogólnej architektury przez naszkicowanie i opisanie kluczowych elementów API jest po prostu konieczne. Kod powstający metodą całkowicie "bitewną" może działać ale tworzony bez zastanowienia się nad architekturą całości - w pogoni za kolejnymi chaotecznymi koncepcjami - najczęściej robi równie chaotyczne wrażenie, jest trudny w używaniu i utrzymaniu i nie nadaje się do rozbudowy.
  • Będąc bardzo młodym programistą wierzyłem w możliwość kompleksowego projektowania przy pomocy technik CASE i zastanawiałem się, które narzędzie byłoby tym bajecznym (bo jakoś żadne z oglądanych - wbrew zapewnieniom sprzedawców - nie było). Mając już niejakie doświadczenie stwierdzam, że nie spotkałem się jeszcze z udanym projektem informatycznym zorientowanym wokół modelu a nie wokół kodu - i mam spore wątpliwości czy jest to możliwe, projekty muszą się toczyć iteracyjnie (jak to stwierdza Booch udany duży system zawsze opiera się na mniejszym działającym systemie, duży system implementowany od zera nigdy nie działa i nie może być doprowadzony do działania), a wciąż nie spotkałem narzędzia które pozwoliłoby na płynny powrót z kodu do modelu. Acz oczywiście cenię i używam rozmaite techniki analityczno-projektowe (choćby diagramy Use-Case czy kartki CRC - traktowane po prostu jako metoda dyskusji, albo diagramy klas i interakcji - używane jednak do modelowania fragmentów, które są na tyle skomplikowane, że warto je zamodelować).

Podsumowanie

Technikę automatycznego generowania dokumentacji referencyjnej ze źródeł programu można zacząć używać w ciągu kilku dni - i to nic za to nie płacąc. Nie jest to panaceum na wszelkie problemy z dokumentacją ale stanowi bardzo użyteczną metodę wspomagającą pracę zespołów informatycznych.

komentarze obsługiwane przez Disqus