fbpx
devstyle.pl - Blog dla każdego programisty
devstyle.pl - Blog dla każdego programisty
10 minut

Kilka słów komentarza o… komentarzach


14.01.2013

Pod jednym z ostatnich postów wywiązała się krótka dyskusja na temat komentowania kodu i postanowiłem poczynić follow-up w postaci osobnej notki.

Mam cały czas na myśli oczywiście komentarze wyjaśniające działanie kodu a nie zakomentowany kod. Zakomentowany kod powinien być usuwany na poziomie repozytorium w momencie check-ina i problem mielibyśmy z głowy. Na ten temat nawet nie będę się rozwodził.

Komentarze są fe!

Jeśli miałbym w jednym zdaniu zmieścić swój pogląd na komentarze to właśnie tak by ono brzmiało. Powodów jest wiele.

Jeśli skupiamy się na pisaniu komentarzy to nie skupiamy się tak mocno na jakości produkowanego kodu. Po co pisać łatwy w zrozumieniu kod, skoro jest on (w teorii) wytłumaczony obok? Dawno temu, jeszcze na praktykach, “boss” zerknął mi przez ramię i powiedział: “ooo, dużo zielonego, dobrze, ładny kod”. Nawet nie spojrzał na faktyczny kod – zobaczył tylko że 70% ekranu pokrywały komentarze (bo mnie tak uczyli). Jak można ocenić czyjąś pracę na podstawie tego ile napisał komentarzy? To jak ocenić pracę kuriera na podstawie tego ile kilometrów przejechał, a nie ile przesyłek dostarczył. Bez sensu.

Po drugie: gdy jedna osoba pisze komentarz to druga automatycznie jest zobligowana do jego przeczytania. Często nie odpowiada mi nawet zapoznawanie się z nieładnie napisanym kodem, no ale trudno – taki zawód. Ale gdy jeszcze do tego dochodzą wypracowania w postaci komentarzy to mnie krew zalewa. A czytać – czas stracić – muszę, bo jak nie przeczytam to potem będzie że coś napsułem a “przecież jest w komentarzu napisane co i jak!”.

Pół biedy jeszcze, gdy w treści zawarte jest faktycznie to co być powinno. Niestety – nie jest to regułą. Komentarze mają to do siebie, że bardzo lubią rozsynchronizowywać się z tym, co mają opisywać. A bo trzeba było szybko zrobić fix – to się zmieniło kod, ale nie zaktualizowało komentarza. Albo jeszcze w trakcie pracy ta sama dokładnie osoba wpadła na jakiś nowy pomysł i umieściła zmianę tylko w jednym miejscu. Albo ktoś inny przejął kod i metodą prób i błędów doprowadził do pożądanego zachowania, ale istniejącego komentarza przecież nie wyrzuci bo nie jego i nie kazali. A potem kolejny biedny dev głowi się dlaczego na zielono napisane jest jedno, a kod działa inaczej. Mało tego – program działa tak jak wynika z kodu, ale nie tak jak powinien – bo komentarz mówi co innego. Nie skłamię chyba jeśli napiszę, że częściej widywałem komentarze nieaktualne niż aktualne. Powód jest bardzo prosty: komentarz jest aktualny tylko w momencie jego pisania, podczas gdy kod ewoluuje. I zwykle komentarze nie ewoluują razem z nim.

Ale przypuśćmy, że przytrafiła się sytuacja, w której faktycznie 4-5 słów wyjaśnienia by się przydało. I wielkiej krzywdy nikomu tym nie wyrządzono… tym razem. A przecież znana jest wszystkim metoda Copy’ego/Paste’a, gdzie do schowka wędruje wszystko co się akurat zaznaczy w edytorze. Potem ze schowka całość wskakuje w inne miejsce, w potencjalnie inny kontekst. Kod się doszlifuje, dokręci tak żeby było w porządku. A komentarze? Zostają stare. Masz babo placek.

