Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: Piotr Szawdyński
Inne artykuły

Dobre praktyki wytwarzania oprogramowania

[artykuł] Artykuł omawia dobre praktyki wytwarzania oprogramowania w oparciu o 18 letnie doświadczenie w branży IT z punktu widzenia dewelopera/programisty. Artykuł nawiązuje do praktyk zalecanych w projektach domowych, praktyk stosowanych w projektach komercyjnych, zalety i wady poszczególnych rozwiązań związanych z organizacją projektu, organizacją pracy w projekcie itp.

Wprowadzenie

Dobre praktyki w wytwarzaniu oprogramowania to hasło, które wzbudza zainteresowanie zarówno amatorów programowania jak i profesjonalistów z dużym doświadczeniem. Przez dobre praktyki każdy może rozumieć coś innego - dla jednych jest to szybkość dostarczania produktów (czyli procedury w firmie działają efektywnie i produkty są zawsze dostarczane na czas), dla innych mała awaryjność produktu (czyli np. produkt jest dobrze przetestowany przed wypuszczeniem na rynek i ewentualne błędy są korygowane zanim produkt ujrzy światło dzienne), kolejne osoby mogą oczekiwać, że do projektu będzie można stosunkowo łatwo wdrażać nowe osoby (czyli projekt jest dobrze zorganizowany), dla jeszcze innych będzie to czytelność kodu źródłowego, pokrycie kodu testami jednostkowymi albo nawet stosowanie code review.

Innymi słowy: punkt widzenia zależy od punktu siedzenia. W niniejszym dokumencie przedstawię swój punkt widzenia w oparciu o 18 letnie doświadczenie związane z wytwarzaniem oprogramowania, w tym ponad 8 letnie w komercyjnych projektach.

Rozwiązania sprawdzone i skuteczne

Mieć i używać repozytorium

Niezależnie od tego czy pracujesz sam czy z kimś, zawsze organizuj swoje projekty na repozytorium. Do najbardziej popularnych repozytoriów należą SVN i GIT. W dzisiejszych czasach nie znajdziesz ani jednej poważnej firmy, która nie stosowałaby repozytorium do wersjonowania zmian w projekcie. Jest to standard bez którego obecnie żaden nowy biznes IT nie zaistnieje.

Posiadać jeden system do kontroli błędów

Dobrze mieć jakiś system kontroli błędów. Zdecydowana większość firm używa systemu kontroli błędów Jira. Jest to niestety produkt płatny, jednak jakość tego produktu jest tak wysoka, że firmy na rynku IT niemalże lawinowo przeszły z darmowych rozwiązań typu np. Mantis, Bugzilla na rzecz Jira.

Warto mieć świadomość, że system kontroli błędów to nic innego jak biurokracja, jednak biurokracja w komercyjnych projektach zawsze istnieje, a w domowych projektach czasami jednak chcemy ją równiez mieć po to, aby mieć miejsce w którym zechcemy opisać zauważone błędy, na które nie mamy aktualnie czasu spojrzeć.

Planowanie zawsze krótkoterminowe

Programista zawsze powinien skupiać się na planowaniu krótkoterminowym. Programista nie jest bowiem od tworzenia wizji rozwoju całej firmy, nie ma wiedzy na temat planów biznesowych rozwoju firmy, nie podpisuje kontraktów z których finansowane są między innymi wynagrodzenia programistów. Innymi słowy: programista jest narzędziem dla firmy, które ma realizować określone zadania. Długofalowy plan znają architekci/team leaderzy, którzy czuwają nad prawidłowym kierunkiem prac osób w zespole. Team leaderzy często bowiem biorą udział w ustaleniach biznesowych i oszacowują czas realizacji nowych projektów na bazie wieloletniego doświadczenia w wytwarzaniu oprogramowania jak i bardzo dobrej znajomości posiadanych produktów. Odcięcie się od dalekich planów pozwala łatwiej skoncentrować się na zadaniach bieżących, które i tak trzeba zrealizować, aby długofalowy plan zarządu mógł zostać zrealizowany.

Zalety stosowania krótkoterminowego planowania są wymierne, ponieważ można po krótkim odcinku czasu (np. po miesiącu) zweryfikować, czy projekt rozwija się we właściwym tempie i co ważniejsze, czy podąża on we właściwym kierunku. Przypuszczam, że z tych powodów coraz częściej spotyka się firmy, które stosują metodyki zwinne do zarządzania projektem (zapewne słyszałeś takie pojęcia jak Agile, czy też Scrum). Ponadto można na wczesnym etapie stwierdzić, czy oszacowania dotyczące czasu realizacji projektu są poprawne.

Zapamiętaj więc, że programista nie jest od tego, aby tworzyć długoterminowe plany.

Dzielenie dużych zadań na mniejsze

Jeżeli masz zadanie, które trzeba realizować dłużej niż miesiąc to koniecznie powinieneś podzielić je na mniejsze etapy (i zawsze się da). Pamiętaj jednak, aby nie rozbijać zadania na etapy liczone w godzinach. Jest bardzo mało zadań, które da się zrobić w godzinę. Jest sporo zadań, które da się zrobić w dzień (ewentualnie kilka dni).

Racjonalne planowanie własnej pracy

Jeżeli planowanie zajmuje Ci więcej niż realizacja zadań – zastanów się co jest ważniejsze: dobry plan czy zrealizowanie zadania. Niektóre rzeczy lepiej zaplanować ogólnie tj. wykonać mało szczegółowy plan w znacznie krótszym czasie i w trakcie realizacji je doprecyzowywać (w tym korygować założenia).

Wycenianie czasu realizacji zadania

Przed rozpoczęciem realizacji zadań związanych z wytwarzaniem lub aktualizacją kodu, często zachodzi potrzeba wyceny ich pracochłonności. Jeżeli projekt jest przez Ciebie już dobrze znany to mniej więcej jesteś w stanie oszacować ile czasu będziesz potrzebował na realizację nowych funkcjonalności. Pamiętać jednak należy, że nie wszystko da się wycenić. Nie da się wycenić np. czasu realizacji zmian w projekcie którego nie znamy i tym samym może się okazać, że trzeba po prostu improwizować.

Co robić, gdy projektu nie da się wycenić w krótkim czasie

Najważniejsze to zakomunikować fakt, że nie znamy projektu i nie jesteśmy w stanie jego wycenić. Można również dać informację, że projekt być może zajmie 2 tygodnie (jeżeli myślisz, że są szanse zrealizowania w tym czasie), ale po 2 tygodniach może okazać się, że będziemy potrzebowali kolejnych tygodni, bo po szybkich oględzinach kodu nie jesteśmy w stanie stwierdzić jakie mogą wystąpić problemy w trakcie realizacji zadania.

Powyższa argumentacja dla kierownika projektu czy też zarządu brzmi racjonalnie i zazwyczaj wszyscy są z takiej wyceny zadowoleni. Zarząd bowiem chce mieć jakąkolwiek estymację czasu, ponieważ chce wiedzieć czy projekt da się zrealizować w tydzień, w miesiąc czy też rok. Praca każdego pracownika przekłada się na budżet firmy, więc niektóre projekty mogą po prostu nie być realizowane, bo po prostu się to by firmie nie opłaciło.

Realizacja projektu, którego nie byliśmy w stanie wycenić

Jeżeli już dojdzie do realizacji projektu, którego nie znamy i nie byliśmy w stanie wycenić, to powinieneś skupić się na wykonywaniu zadań wg listy tego co trzeba zrobić. Jeżeli będziesz próbował najpierw zrozumieć jak działa cały projekt, to w konsekwencji skupisz się na planowaniu przyszłej pracy, zamiast realizować powierzone zadanie. W trakcie realizacji kolejnych zadań z listy mimowolnie będziesz uczył się projektu i kolejne modyfikacje pójdą Ci szybciej i sprawniej. Innymi słowy: czasem trzeba wykonywać iteracje nie znając projektu. Jest to mało komfortowa sytuacja, jednak jest ona znacznie bardziej skuteczna aniżeli oglądanie przez miesiąc kodu, po czym nastąpi przez Ciebie wycenienie realizacji zadania na kolejny miesiąc (albo i więcej).

Jak wyceniać zadania w projekcie dobrze znanym

Jeżeli robisz już wycenę czegokolwiek to pamiętaj: wyceniaj ZAWSZE z zapasem. Szybciej skończysz? Super! Szybciej weźmiesz kolejne zadanie i tym samym będziesz miał większy zapas czasowy na kolejne zadanie, którego być może ktoś nie doszacował. Nie skończysz w wyznaczonym czasie? Czekają na Ciebie już kolejne zadania! W tym drugim przypadku zaczyna rosnąć presja ze strony zarządu, że produkt jeszcze nie jest skończony, a w Tobie może zrodzić się poczucie niewywiązywania się z obowiązków, które wcale nie poprawi jakości Twojej pracy.

Kultura pracy z repozytoriami

Poniżej wyszczególniłem najważniejsze zasady pracując z repozytoriami:
  • Mieć i używać repozytorium (GIT/SVN);
  • Używaj go zawsze – niezależnie czy pracujesz sam czy z kimś.
  • Opisuj commity nawet jeżeli pracujesz sam – wyrabiasz wówczas tzw. dobre nawyki.
  • Opis to zazwyczaj jedno zdanie, które dodatkowo poprzedza się numerem zgłoszenia z systemu używanego do kontroli błędów (np. Jiry).
    Przykład opisu: ENGINE-1234: Fixup! Keyboard was not responding when low FPS occurred.
  • Nie ma zadania w Jirze? Wystarczy krótki opis w commicie.
  • Nie twórz wielu kopii projektu na boku (wszystkie źródła mają być w repozytorium).
  • Zanim wdrożysz zmianę na repozytorium - przetestuj czy kod się kompiluje i czy funkcjonalność z grubsza działa.
  • Nie wdrażaj zmian na repozytorium, których nie przetestowałeś - pamiętaj, że inni też pracują z tym kodem i możesz przez niedbalstwo nieumyślnie zablokować innym pracę.
  • Zanim wykonasz commita - sprawdź jakie są zmiany w plikach, które commitujesz. Czasami zapomina się o wstawionych komunikatach diagnostycznych (które są na produkcji zbędne). Zapomina się o zmienionej wartości zmiennej, która ułatwiła testowanie kodu. Zdarza się zapominać o tym, że wycięliśmy np. jakąś funkcjonalność po to, aby ułatwić testowanie swojej funkcjonalności.

Jak wygląda praca przy komercyjnych projektach

Spora część firm na obecnym rynku pracy organizuje pracę w zespołach przy użyciu metodyk zwinnych. Innymi słowy aktualnie jest wszechobecny Scrum.

Na czym polega Scrum

Metodyka zwinnego zarządzania projektem polega przede wszystkim na:
  • planowaniu sprintów (sprint jest to jeden cykl wytwarzania oprogramowania)
  • sprint zawyczaj trwa od 2 do 4 tygodni - firma wewnętrznie ustala jaka długość sprintu najbardziej odpowiada w danym zespole lub firmie (długość sprintu nie jest zmieniana);
  • w dniu rozpoczęcia nowego sprintu ustala się jakie zadania powinno udać się zrealizować w ramach sprintu;
  • po ustaleniu zadań jakie będą realizowane w sprincie, żadne nowe zadanie nie może zostać dodane do sprintu w trakcie jego trwania (w końcu cały czas trwania sprintu został teoretycznie zaplanowany) - ta reguła jest niewygodna dla kierownictwa i w praktyce często się robi od niej odstępstwa;
  • każdego dnia jest standup o stałej godzinie, który trwa zazwyczaj około 15 min (na standup każdy w zespole mówi w skrócie co zrobił dnia poprzedniego i co planuje robić w dniu dzisiejszym);
  • codzienne spotkania są dobrym miejscem komunikowanie o ewentualnych problemach technicznych, być może od ręki ktoś coś poradzi lub zaoferuje wsparcie po zakończeniu spotkania;
  • każdy sprint kończy się podsumowaniem efektów prac i ustala się termin planowania kolejnego sprintu.
Z grubsza tak wygląda metodyka Scrum - szczegóły możesz sobie zawsze gdzieś doczytać.

Problemy związane z metodologią Scrum

Każda metoda zarządzania projektami ma swoje wady, w tym również i Scrum. Największym problemem jest fakt, że firmy często próbują wdrożyć omawianą metodykę zarządzania projektami na siłę, choć ewidentnie nie pasuje ona do charakteru pracy zespołu.

Przykład

Mamy zespół, który zajmuje się utrzymaniem projektu oraz jego rozwojem. Zadaniem tego zespołu jest dostarczanie nowych usług oraz naprawianie usług, gdy przestaną one nagle działać. Pracujemy w metodyce Scrum, więc planujemy sprint i rozpoczynamy jego realizację. W środku trwania sprintu pojawia się pilne zgłoszenie - usługa za którą płacą klienci przestała działać. Zgodnie z metodyką Scrum nie powinno się ingerować w listę zadań będących w sprincie. Oznacza to, że z formalnego punktu widzenia nikt nie powinien usuwać awarii aż do zaplanowania jej w następnym sprincie. Z punktu widzenia biznesu taka sytuacja jest niedopuszczalna, ponieważ firma zarabia na usłudze, która aktualnie nie działa. Usługa nie działa, do firmy nie płyną pieniądze.

Rozwiązanie problemu

Taki problem rozwiązuje zastosowanie innej metodyki zwanej Kanban. Metodyka Kanban jest dostosowana do utrzymania i rozwoju projektów. Metodyka Scrum bardzo dobrze się sprawdza, ale tylko do rozwoju tej części oprogramowania, która nie jest jeszcze dostępna na rynku oraz istnieje zespół, który zajmuje się bieżącym utrzymaniem produktów tzw. produkcyjnych.

Praca przy projektach komercyjnych

  • Kod prawie nigdy nie wygląda tak jak byśmy sobie to wyobrażali.
  • Trzeba się dostosować do standardu jaki obowiązuje w danej firmie (co firma to inny standard).
  • Kod w komercyjnych projektach zazwyczaj się ciężko czyta (a czytać trzeba, aby zrozumieć jak on działa).
  • Czasem czytanie kodu wcale nie pomaga zrozumieć sensu działania aplikacji (bo warunki opierają się np. o ustawy zmieniające się w czasie).
  • Jest dużo, a nawet bardzo dużo kodu.
  • Projekt w praktyce zawsze zależy od bibliotek 3rd-party (np. zlib, boost, libpng, sqlite itd.).
  • W każdym systemie kontroli błędów znajdują się zgłoszenia, które czekają na realizację rok albo i dłużej (bo np. udało się odtworzyć błąd, ale nie udało się znaleźć jego przyczyny, lub np. cały czas są ważniejsze zgłoszenia).
  • Zasady wytwarzania kodu są odgórnie narzucone.
  • Większość z nich dotyczy formatowania kodu (stosowane konwencje nazewnicze itp.).
  • Czasem standard firmy mówi o tym jak nazywać zmienne, funkcje itd. (generalna zasada: maksymalnie krótka nazwa, która niesie maksymalnie dużą informację do czego służy funkcja/metoda lub co przechowuje zmienna).
  • Właściwa nazwa metod, funkcji i zmiennych = kluczowy element dokumentowania kodu.
  • Czasem standard określa gdzie powinny być spacje, a gdzie nie (to jest irytujące) i z tego powodu często modyfikacja nie przechodzi review, jeżeli firma je stosuje.
  • Prawie nigdy nie ma wzmianki o tym, aby robić śladową dokumentację do tego, co robi dany kod.
  • Nawet jeżeli jest wzmianka o dokumentacji – zdecydowana większość programistów albo jej nie pisze, albo jej jakość jest tak niska, że opis mówi mniej, niż sama nazwa metody/funkcji.
  • Dokumentowanie kodu w postaci komentarzy często jest pisane w oparciu o posiadaną wiedzę o projekcie na daną chwilę (czyli gdy wszystko jest oczywiste, bo projekt jest świeży). Autor dokumentacji nie zastanawia się, że za pół roku być może coś trzeba będzie poprawić w tym kodzie. Konsekwencja: dokumentacja jest bezużyteczna zarówno dla autora jak i innych osób czytających kod. Dlaczego? Bo była wykonana niedbale i prawdopodobnie użyte zostały w niej słowa kluczowe jakich się używało podczas realizacji modyfikacji zamiast łopatologicznego opisu.
  • Jeżeli jest już prowadzona dokumentacja w kodzie to zazwyczaj jest w języku angielskim (zakłada się bowiem, że będziemy chcieli mieć zespół międzynarodowy).
  • Zazwyczaj nie prowadzi się osobnej dokumentacji do projektu (chyba, że kontrakt tego wymaga).