Zielone elaboraty są szczególnie irytujące, gdy ktoś nieznający zbytnio angielskiego sili się na wielce szykowne formy opisania swojej pracy. Osoba po drugiej stronie repozytorium musi wtedy odwrócić proces “polski -> translator -> pseudoangielski”, domyślając się co może oznaczać dane słowo w ogóle niepasujące do kontekstu, a będące jakimś przypadkiem synonimem polskiego wyrazu mającego tutaj zastosowanie. Olaboga, dość.

Nie przemawia do mnie argument, że “komentarze są potrzebne bo kod jest brzydki i zły”. Nie widzę tutaj w ogóle logiki. Skąd założenie, że ktoś, kto pisze “zły i brzydki” kod, napisze “dobre i ładne” komentarze? Jest wręcz przeciwnie: bo przecież komentarz nie wywali błędu kompilacji ani nie wywróci systemu w runtime.

Dochodzimy prawie do końca litanii, ale nie może zabraknąć jeszcze jednego aspektu: komentarzy nadmiarowych. Nawkładano kiedyś komuś do głowy, że kod trzeba komentować. I potem musimy oglądać takie coś:

/// <summary>
/// Class for getting data from database
/// </summary>
public class DataAccess
{
    /// <summary>
    /// Gets user by id
    /// </summary>
    /// <param name="id">User id</param>
    /// <returns>Returns user</returns>
    public User GetUserById(int id)
    {
        if (id < 1)
        {
            // returning null
            return null;
        }
        // getting user from database
        return _database.GetUserById(id);
    }
    // ...

Przejaskrawione? Pewnie tak. Ale narzędzia takie jak GhostDoc właśnie takie coś promują. A same są również nierzadko promowane jako “must-have” w dotnetowym przyborniku. Let this motherfuck*r burn.

Alternatywy

No dobra, ale co zamiast komentarzy? Odpowiedź jest prosta. A właściwie są dwie. Proste.

Po pierwsze: staranne strukturyzowanie kodu. Zamiast komentować kilka linijek – zamknij je w osobną metodę o wiele mówiącej nazwie, człowiecze. Taka metoda będzie pewnie prywatna, więc nie musisz trzymać się CamelCase które przy więcej niż 3 słowach staje się masakrycznie nieczytelne. Pomyśl przez chwilę jak skrócić komentarz do kilku wyrazów i nazwij tak metodę, nawet jeśli byś musiał uciec się do zwiększenia czytelności jej nazwy przez zastosowanie “underscore_naming” (nie wiem czy tak się nazywa, teraz to określenie wymyśliłem, ale chyba wiadomo o co chodzi).

Zmienne, parametry, pola, właściwości – wszystko powinno być nazwane tak, aby nie było wątpliwości do czego służy. To nie jest takie trudne.

Po drugie: testy jednostkowe. Zamiast pisać komentarz “ten kod się wywali jeśli podstawisz 3 zamiast 1 pod X bo…” – napisz testy. Jeden, który pod X podstawi 1 i wykona się jak trzeba i drugi, który pod X podstawi 3 i będzie oczekiwał wyjątku. Ten drugi test nazwij tak, aby nie było wątpliwości skąd takie a nie inne zachowanie systemu – z wyjaśnieniem w nazwie testu. Ale o tym pisałem już kiedyś w poście “Jak nazywam testy“.

Komentarze uzasadnione

Ja właściwie nigdy nie komentuję kodu i nie zdarzyło się jeszcze, aby mi komentarzy brakowało, nawet jeśli wracam do projektu po kilku miesiącach. Są jednak wyjątki…

Jeśli jesteś szczęśliwym autorem publicznie używanej biblioteki bądź frameworku to najprawdopodobniej lepiej będzie mieć nawet ścierwiaste ghostdocowe komentarze na publicznym API niż nie mieć ich w ogóle. Ludzie lubią jak im się coś pojawia w dymku Intellisense, nawet jeśli nie wnosi nic nowego. A jeżeli dorzucisz tam faktycznie przydatne informacje, jak przykłady użycia w tagu <example> albo szczególne przypadki w tagu <remarks> to tym lepiej dla ciebie. To powinno znaleźć się w dokumentacji, ale w tym przypadku komentarze (mówię oczywiście o xmlowych komentarzach) pełnią właśnie taką rolę. I lepiej żeby nie były rozjechane z faktycznym kodem.

Te XMLowe komentarze nawet w wewnętrznych projektach mają jedne zastosowanie: oferują tag <exception>, gdzie można zadeklarować wyjątki mające prawo wyskoczyć z metody. Co prawda i tutaj najprawdopodobniej wpadniemy w pułapkę out-of-sync (bo ktoś doda try/catch w metodzie ale nie zaktualizuje komentarza, albo okaże się że jednak czasami lecą także inne wyjątki). ale mimo wszystko warto o tym pamiętać i czasami stosować.

Ostatni przypadek uzasadnionych komentarzy jaki przychodzi mi do głowy do tzw. “dirty workarounds”, czyli walczenie z wiatrakami wykorzystywanych bibliotek i obchodzenie ich dziwnego zachowania. Nazewnictwo składowych programu nic tutaj nie pomoże. Podobnie jak osobna dokumentacja. Wyjaśnienie takich scenariuszy musi znajdować się w kodzie, tuż obok pokręconych struktur pomagających osiągnąć pożądany efekt.

Mam dwa przykłady:

  1. Pobierałem dane z FIM za pomocą ich web service wysyłając zapytanie xpath z funkcją “contains”. Okazało się jednak, że geniusze w Microsofcie, jak to u nich czasami bywa, napisali własną interpretację tej funkcji xpath i “contains” oznaczało u nich coś innego niż powinno (w FIMie to “zwróć true jeśli których z wyrazów w przeszukiwanym stringu zaczyna się na podany string” czy jakoś tak). Oszukałem system – podczas wysyłania zapytania zamieniałem “contains(a1, a2)” na “starts-with (a1, %a2)” i działało jak trzeba, bo % jest traktowany jako normalny SQL-owy wildcard. Ale taki kod wymagał porządnego wyjaśnienia, łącznie z linkami, dlaczego czynię takie herezje.
  2. Raz jeden jedyny w życiu natknąłem się na sytuację, której nijak nie potrafiłem logicznie wytłumaczyć. Dotyczyło to NHibernate i MySql – w pewnych bliżej nieokreślonych okolicznościach NH zwracał mi obiekt z bazy, ale wywalał się przy pobieraniu jego relacji mimo tego, że one tam były. Zdarzało się to raz na kilka tygodni (w kawałku kodu wykonywanym co kilkanaście sekund) i w systemie działa się przez to sodoma i gomora (użytkownicy byli bombardowani SMSami co, jak się domyślacie, nie było ani miłe ani tanie). Spędziłem nad tym masę, masę czasu, ale do niczego nie doszedłem. Skończyło się na wyłapaniu jednego konkretnego wyjątku, zbadaniu czy na 100% wystąpiła ta konkretna sytuacja i… zabiciu aplikacji przez wywołanie HttpRuntime.UnloadAppDomain(). Bardzo, bardzo brzydkie rozwiązanie, ale okazało się, że jest do zaakceptowania. I oczywiście wymagało naprawdę solidnego komentarza gdyby ktoś się kiedyś zastanawiał dlaczego rzucam na system bombę atomową.

Czekam na… komentarze;)

I to tyle co mam do powiedzenia. O ile komentarze w kodzie są be, fu i w ogóle, co starałem się udowodnić, to na blogu im więcej tym lepiej oczywiście. Więc jak to wygląda u Was?

0 0 votes
Article Rating
28 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Wojtek(szogun1987)
11 years ago

Ogólnie w artykule jest wszytko co być powinno. Mam w zasadzie tylko dwie uwagi:

“Zakomentowany kod powinien być usuwany na poziomie repozytorium w momencie check-ina i problem mielibyśmy z głowy.” – Moim zdaniem zakomentowany kod można zostawić ku przestrodze następców z komentarzem “tak nie należy programować”.