Kultura pracy w projektach IT

Nigdy nie wyrażaj ostrej krytyki na temat jakości zastanego kodu. Dlaczego? W większości przypadków nie wiesz pod jaką presją czasową był wytwarzany kod oraz w jak krótkim czasie modyfikacja musiała być zrealizowana. Prawdopodobnie nie wiesz również jakie standardy obowiązywały w firmie w latach, gdy kod powstawał oraz jakie były dostępne zasoby technologiczne.

Przykład

Kilka lat temu procesory były dużo słabsze, kompilatory miały znacznie mniej optymalizacji, IDE do programowania było znacznie gorsze i wolniejsze. Kilka lat temu była znacznie mniejsza wiedza o metodach wytwarzania aplikacji, był gorszy system zarządzania błędami, trzeba było dostarczać oprogramowanie działające np. na Windows NT, aplikacja miała działać szybko na bardzo słabych komputerach (np. jeden rdzeń 400MHz, 256MB ram na system i aplikacje) itd.

Teoria vs praktyka

  • W teorii mamy mechanizmy podnoszące jakość kodu - w praktyce spotykamy uciążliwy dla programistów proces, który maksymalnie spowalnia czas wytwarzania oprogramowania.
  • W teorii mamy standardy wytwarzania kodu - w praktyce mamy większość kodu, która odbiega od standardu (np. zaszłość historyczna).
  • W teorii powinien być jeden system kontroli błędów - w praktyce zdarzają się firmy, w których systemów kontroli błędów jest kilka (i każdy dział ma swój).
  • W teorii kod powinien być czytelny i wysokiej jakości - w praktyce kod często jest taki, że jego zrozumienie zajmuje ładnych kilka godzin zanim cokolwiek uda się w nim poprawić.
  • W teorii istnieją profesjonalne narzędzia do wytwarzania kodu - w praktyce mało kto je posiada lub używa (bo np. barierą jest koszt licencji).

Wnioski

  • Każda firma ma swoje mocniejsze i słabsze strony.
  • Narzekać zawsze można, ale jak to mawiają "chcesz aby było po Twojemu, załóż własną firmę".
  • Eksperymenty odnośnie metod wytwarzania oprogramowania możesz prowadzić na prywatnych projektach (w końcu nic za to nie płacisz poza swoim czasem).
  • Każda firma uczy się i rozwija adekwatnie do posiadanych projektów, wymagań biznesowych oraz zasobów finansowych.
  • Zarządzanie projektami IT wcale nie jest łatwe - wraz z nabieraniem doświadczenia na pewno zauważysz to.
Problem rozrastającej się firmy i projektu bardzo fajnie został opisany w artykule 'historia o mrówce':
https:/​/strefamanagera.wordpress.com​/2012/10/14​/odwrocona-piramida-czyli-histo​ria-o-mrowce​/
Jeżeli link nie działa, to wspomniany tekst można znaleźć np. pod hasłem: mrówka lew praca bajka w wyszukiwarce Google.

Prowadzenie projektów domowych

Przez ostatnie 18 lat kodowania projektów w zaciszu domowym, które w późniejszym okresie były (i nadal są) przeplatane komercyjnymi projektami, zaobserwowałem wiele problemów związanych z organizacją projektów w firmach. Sam dostrzegałem również problemy w projektach domowych, więc poszukiwałem inspiracji albo w tym jak poradziła sobie firma, w której aktualnie pracowałem lub na własną rękę szukałem lepszego rozwiązania. Zebrane wnioski przedstawiłem i po krótce opisałem poniżej.

Wszystkie projekty w jednym repozytorium

Trzymaj wszystkie własne projekty w jednym repozytorium, a przynajmniej wszystkie projekty związane z jednym językiem programowania.

Dokumenty niezwiązane z programowaniem

Chcesz repozytorium na pisane dokumenty - super! Chcesz dorzucić do niego swoje CV, pracę dyplomową i inne dokumenty jakie od czasu do czasu trzeba napisać lub zaktualizować? Jeszcze lepiej! Posiadanie takiego repozytorium to dobra sprawa. Można w nim zapisać wyeksportowaną konfigurację używanego IDE (w sam raz do zaimportowania ustawień po reinstalacji systemu). Można w nim trzymać konfiguracje serwera Apache, języka PHP i innych dokumentów, które po prostu chcesz mieć uporządkowane i są dziełem Twojej własnej pracy. Jedno repozytorium w zupełności wystarczy Ci do tego.

Projekty związane z programowaniem

Moje doświadczenie mówi: jedno repozytorium na jeden język programowania to dobra sprawa. Jedno repozytorium na jeden projekt to fatalne rozwiązanie. Powód jest dość prosty: w domowym zaciszu robi się dużo projektów, które są zazwyczaj niewielkie. Mając sensownie zorganizowane repozytorium wszystkie projekty masz w jednym miejscu. Żadne repozytorium Ci nie zginie, bo wiesz, że zawsze pracujesz z jednym repozytorium, w którym masz projekty np. C++. Mając wiele repozytoriów do jednego języka programowania zaczynasz duplikować kod. Robisz modyfikację w jednym projekcie - kopiujesz przydatne narzędzia z drugiego projektu. Po kilku miesiącach kodowania okazuje się, że zmiany w drugim projekcie mogą się też przydać w projekcie pierwszym i zaczynasz w konsekwencji mieć bałagan, nad którym prędzej czy później przestaniesz panować. Zapewne w pewnym momencie wpadniesz na pomysł, na który ja wpadłem, czyli założyłem trzecie repozytorium, do którego wrzuciłem dwa poprzednie projekty. Takim sposobem uzyskałem w krótkim czasie 3 repozytoria, gdzie teoretycznie jedno zawiera źródła z dwóch pozostałych. W praktyce jednak nie chce się robić gruntownych porządków we wszystkich projektach, więc... recepta na bałagan jest gotowa. Jeżeli chcesz uniknąć problemów z organizacją własnych projektów, poświęć na początku dużo energii we właściwą organizację projektów na jednym repozytorium - dzięki temu nie będziesz musiał robić porządków w przyszłości, które są nudne, męczące i czasochłonne.

Biblioteki 3rd-party

Nigdy nie trzymaj bibliotek 3rd-party w tym samym repozytorium co własne projekty. Biblioteki 3rd-party co jakiś czas trzeba aktualizować, więc nie chcesz robić 'śmieciowych' commitów na swoim głównym repozytorium z projektami. Jeżeli będziesz pracował pod Linuxem to prawdopodobnie będziesz używał polecenia grep do przeszukiwania źródeł projektu. Zapewne nie chcesz, abyś dostawał wyniki wyszukiwania ze źródeł 3rd-party.

Projekty 3rd-party powinieneś zawsze kompilować ze źródeł. Samodzielna kompilacja bibliotek gwarantuje Ci, że biblioteki zawsze będą się poprawnie linkowały z Twoimi projektami. Ponadto przejście na nowszy kompilator nie będzie stanowiło żadnego problemu - wszystko możesz bowiem od ręki skompilować ponownie.

Wypracuj sobie system szybkiej kompilacji wszystkich bibliotek, jakie masz w 3rd-party. Najlepiej w wersji Debug i Release. Biblioteki 3rd-party zawierają bowiem czasami błędy, które trzeba samodzielnie poprawić.

Nigdy nie kopiuj bibliotek 3rd-party do repozytorium z własnymi projektami - wykonaj poprawną konfigurację projektów określając gdzie kompilator ma szukać plików nagłówkowych i bibliotek statycznych.

Biblioteki dynamiczne w najgorszym przypadku będziesz musiał kopiować z 3rd-party do katalogu, w którym będą się generowały Twoje aplikacje (biblioteki dynamiczne *.dll muszą się bowiem znajdować w katalogu obok aplikacji *.exe). Jeżeli chcesz uniknąć kopiowania bibliotek dynamicznych za każdym razem - skonfiguruj projekty w 3rd-party tak, aby pliki *.dll były generowane do katalogu np. _bin_. Katalog _bin_ w rzeczywistości może być bowiem linkiem symbolicznym na katalog, w którym generowane są pliki wykonywalne z Twoich projektów.

Wszędzie gdzie się da stosuj linkowanie statyczne (mowa o C++). Nie trzeba będzie wówczas dostarczać dodatkowych bibliotek niezbędnych do uruchomienia aplikacji. Ponadto linker wytnie kod, który nie został użyty, dzięki czemu sumarycznie aplikacja będzie zajmowała mniej miejsca na dysku i będziesz miał mniej problemów z jej publikacją.

Określ standardy kodowania

Tworząc własne projekty zadbaj o to, abyś wszystkie prowadził w jednym standardzie. Przez standard należy rozumieć przede wszystkim stosowaną konwencję nazewniczą i sposób dokumentowania kodu. Nie wymyślaj raczej koła na nowo – wyszukaj jakiś standard konwencji nazewniczej i stosuj ją we wszystkich projektach. Jeżeli z jakiegoś powodu chcesz zrobić zlepek z różnych standardów - nie ma problemu. Zadbaj jednak o to, aby standard został spisany przed rozpoczęciem prac nad różnymi projektami. Zapisz go również gdzieś na repozytorium, aby był on pod ręką (np. w katalogu docs ze źródłami Twoich projektów). Nie próbuj również dokumentowania kodu w stylu doxygen – to się nie sprawdzi - tylko się zniechęcisz do robienia jakiejkolwiek dokumentacji. Tworząc standard pamiętaj, aby zostawić jakąś minimalną swobodę i nie określać niepotrzebnie standardu do wszystkiego co wymyślisz. Wyjdź z założenia, że standard powinien być maksymalnie prosty do zapamiętania. Raczej nikt podczas pisania projektu nie będzie co i rusz sięgał do projektu, bo np. ustalisz restrykcje odnośnie długości zmiennych, bądź zdefiniujesz n-dziesiąt prefiksów, które Twoim zdaniem powinny określać typ zmiennej. Minimalizuj konieczność zapamiętywania rzeczy opisanych w standardzie do minimum. Niech pisanie kodu będzie oczywiste, a nie wydumane.

Dbaj o aktualizacje standardu

Czasem się zdarza tak, że w standardzie znajdują się zapisy nieżyciowe, bądź czegoś w nim nie uwzględniliśmy. Wówczas standard należy zaktualizować po to, aby wyeliminować nieżyciowe zapisy bądź uzupełnić go o bardziej zrozumiałe przykłady. Jeżeli standard będzie zawierał nieżyciowe zapisy to prędzej czy później nie będziesz go używał. Brak standardu z kolei będzie powodował, że prawdopodobnie będziesz zmieniał konwencję wraz z tym, w jakiej firmie aktualnie pracujesz.

Dbaj o dokumentowanie kodu

Dokumentowanie kodu jest wbrew pozorom bardzo opłacalne we wszelkiego rodzaju projektach. Polecam dokumentować funkcje/metody jedną/dwoma linijkami tekstu. Krótki komentarz ułatwia zrozumienie działania funkcji. Ponadto jak się wraca do projektu po kilku miesiącach przerwy znacznie łatwiej go zrozumieć. Jeżeli nie nauczysz się dokumentować własnego kodu to prawdopodobnie po kilku miesiącach przerwy od projektu nie będziesz wiedział po co dany kod został napisany. Ponadto kod z miejsca wyda Ci się albo nieoptymalny albo nieintuicyjny, co spowoduje, że przekreślisz n-miesięcy swojej przeszłej pracy i zaczniesz go pisać od nowa.

Dokumentując kod używaj prostych sformułowań – wyjdź z założenia, że terminów i tak nie będziesz pamiętał za kilka miesięcy. Pisz w kodzie komentarze, jeżeli uważasz, że się mogą przydać w razie nagłej potrzeby poprawienia kodu w wyniku znalezienia buga. Pisz komentarze tym bardziej jeżeli pracujesz nad wieloma projektami. Analiza starego kodu kosztuje dużo czasu – pisz komentarze zawczasu tam gdzie uważasz za słuszne, aby zredukować czas potrzebny na analizę w przyszłości.
Do dokumentowania kodu polecam używać tylko i wyłącznie komentarzy jednowierszowych – komentarz wielowierszowy zostaw dla innego programisty (oraz dla siebie) jeżeli będziesz chciał wyciąć jakiś fragment kodu i pisać na jego podstawie 'lepszy' algorytm.

Staraj się dokumentować kod choć w minimalnym stopniu. Jeżeli nie masz ochoty dokumentować wszystkiego, to zadbaj chociaż o to, aby wszystkie metody i funkcje publiczne posiadały chociażby śladową dokumentację.

W jakim języku dokumentować kod?

Osobiście polecam pisać dokumentację w języku ojczystym (czyli w polskim). Powód jest prosty: większość z nas słabo pisze po polsku, a co dopiero w innym języku. Najpierw powinieneś nauczyć się pisać poprawnie po polsku, a dopiero potem możesz myśleć o pisaniu dokumentacji w innym języku. Ponadto łatwiej będzie Ci zrozumieć po kilku miesiącach dokumentację polską, aniżeli angielską - dysponujesz bowiem znacznie bogatszym słownictwem i lepiej kojarzysz znaczenia słów z ojczystego języka. Jest również wielu przeciwników pisania w języku innym niż angielski - w komercyjnych projektach jeżeli jest już dokumentacja to tylko angielska. Powód: zespoły są często międzynarodowe. Jeżeli chcesz więc ćwiczyć w domu pisanie dokumentacji w języku angielskim - Twój wybór. Osobiście jednak do dokumentowania kodu za pomocą komentarzy preferuję język polski.

Unikaj refaktoryzacji kodu

Unikaj refaktoryzacji kodu. Refaktoryzacje są bardzo kosztowne czasowo. BARDZO.

Jeżeli komponent działa – nie ruszaj go. Jest brzydko napisany? Nie ruszaj go (bo działa). W tym czasie możesz napisać coś nowego, a refaktoring tylko i wyłącznie zabierze Ci czas na coś co i tak działa. Po refaktoringu kod będzie zwracał ten sam wynik, więc po co masz poświęcać dzień, tydzień, miesiąc na refaktoring skoro można ten czas wykorzystać na rozwój projektu?