“Co prawda i tutaj najprawdopodobniej wpadniemy w pułapkę out-of-sync (bo ktoś doda try/catch w metodzie ale nie zaktualizuje komentarza, albo okaże się że jednak czasami lecą także inne wyjątki). ale mimo wszystko warto o tym pamiętać i czasami stosować.” – Zapomniałeś o Exceptionalu (http://www.maciejaniserowicz.com/2009/05/27/relacja-z-codecamp-warszawa-2009/) fakt faktem projekt dawno nie rozwijany a sam z niego nie korzystam. Ale pozwala względną spójność utrzymać.

trackback
11 years ago

Kilka słów komentarza o… komentarzach | Maciej Aniserowicz o programowaniu…

Dziękujemy za dodanie artykułu – Trackback z dotnetomaniak.pl…

Wojtek(szogun1987)
11 years ago

Z tego co pamiętam Bartek mówił że go niby używają. Ciekawe czy przestali czy uznali za projekt wewnętrzny którym nie będą się dzielić.

Kamil
Kamil
11 years ago

Istnieje chyba jeszcze jeden wyjątek. Są to komentarze typu TODO:
Nie zawsze ze wszystkim da się wyrobić, szczególnie jeśli chodzi o kwestie wydajności, która może być kluczowa. Zapisywanie takich zadań gdzieś w innym miejscu powoduje, że musimy wyszukiwać konkretne miejsca, które ew. wymagają poprawy… Wiadomo, nie można z nimi przesadzać;] ale wydaje mi się, że to dobry przykład komentarza, który jednak warto zostawić w repozytorium…

Wojtek(szogun1987)
11 years ago

Jak coś się pojawi w TODO: nigdy nie zostanie zrobione, takie rzeczy lepiej umieścić w Buzilli, Jirze, TFSie, Rationalu, czymkolwiek co czytają osoby decyzyjne (do kodu zazwyczaj nie zaglądają).

Jacek
Jacek
11 years ago

Maćku, Twoje rozwiązanie nie zawsze się sprawdza – bo co jeśli w głównym branchu musi być kod który 1. kompiluje się, 2. przechodzi wszystkie testy? Jeśli jesteś zależny od innego zespołu nie możesz sobie napisać failing testu, bo zepsujesz builda. Możesz czekać miesiąc aż zespół od którego zależysz napisze wymaganą przez Ciebie funkcjonalność ale wtedy polecam rebasowanie po miesiącu zwłaszcza jak nie dysponujesz takimi narzędziami jak perforce czy TFS… Niestety jedyne wyjście to TODO, albo tak jak napisał Wojtek, task w systemie ale to też jest zwodnicze bo TODO zostaje zawsze tam gdzie trzeba a mając task to trzeba znaleźć odpowiednie miejsce w kodzie itp itd. Najlepsze jest połączenie – TODO z krótkim komentarzem albo numerem tasku w taskowni, gdzie już problem jest opisany dogłębniej…

Paweł
11 years ago

Dzień Dobry.

W zasadzie zgoda, ale teraz załóżmy, że w firmie panuje rotacja pracowników. W wielu firmach panuje przekonanie, że każdego pracownika można zastąpić skończoną liczbą klientów (bez urazy). I tu mam wątpliwość, czy kod samokomentujący się wystarczy. Wiadomo – teoria jest taka, że pracownik przychodzi z określonym workiem kompetencji, że przechodzi szkolenia wewnętrzne, treningi, cuda wianki. Osobiście mam odczucie, że rzeczywisość jednak nieco skrzeczy. Dlatego wydaje mi się, że nie ma nic zdrożnego w naświetleniu logiki/domeny biznesowej, którą dany fragmentu kodu realizuje, bo pozwala młodemu pracownikowi na szybsze wdrożenie się do projektu. Szczególnie warto zwrócić uwagę na biblioteki źródłowe, inaczej lądujemy z pięcioma wersjami liczenia wieku, a każda potrafi zadziałać inaczej. Do tego komentując można utrwalać pożądane wzorce (to robimy tak i tak, bo…).
Ludzie są zdolni i potencjalnie zawsze coś w kodzie przelezie dalej. Ot – szczególnym przypadkiem może być Javascript, gdzie możemy sobie napisać piękny model obsługi wszystkiego, a z racji braku zmiennych prywatnych ktoś sobie pójdzie na skrót, bo tak mu szybciej.
Może mam spaczone podejście (bo w .NET siedzę od stosunkowo niedawna), ale widziałem samokomentujący się kod po przejściu przez ręce niedoświadczonych programistów (bo każdy doświadczenie gdzieś zdobywać musi).
Reasumując – jestem na tak, ale z drobnym ‘ale’. ;)