Dbaj o wysoką jakość kodu na bieżąco – nie będziesz musiał poświęcać później czasu na ewentualne refaktoryzacje.

Zadbaj o profesjonalne narzędzia pracy

Profesjonalne narzędzie pracy to jeden z kluczowych elementów efektywnej pracy z projektami. Jeżeli kodujesz w C++, polecam Visual Studio (wystarczy wersja Professional). Jeżeli kodujesz w PHP, polecam PHP Storm. Pracujesz z bazami danych MySQL? Używaj MySQL Workbench. Pracujesz z Pythonem? Używaj PyCharm.

Zdobywaj wiedzę jak korzystać efektywnie z narzędzi dostępnych w profesjonalnych IDE.

Zbieraj dobre praktyki we własnych projektach

Wraz ze zdobywaniem doświadczenia zawodowego staraj się zbierać dobre praktyki i wdrażać je do własnych projektów. Jeżeli dobrych praktyk nie będziesz używał w domowych projektach to prawdopodobnie część z nich przepadnie po zmianie pracy i po pewnym czasie o nich zapomnisz lub będziesz je kojarzył jak przez mgłę.

Aktualizuj swoją wiedzę

Pamiętaj, aby nie zaniedbywać aktualizacji swojej wiedzy. Ucz się wysokich standardów wytwarzania oprogramowania i dobrych praktyk oraz stosuj je w praktyce.

Branche, Unit testy i code review

Branche

Tworzenie branchy w prywatnym projekcie rozwijanym w oparciu o SVN mija się z celem (przynajmniej dopóki nie ma się produktu w wersji produkcyjnej). Pracuj więc na głównej gałęzi i commituj tam wszystko jak leci. W przypadku git-a branche mogą się przydać w przypadku pracy wieloosobowej, ponieważ GIT wprowadza nieco więcej chaosu w commitach. SVN ma to do siebie, że wszyscy deweloperzy w projekcie widzą ten sam stan głównego repozytorium i w praktyce zawsze pracują na ostatniej wersji. W SVN nie da się skasować tego co zostało już wcommitowane i to jest całkiem dobra własność. W przypadku GIT-a każdy deweloper ma własną kopię repozytorium. Ponadto zazwyczaj GIT jest skonfigurowany tak, że można nadpisać repozytorium zdalne wersją lokalną. Raczej nie chciałbyś utracić swoich zmian, bo kolega z zespołu nadpisał repozytorium zdalne no nie? :) Można też GIT-a skonfigurować tak, aby nie dało się nadpisywać zmian na repozytorium zdalnym, ale i tak GIT jest stworzony do tworzenia wielu branchy, scalania zmian do głównej gałęzi i kasowania gałęzi już scalonych do głównej gałęzi.

Unit testy

Unit testy i code review w prywatnych projektach nie mają sensu. W prywatnym projekcie jesteś jedynym deweloperem, który ciągnie projekt do przodu – robiąc unit testy, zatrzymujesz projekt na jakiś czas. Chcąc zminimalizować czas na robienie unit testów, będziesz robił je mało użyteczne (małe pokrycie kodu), bo będziesz dążył do rozwoju projektu, a nie automatycznego testowania tego co napisałeś. Nawet jeżeli będziesz robił projekt ze znajomymi 'po godzinach' to 'po godzinach' nie będziesz miał drugiego etatu na robienie projektu zgodnie ze sztuką wynikającą z unit testów.

Code review

Pracując samemu nad projektem nie zrobisz po sobie code review. Nawet jeżeli będziesz robił projekt ze znajomymi po godzinach to i tak code review się nie sprawdzi. Prawdopodobnie każda osoba w zespole będzie rozwijała projekt w różnych dniach i godzinach. Ponadto jak już ktoś będzie chciał coś robić to będzie to kodowanie, a nie czytanie kodu i wytykanie w nim błędów zanim wejdzie on do głównej gałęzi repozytorium.

Unit testy

Unit testy służą do tego, aby automatycznie testować poprawność działania poszczególnych komponentów w kodzie. Pisanie unit testów jest kosztowne czasowo i należy mieć tego pełną świadomość. Unit testy trzeba utrzymywać tj. trzeba je adoptować do zmian wprowadzanych w projekcie. Zazwyczaj unit testy aktualizuje się równolegle wraz z modyfikacją konkretnej funkcjonalności. Unit testy są bardzo przydatne w projektach dojrzałych, tj. w takich, które generują już zyski i konieczne jest wprowadzanie nowych modyfikacji. Pokrywanie kodu unit testami pozwala na podnoszenie jakości finalnego produktu. Jakość w tym wypadku należy rozumieć przez mniejsze prawdopodobieństwo wystąpienia nieoczekiwanego błędu, ponieważ w chwili wystąpienia błędu najpierw błąd zostanie namierzony, a z chwilą jego naprawy rozbudowuje się unit test tak, aby wykrywał w przyszłości scenariusz, który spowodował wystąpienie błędu w aplikacji. Unit testów w komercyjnych projektach jednak często nie ma, ponieważ ich tworzenie jest kosztowne czasowo. Z mojego doświadczenia wynika, że unit testów nie opłaca się robić w projektach, które są małe i nie przekładają się w żaden sposób na zyski firmy. Programiści oczywiście chętnie by je mieli, chętnie by je pisali (w pracy), ale nie ukrywajmy - to jest duży koszt czasowy. Tylko firma, która zarabia swoimi produktami na cały biznes może pozwolić sobie na robienie unit testów w celu podnoszenia jakości swoich produktów. W domowych projektach nie będziesz chciał ich tworzyć właśnie ze względu na wysokie koszty czasowe ich wytworzenia oraz ich późniejszego utrzymywania. Uruchamianie unit testów powinno być zautomatyzowane tj. po każdym commicie buildbot powinien skompilować ze źródeł kod i uruchomić testy jednostkowe. W przypadku pojawienia się błędnych wyników w testach jednostkowych raport powinien zostać wysłany do osób rozwijających dany projekt. Posiadanie takiego mechanizmu w domowym zaciszu jest w moim przekonaniu zbyt kosztowne. Dla wielu małych firm również jest to koszt, który moim zdaniem firma będzie wolała przenieść w inne miejsce, np. na rozwój produktu, który chce wypromować na rynku.

Unit testy są dobre, jednak z mojego punktu widzenia znajdują one zastosowanie dopiero na poziomie korporacyjnym lub w dobrze prosperującej firmie, którą po prostu stać na taką inwestycję. Koszt poniesiony na testy jednostkowe zwraca się w długoterminowym rozwoju projektu - w szczególności, gdy jesteśmy narażeni na rotacje pracowników przy projekcie.

Code review

Kolejnym wynalazkiem, który ma na celu podnoszenie jakości produktu jest tzw. code review, czyli przeglądanie kodu kolegów z zespołu zanim zmiany trafią do repozytorium. Podczas takiej weryfikacji sprawdza się zgodność formatowania z obowiązującymi standardami, analizuje się czy kod jest napisany zrozumiale oraz czy wiadomo o co w nim chodzi. W przypadku niejasności wyjaśnia się je z autorem zmian i zleca się ewentualne poprawki jakościowe. Deweloperzy weryfikujący kod mają za zadanie również przeanalizować algorytmy czy np. zmiany nie spowodują wystąpienia błędu krytycznego aplikacji (praca na niezaincjalizowanym wskaźniku) lub czy programista zadbał o to, aby nie wystąpiła nieskończona pętla. Code review bez wątpienia podnosi jakość kodu, jednak w większości projektów nie powinien mieć on racji bytu. Code review generuje ogromne koszty czasowe, spowalniając wytwarzanie oprogramowania do tego stopnia, że funkcjonalności, które powinny powstać w jeden dzień i zostać oddane do użytku potrafią być wdrażane tydzień albo i dłużej. Code review wymaga weryfikacji kodu zazwyczaj przez co najmniej dwóch deweloperów, w tym zazwyczaj musi być co najmniej jeden senior, czyli osoba w zespole z bardzo dużym doświadczeniem (i wiedzą na temat produktu). Oznacza to, że w implementację zaangażowany jest jeden pracownik, a w weryfikację co najmniej dwóch pracowników. Tych dwóch pracowników ma za zadanie przeanalizować kod czy wygląda on poprawnie oraz czy powinien on działać dobrze. Tych dwóch pracowników musi znaleźć czas pomiędzy swoimi zadaniami, aby zrobić innym pracownikom code review. Tych dwóch pracowników prawie zawsze zgłosi jakieś uwagi do kodu. Uwag tych nie otrzymuje się od ręki - modyfikacja zazwyczaj czeka minimum 24h na weryfikację przez inne osoby (w najlepszym wypadku można próbować przyśpieszyć takie weryfikacje poprzez osobiste prośby). Opisany proces potrafi być powtarzany kilkukrotnie zanim modyfikacja zostanie wdrożona do głównej gałęzi repozytorium. Od chwili gdy deweloper utworzył pierwszą wersję mijają kolejne dni, w między czasie wchodzą na główną gałąź inne poprawki, a potem jak przychodzi do chwili, w której deweloper dostanie zielone światło okazuje się, że musi rebaseować swoje repozytorium bo kod w między czasie się tak zmienił, że musi ponownie zweryfikować, czy wprowadzone zmiany poprawnie nałożą się na główną gałąź.

Po długim i męczącym procesie code review - zarówno dla autora zmian jak i osób, które musiały czytać ten kod - przychodzi chwila prawdy - wynik testowania aplikacji przez zespół testujący bądź testy automatyczne. Bardzo często okazuje się, że pomimo stosowania code review aplikacja nie działa zgodnie z założeniami i po tygodniu wdrażania jednej zmiany (napisanej w jeden dzień) trzeba:
  • przeanalizować przyczynę wystąpienia błędu;
  • napisać poprawkę;
  • oddać do review;
  • poprawiać uwagi z review aż do skutku.

Podsumowując, z mojego punktu widzenia taka forma code review jest zbyt kosztowna dla przytłaczającej większości projektów. Mogę się zgodzić, że code review ma sens w produkcie takim jak Chromium Engine. Produkt ten ma bowiem miliony linii kodu, a ilość użytkowników tego produktu idzie w setki milionów osób rozproszonych po całym świecie. Dobrze jest, jeżeli firma jest świadoma kosztów jakie niesie ze sobą stosowanie code review w takiej formie.

UML, a projekty w firmach

W pracy zazwyczaj nie powstają modele analityczne UML do realizacji projektu (w zasadzie to nie tworzy się ich wcale – chyba, że kontrakt tego wymaga lub ułatwi to komunikację z klientem). Są osoby, które twierdzą, że to bardzo źle (w zasadzie są to teoretycy, którzy chcą projektować oprogramowanie). W praktyce tylko jedna firma w której pracowałem modelowała diagramy w UML (i tylko dlatego, że wymagał tego kontrakt, a nie dlatego, że ktoś z deweloperów/analityków uważał, że jest to użyteczne).

Problem z modelami UML jest taki, że zawsze są one zbyt ogólne i praktycznie zawsze pomijają jakiś istotny szczegół, który wychodzi w najgorszym przypadku w trakcie kodowania. Znacznie częściej jednak błędy w założeniach wytyka deweloper zaraz po zapoznaniu się z diagramem klas. Niski poziom szczegółowości i ogólne spojrzenie analityczne powoduje, że w konsekwencji projekt nigdy nie jest zgodny z modelami klas UML. Programista ma większą wiedzę o wytwarzaniu oprogramowania niż analityk, który chce zaprojektować aplikację na poziomie UML-a. Programista widzi więcej szczegółów, ponieważ wszystkie problemy musi rozwiązać. Analityk na poziomie UML tylko może dostarczyć szkic i przekonywać, że w teorii wszystko działa, a w praktyce niestety prawie nigdy tak nie jest. Programista sam umie zrobić sobie teoretyczny szkic, więc nie potrzebuje do tego analityka, który będzie wmawiał programiście, że dostarczony projekt jest idealny. Zgodzę się, że UML to fajna teoria, ale tylko do przedstawiania koncepcji w gronie deweloperów IT. Innymi słowy UML może przydać się do wymieniania koncepcjami pomiędzy deweloperami jak jakiś problem rozwiązać. UML powinien być używany co najwyżej przez osoby, które fizycznie oglądają kod i z nim pracują (utrzymują go i rozwijają). Jest oczywiście również modelowanie na poziomie przypadków użycia (Use cases) i to jest coś co może, a nawet powinien dostarczyć analityk. Diagram przypadków użycia określa bowiem oczekiwania klienta wobec produktu i nie określa on w praktyce jak ma być zbudowany kod.

Pomimo, że teoria UML ładnie się prezentuje, to jednak firmy znacznie częściej dostarczają wymagania w postaci opisu tekstowego. Opis tekstowy zajmuje mniej miejsca, jest bardziej precyzyjny i szybciej się go tworzy. Jeżeli firmę stać na to, aby z opisu wygenerować diagram przypadków użycia - to fajnie. Programista to doceni. Zazwyczaj się tego jednak nie robi, bo robienie diagramu przypadków użycia to nic innego jak wykonywanie dodatkowej pracy, która i tak opisuje coś co już zostało opisane w postaci dokumentu/zgłoszenia/umowy. Warto rozważyć zrobienie diagramu przypadków użycia jeżeli firma chce się upewnić, czy oczekiwania klienta zostały właściwie spisane i czy są one właściwie zinterpretowane.

Projekty prywatne, a praca w grupie

Z racji swojego doświadczenia zdarzyło mi się kilka razy pracować w niekomercyjnych projektach grupowych. Poniżej zamieściłem te informacje, które uważam, że wpływają pozytywnie na rozwój projektu, nie generują niepotrzebnych opóźnień w projekcie, minimalizują biurokrację i pozwalają rozwijać produkt zapewniając mu dość dobrą jakość kodu.
  • Team leader powinien przeglądać mniej więcej zmiany wprowadzane przez uczestników projektu.
  • Team leader powinien porozmawiać z określonym uczestnikiem i powiedzieć mu co powinien poprawić w tym co już wcommitował.
  • Refaktoring kodu (polegający głównie na adaptacji kodu do obowiązujących standardów) zazwyczaj wykonuje autor, ale dla przykładu team leader może go zrobić, aby pokazać autorowi jak powinien wyglądać poprawny kod (aby stosował się do tego na przyszłość).
  • Dobrze mieć ogólnodostępny punkt wymiany informacji, np. system kontroli błędów Jira.
  • Jeżeli robicie jakieś ustalenia między sobą co trzeba zrobić w projekcie poprzez komunikator – wnioski ze spotkania i plan działania powinien być spisany i udostępniony w miejscu ogólnodostępnym dla zespołu (np. Jira, prywatne forum projektu).
  • Osoby w zespole powinny zadeklarować się, że w każdym tygodniu poświęcą minimum 10h sumiennej pracy na projekt (jeżeli w ogóle ma się on w jakimś tempie rozwijać).
  • Jak komuś n-tygodni z rzędu coś wypada i ciągle przekłada pracę – zakończcie współpracę. Taka osoba stale coś będzie miała (czytaj: priorytetem nie jest realizowany po godzinach projekt tylko np. wyjście ze znajomymi na imprezę, szykowanie się do konkursów lub inne powody się znajdą dla których nie będzie miał czasu rozwijać projektu).
  • Nie buduj zespołu w oparciu o osoby, które uczą się programować – rekrutuj tylko te osoby, które znają składnię, znają programowanie obiektowe, znają kluczowe biblioteki ułatwiające wytwarzanie oprogramowania. Nie chcesz w końcu mieć w zespole osób, które implementują samodzielnie listę lub drzewo binarne zamiast wykorzystać istniejące standardowe biblioteki.