pk
pk
11 years ago

Czy jeśli przedstawiacie komuś kod to nie rzucacie od siebie jakiś wartych uwag? Ja zawsze coś dorzucam i mimo, że wielką dbałość przywiązuje o szczegóły to i tak widzę potrzebę opisu słownego.

Osobiście lubię tworzyć opis przed testem, a test przed implementacją. Tworząc opis mogę się zastanowić nad tym co będę programował. Opis niekoniecznie musi być bardzo rozwlekły, wręcz gdy taki już jest to wiem, że coś jest nie tak i mogę szybko zareagować i znaleźć lepsze rozwiązanie :)

Poza tym posiadanie komentarzy pozwala upraszczać nazwy, jest mi to szczególnie potrzebne, gdy tworzę implementację funkcji. Zamiast robić wielosłowny argument, używam jedno proste słowo, które co prawda nie mówi wszystkiego same za siebie, ale dzięki komentarzowi wyjaśnia dokładnie swe znaczenie.

Lubię też w dokumentacji zostawiać pomysły na później, bo nie zawsze jest sens ulepszać od razu wybraną funkcję.

Inna rzecz, to sprawdzanie argumentów. W języku Java dość często metody przed wykonywaniem konkretnego kodu stosują tzn. preconditions. Natomiast w Pythonie preferowane jest podejście stosowanie dokumentacji, bo jeśli coś ma spowodować błąd to i tak spowoduje. Dlatego dokumentacja sprzyja ostrzeganiu przed złem.

No i ostatnia kwestia o jakiej chcę wspomnieć. Języki z dynamicznym typowaniem nie mają jawnie oznaczonego typu jaki zwraca funkcja. Dlatego tego typu treści warto opisać właśnie w dokumentacji.

pk
pk
11 years ago

A jaka korzyść odnosisz posiadając opis funkcji poza kodem? Tak przynajmniej wszystko jest na mniejscu, a największą korzyścią jest to, że nie trzeba analizować kodu funkcji, by rozumieć co ona robi. Przykładowo mogę mieć taką funkcję: http://wklej.org/id/924499/

Jestem zdania, że treść dokumentacji powinna opisywać tak implementację, aby inny programista nie mając kodu potrafił samemu odtworzyć identycznie działającą funkcję. Wtedy jest elegancko :)

A i też mógłbym nazwać argument: package_dir_path, ale w ten sposób uzyskałbym kod, który przekracza w poziomie 80 znaków :-|

pk
pk
11 years ago