Projektowanie nowych komponentów

Tworząc nowe komponenty pamiętaj o zasadzie KISS (ang. Keep It Simple Stupid). Staraj się tworzyć klasy, które są odpowiedzialne za wykonywanie jednego zadania. Opisz interfejs tej klasy tak, aby programista, który będzie chciał z niego skorzystać nie będzie miał potrzeby oglądania plików źródłowych i zadowoli się plikiem nagłówkowym. Robienie 'czarnych skrzynek' bardzo ułatwia budowanie złożonego systemu, ponieważ w pewnym momencie zaczynasz go budować w oparciu o 'klocki', które w razie konieczności będzie można wymienić.

Jeżeli wiesz, że potrzebujesz jakiegoś komponentu, ale nie bardzo masz koncepcję jak powinien wyglądać do niego interfejs - zmień podejście. Napisz fragment kodu używający komponentu, który dopiero będziesz tworzył. Dzięki temu poczujesz co tak na dobrą sprawę jest ważne dla programisty, który będzie miał tego komponentu używać. Praktycznie zawsze mile widzianym aspektem jest łatwość w użyciu. Stosując takie podejście uzyskasz intuicyjny interfejs dla nowego komponentu, przykład użycia klasy i będziesz wiedział na co należy kłaść duży nacisk podczas implementowania nowego komponentu.

Przykład źle zaprojektowanych komponentów

Najlepszym przykładem źle zaprojektowanych komponentów są algorytmy grafowe w bibliotece boost dla C++. Autorzy implementujący algorytmy grafowe w bibliotece boost nie zastanawiali się bowiem nad tym, aby użycie dostarczanego rozwiązania było proste. Skupili się na tym, aby po prostu dostarczyć algorytmy grafowe. W konsekwencji istnieje biblioteka, która posiada zaimplementowane algorytmy grafowe, ale nikt nie chce jej używać, bo są po prostu nieintuicyjne, nieczytelne oraz stosunkowo dużo zawiłego kodu trzeba napisać, aby użyć jednego prostego algorytmu grafowego.

Dokumentowanie kodu

Komentarze w kodzie

Jest wielu przeciwników i raczej niewielu zwolenników odnośnie stosowania komentarzy w kodzie.

Argumentacja przeciw komentarzom w kodzie

Najczęstsze uzasadnienia przeciwników dlaczego nie stosować komentarzy brzmią następująco:
  • poprawne nazwy zmiennych, funkcji i metod najlepiej dokumentują kod;
  • najlepszą dokumentacją jest kod źródłowy;
  • komentarze się dezaktualizują wraz z kolejnymi modyfikacjami kodu;
  • nie ma lepszej dokumentacji niż kod, komentarze to strata czasu.
Przykładowy kod bez komentarzy - plik nagłówkowy:
C/C++
class LanguageTranslator
{
public:
    typedef std::wstring DefaultText;
    typedef std::wstring TranslationDetails;
    typedef std::wstring TranslatedText;
   
    TranslatedText getText( const DefaultText & _text, const TranslationDetails & _translationDetails = L"" ) const;
   
    void insertTranslation( const TranslatedText & _newText, const DefaultText & _originalText, const TranslationDetails & _translationDetails = L"" );
   
    //(...)
};
Plik źródłowy:
C/C++
LanguageTranslator::TranslatedText LanguageTranslator::getText( const DefaultText & _text, const TranslationDetails & _translationDetails ) const
{
    TranslationMap::const_iterator itFound = m_translations.find( std::make_pair( _text, _translationDetails ) );
    if( itFound != m_translations.end() )
         return itFound->second;
   
    itFound = m_translations.find( std::make_pair( _text, L"" ) );
    if( itFound != m_translations.end() )
         return itFound->second;
   
    return _text;
}

void LanguageTranslator::insertTranslation( const TranslatedText & _newText, const DefaultText & _originalText, const TranslationDetails & _translationDetails )
{
    m_translations[ std::make_pair( _originalText, _translationDetails ) ] = _newText;
   
    m_translations.insert( std::make_pair( std::make_pair( _originalText, L"" ), _newText ) );
}

Argumentacja za komentarzami w kodzie

Ja zaliczam się do zwolenników racjonalnego dokumentowania kodu:
  • kluczowym jest poprawne nazywanie zmiennych, funkcji i metod, ale w wielu przypadkach taka nazwa nie będzie wyczerpująca, więc komentarz się przydaje;
  • rzadko kiedy zmienia się logikę działania funkcji, więc dokumentacja kodu się nie zmienia, a co najwyżej się rozszerza;
  • jeżeli zmieniasz już logikę działania funkcji, to dotychczasowe komentarze pomogą zrozumieć Ci intencję działania dotychczasowego kodu;
  • nieaktualny komentarz łatwo zaktualizować (w najgorszym przypadku można go po prostu usunąć);
  • skoro większość czasu pracy programisty to czytanie kodu to dlaczego nie pozostawić informacji w kodzie o tym jakie było założenie podczas jego pisania?
  • komentarze w kodzie ułatwiłby robienie ewentualnego code review, które i tak jest czasochłonne i męczące;
  • lepiej mieć jeden komentarz więcej, niż jego brak - to nic nie kosztuje na etapie tworzenia nowych funkcjonalności;
  • znacznie mniej czasu poświęcisz na napisanie sensownego komentarza w trakcie pisania algorytmu, aniżeli na ponowną analizę całego kodu w przypadku poszukiwania ewentualnego błędu w kodzie;
  • wątpliwy kod i tak przeanalizujesz, ale z grubsza komentarz powie Ci co dany kod powinien robić, więc łatwiej oraz szybciej go zrozumiesz;
  • im więcej masz komentarzy tym mniej musisz zapamiętywać - w końcu możesz przeczytać co robi dany fragment kodu, a nie zapamiętać co on robi.
Przykładowy kod z komentarzami - plik nagłówkowy:
C/C++
// Klasa przeznaczona do wykonywania tłumaczeń tekstu.
// Obiekt tej klasy można utworzyć tylko za pomocą WeakSingletona.
class LanguageTranslator
{
public:
    typedef std::wstring DefaultText;
    typedef std::wstring TranslationDetails;
    typedef std::wstring TranslatedText;
   
    // Metoda zwraca tłumaczenie podanej frazy, bądź frazę przekazaną poprzez argument, jeżeli frazy nie znaleziono.
    // Argument TransationDetails umożliwia doprecyzowanie rodzaju tłumaczenia (przykładowo: ten sam tekst w "Menu" gry może inaczej być tłumaczony w "HUD" gry).
    // Pusty TranslationDetails jest tłumaczeniem najbardziej ogólnym, stosowanym jeżeli nie znajdzie się tłumaczenie precyzyjne dla wskazanej frazy.
    TranslatedText getText( const DefaultText & _text, const TranslationDetails & _translationDetails = L"" ) const;
   
    // Metoda dodaje tłumaczenie podanej frazy.
    // Pierwszy argument określa nową frazę.
    // Drugi argument określa hardcodowaną frazę w aplikacji.
    // Trzeci argument określa hardcodowany doprecyzowany rodzaj tłumaczenia (np. tłumaczenie dotyczy "Menu").
    void insertTranslation( const TranslatedText & _newText, const DefaultText & _originalText, const TranslationDetails & _translationDetails = L"" );
   
    // Metoda kasuje wszystkie zapamiętane tłumaczenia fraz.
    void clearTranslations();
   
    //(...)
}; //class LanguageTranslator
Plik źródłowy:
C/C++
LanguageTranslator::TranslatedText LanguageTranslator::getText( const DefaultText & _text, const TranslationDetails & _translationDetails ) const
{
    // Szukamy precyzyjnego tłumaczenia frazy:
    TranslationMap::const_iterator itFound = m_translations.find( std::make_pair( _text, _translationDetails ) );
    if( itFound != m_translations.end() )
         return itFound->second;
   
    // Nie znaleziono precyzyjnego tłumaczenia. Szukamy tłumaczenia ogólnego:
    itFound = m_translations.find( std::make_pair( _text, L"" ) );
    if( itFound != m_translations.end() )
         return itFound->second;
   
    // Nie znaleziono ogólnego tłumaczenia. Zwracamy frazę domyślną.
    return _text;
}

void LanguageTranslator::insertTranslation( const TranslatedText & _newText, const DefaultText & _originalText, const TranslationDetails & _translationDetails )
{
    //Wstawiamy lub aktualizujemy tłumaczenie dla precyzyjnego tłumaczenia.
    m_translations[ std::make_pair( _originalText, _translationDetails ) ] = _newText;
   
    //Wstawiamy wartość dla domyślnego tłumaczenia tylko wtedy, gdy jeszcze nie istnieje.
    m_translations.insert( std::make_pair( std::make_pair( _originalText, L"" ), _newText ) );
}

void LanguageTranslator::clearTranslations()
{
    m_translations.clear();
}

Dokumentowanie ze standardami typu Doxygen

O ile jestem zwolennikiem racjonalnego dokumentowania kodu, o tyle robienie dokumentacji w kodzie takiej, która umożliwia automatyczne wygenerowanie ładnego HTML-a uważam za bezsensowne. Konsekwencje dokumentowania kodu w standardzie automatyzującym generowanie HTML-a są następujące:
  • wymusza dokumentowanie funkcji, klas, metod, pól klasy itd.
  • znaki w komentarzach dokumentujących muszą tworzyć poprawnie zakodowany fragment dokumentu HTML (np. zamiast zapisu <= trzeba napisać &lt;=);
  • standard wymusza opisywanie każdego parametru przyjmowanego przez funkcję;
  • wymusza opisywanie zwracanego typu;
  • wymusza stosowanie określonych typów komentarzy;
  • dodanie nowych funkcjonalności oznacza koieczność wygenerowania nowej dokumentacji;
  • modyfikacja działania funkcji/metody oznacza wygenerowanie nowej dokumentacji;
  • nie spotkałem firmy, która by stosowała automatycznie generowaną dokumentację (8 lat komercyjnego doświadczenia w wytwarzaniu oprogramowania);
  • bez buildbota, który aktualizowałby ogólnie dostępną dokumentację, generowana dokumentacja doxygen jest bezużyteczna (prawie zawsze jest nieaktualna);
  • jeżeli wygenerowana dokumentacja nie będzie posiadała dobrego wyszukiwania to będzie ona bezużyteczna;
  • opisy w dokumentacji zazwyczaj i tak są zbyt ogólne, więc trzeba czytać kod;
  • w dokumentacji nie postawisz breakpointa (możesz czytać dokumentację metod, które wcale nie są wywoływane w miejscu, w którym szukasz błędu);
Dodatkowo standard dokumentowania kodu dla generatora powoduje, że:
  • trzeba napisać bardzo dużo tekstu, aby udokumentować jedną funkcję;
  • pisanie opowiadań do rzeczy prostych jest nudne i czasochłonne (i zbędne);
  • plik zawierający deklarację klasy zawiera w konsekwencji bardzo dużo komentarzy;
  • długie komentarze zaciemniają API klasy (przykład: SFML – 15 linijek komentarza do jednej prostej i intuicyjnej metody);
  • jeżeli standard już jakiś został ustanowiony to trzeba się go trzymać (czyli pisanie opowieści stanie się codziennością);
  • będziesz chciał unikać opowieści, więc zamiast dzielić kod na mniejsze metody, będziesz robił jedną dużą (w końcu po co pisać do każdej metody opowieść).
Fragment udokumentowanego kodu wg standardu umożliwiającego automatyczne wygenerowanie dokumentacji HTML z kodu źródłowego:
C/C++
class Image
{
public:
    // (...)
    ////////////////////////////////////////////////////////////
    /// \brief Get the color of a pixel
    ///
    /// This function doesn't check the validity of the pixel
    /// coordinates, using out-of-range values will result in
    /// an undefined behavior.
    ///
    /// \param x X coordinate of pixel to get
    /// \param y Y coordinate of pixel to get
    ///
    /// \return Color of the pixel at coordinates (x, y)
    ///
    /// \see setPixel
    ///
    ////////////////////////////////////////////////////////////
    Color getPixel( unsigned int x, unsigned int y ) const;
   
    // (...)
};
Długość implementacji:
C/C++
Color Image::getPixel( unsigned int x, unsigned int y ) const
{
    const Uint8 * pixel = & m_pixels[( x + y * m_size.x ) * 4 ];
    return Color( pixel[ 0 ], pixel[ 1 ], pixel[ 2 ], pixel[ 3 ] );
}

Kiedy może mieć sens dokumentowanie automatyczne

Być może automatycznie generowana dokumentacja ma uzasadnienie, gdy udostępniamy/sprzedajemy bibliotekę, której API z założenia się nie zmienia (co najwyżej dodawane są nowe funkcjonalności). Innymi słowy, jeżeli posiadamy bardzo duży projekt, który jest udostępniony na skalę światową. Niemniej jednak wówczas pojawia się inny problem: a co ze wsparciem wielu języków? Workaround: zawsze angielski. A co na to biznes? Raczej nie będzie z tego zadowolony.

Dokumentowanie kodu, a praca nad wieloma projektami

Sensowne komentarze w kodzie docenisz wtedy, gdy:
  • Będziesz pracował z wieloma projektami - przełączanie między projektami jest kosztowne, analiza istniejącego kodu również - komentarze zredukują Ci czas analizowania kodu i szybciej będziesz mógł przystąpić do wytwarzania oprogramowania.
  • Projekt będzie charakteryzował dynamiczny rozwój, wiele osób będzie go rozwijało i będziesz chciał w krótkim czasie zrozumieć co robią poszczególne komponenty rozwijane przez inne osoby w projekcie.
  • Będziesz osobą, która musi robić code review - komentarz da Ci informację co powinien robić dany fragment kodu, dzięki czemu szybciej będziesz mógł znaleźć miejsca w kodzie, które są potencjalnie groźne lub po prostu niepoprawne.
Pamiętaj, że komentarze powinny być raczej krótkie co oznacza, że rzeczy prostych i oczywistych nie należy rozpisywać na kilka zdań 'bo mamy dokumentować kod'. Jeżeli użycie jakiejś funkcji może być nieoczywiste lub niezrozumiałe dla osoby, która nie tworzyła kodu to warto napisać kilka zdań więcej, aniżeli po miesiącu zastanawiać się 'o co tu chodziło', po czym zacząć od nowa analizować kod.

Podsumowanie

Mam nadzieję, że przytoczone informacje związane z wytwarzaniem oprogramowania są dla Ciebie dobrym źródłem inspiracji jak można zorganizować pracę nad swoimi projektami w zaciszu domowym. Artykuł być może dało się lepiej zorganizować, jednak dysponowałem tylko trzema dniami na jego napisanie, więc... trzeba się zadowolić tym co jest :)