Chciałbym zgłosić, że drugiej wiadomości ten blog nie chce mi już wyświetlić, a gdy ponawiam to informuje o duplikowaniu treści :[

WhiteLightning
11 years ago

Macku, a co powiesz o sytuacjach gdzie cos optymalizujesz (zwlaszcza w jezykach typu C/C++) i robisz np. jakies skomplikowane niskopoziomowe operacje, operacje na bitach, obliczenia itp. W takim przypadu linijka komentarza, albo chocby napisanie jaki wzor wyliczamy drastycznie zwieksza czytelnosc.

Podobnie gdy piszemy w jezykach skryptowych i chcemy zrobic jak najkrotszy i jak najszybszy kod, czesto nie wyglada on wtedy zbyt czytelnie i komentarz duzo daje (chocby gdy uzywamy seda i awk).

Łukasz
11 years ago

Można powiedzieć, że zgadzam się z tym, żeby w kodzie było jak najmniej komentarzy. Jednak nie w każdej dziedzinie da się zastosować to co tutaj zaproponowałeś, a o czym wspomniał WhiteLightning. W wielu projektach, szczegóolnie OSS, kod to jedyna dokumentacja, a komentarze tam bardzo pomagają, choćby zorientować się w podziale na jednostki/moduły.

Inna sprawa to jeżeli kod jest naprawdę ciężki i nie wynika to z braku umiejętności programisty, a z skomplikowania dziedziny – np. jądro Linuksa. Tam wielolinijkowe elaboraty są nie do przecenienia, gdy starasz się zrozumieć subtelne zawiłości architektoniczne lub sprzętowe czy skomplikowaną logikę zakładania locków. W tego typu zastosowaniach nie ma takich pojęć jak “failing test”, a komentarze w kodzie (łącznie z TODO i FIXME) to Twój jedyny przyjaciel.

Podsumowując moją wypowiedź – według mnie komentarzy powinno być dokładnie tyle, ile trzeba – na pewno więcej niż 0 i jednocześnie mniej niż równowartość kodu.. I powinny być one pielęgnowane na równi z kodem.

PS. Zapewne nie przypadła Ci do gustu koncepcja zaproponowana przez Bardzo Ważną Osobę: http://pl.wikipedia.org/wiki/Literate_programming ;)

Wojtek(szogun1987)
11 years ago

Wracając jeszcze do kwestii Zadań (hiszp. TODOS) w kodzie. Dzisiaj w jednym z projektów zajrzałem na zakładkę Tasks w Visualu i znalazłem tam ponad 200 wpisów. Myślę że to pokazuje ich skuteczność.

mak
mak
11 years ago

Wszelkie działania matematyczne bardzo trudno “skomentowac” nazwami metod, jeszcze trudniej opisac cos takie za pomoca nazw zmiennych, Takze tutaj wydaje mi sie komentarze w kodzie sie przydaja.

Waldimen
Waldimen
11 years ago

A ja zgadzam się z postem, prawie wcale nie używam komentarzy.
W pewnym projekcie którym się zajmowałem były automatycznie generowane komentarze XML na dodatek przeważnie nie uzupełnione, pierwszą rzecz którą zrobiłem to było ich wywalenie.
Nie przeszkadzają mi długie nazwy metod i zmiennych. Ale nigdy nie używam underscore w nazwach nawet prywatnych metod, CamelCase jest dla mnie czytelny.

Kamil
Kamil
11 years ago

“Myślę że to pokazuje ich skuteczność.” A co to, dowód przez przykład?

@rek
11 years ago

Z mojej praktyki, wszelkie komentarze szybciej i później (a najczęściej szybciej) bardzo szybko zaczynają być oderwane od kodu – i jak mówi Uncle Bob – stają się kłamstwem.

stosowałem TODO ale podobnie jak Wojtek po znalezieniu n todo które wiszą od niewiadomo kiedy usunąłem – nic się nie stało, świat dalej istnieje.

Jedyne odstępstwo na jakie sobie pozwalam to komentarz do idiotycznego – nielogicznego kodu który potrzebują czasem zewnętrzne biblioteki – chociaż i to czasem jest bez sensu jak idiotyzmy – nielogiczności są poprawiane w nowych wersjach.

A najbardziej śmieszą mnie zakomentowane całe bloki kodu ciągnące się przez całe ekrany. Takie na później. Bo się przyda bo coś tam coś tam – uwaga zdradzam wielką tajemnicę wszechświata – nie przyda się, nigdy się nie przydaje – a wersja taka jak była kiedyś jest w repozytorium jakby ktoś tak strasznie musiał że nie wytrzyma.

pawelek
pawelek
11 years ago

Też jestem zdania, że komentarze to zło. Przydają się tylko jak robi się coś zupełnie nielogicznego po to by kod działał logicznie :)

Krzysiek Szabelski
Krzysiek Szabelski
11 years ago

—-
“underscore_naming” (nie wiem czy tak się nazywa, teraz to określenie wymyśliłem, ale chyba wiadomo o co chodzi).

Jakby co to się nazywa Snake Case ;]
http://en.wikipedia.org/wiki/Snake_case

Kurs Gita

Zaawansowany frontend

Szkolenie z Testów

Szkolenie z baz danych

Książka

Zobacz również