Dużo się na ten temat pisze i dużo jest też pytań. Dla mnie kwestia jest dość prosta. Weźmy na przykład ten fragment kodu, który kiedyś popełniłem - wygląda on na produkcji dosłownie tak samo:
var septets = new List<int>();
int currentSeptet = 0;
int shiftBits = 0;
for (var i = 0; i < bytes.Length; i++)
{
currentSeptet |= bytes[i] << shiftBits;
shiftBits += 8;
while (shiftBits > 7)
{
septets.Add(currentSeptet >> _shift);
currentSeptet >>= 7;
shiftBits -= 7;
}
}
Tak naprawdę patrząc na niego ciężko jest wywnioskować co on robi - nazwa funkcji, której nie podałem już powie trochę więcej. Ale jest to jakiś konkretny algorytm, który wykonuje konkretną pracę. Teraz jakie jest moje spojrzenie na dokumentowanie kodu:
- Mógłbym dokładnie opisać każdą linijkę co robi i dlaczego za pomocą inline comments, dzięki czemu po roku jak ktoś wyśle mi buga będę wstanie dość szybko przypomnieć sobie jak to i dlaczego tak to działa.
- Mógłbym również w dokumentacji funkcji opisać dokładnie działanie algorytmu a następnie jego implementację pozostawić bez dokumentacji. Dzięki czemu tak jak poprzednio będę wstanie skumać szybko co jest nie tak jeżeli błąd wystąpi.
- Mógłbym również pozostawić kod bez dokumentacji i zdać się na nazwę metody jako opis co powinien robić algorytm - na przykład DecodeText. W tym jednak jest jeden problem, jeżeli algorytm jest specyficzny dla rozwiązania to będzie ciężko potem coś z nim zrobić, jeżeli zaś algorytm jest otwarty i dostępny na sieci, to nazwa DecodeText za dużo nie powie, prędzej Decode14BitText już może powiedzieć coś więcej.
- Mógłbym opisać tylko najbardziej kluczowe miejsca w algorytmie - na przykład dlaczego sprawdzam wartość shift? A tym bardziej opisać takie miejsca, które nie są zgodne z oryginalnym algorytmem gdyż API firmy XYZ ma specyficzne przestawianie danych w porównaniu do innych firm, przez co cały algorytm musi ulec zmianie.
Dwa pierwsze podejścia nie tylko mi ułatwiają pracę jak i także osobie, która będzie musiała usiąść do tego kodu kiedy ja nie będę mógł, lub ja już nie będę pracował dla danego klienta – jednak nie są one przezroczyste, kod będzie nie ładnie wyglądał oraz jego modyfikacja spowoduje kolejne komplikacje jeżeli chodzi o komentarze (czy usunąć ten komentarz, czy go zaktualizować, albo zostawię to sobie na później). Oczywiście pojawia się pytanie, czy my chcemy dać możliwość pisania kodu komukolwiek czy wolimy by się do nas firma zwróciła w celu poprawny kodu?
Podejście trzecie zaś jest domyślnym zachowaniem, zamiast nazywać funkcję AAAA nazywamy ją konkretnie - co jest nawet chyba best practices jeżeli chodzi o nazewnictwo funkcji. Jednakże nazwa nie gwarantuje ani nam ani komukolwiek innemu możliwości „szybkiego” zapoznania się z kodem i go zrozumienia.
Jeżeli chcemy by kod mógłby być dalej rozwijany to w kluczowych miejscach gdzie wykonywana jest jedna z podstawowych operacji dla całego systemu i jest ona na tyle skomplikowana, że kod sam siebie nie wytłumaczy to ja osobiście piszę komentarze. Jeżeli zaś nie chcemy by ktoś inny się tym zajmował to możemy po prostu komentarze olać.
Ale oczywiście to też ma potem na mnie ujemny wpływ - kiedy naprawdę przyjdzie problem z moim kodem, który był skomplikowany obliczeniowo i teraz nie działa, ja będę musiał poświęcić 2 razy więcej czasu na jego sobie przypomnienie.
Dlatego ja zawsze podchodzę do tego umiarkowanie, raz napiszę dokumentację, raz jej nie napiszę, ale zawsze w miejscach kluczowych napiszę jedną linijkę z opisem dlaczego, by w razie co móc to potem poprawić - to jest właśnie rozwiązanie nr cztery.
Wracając do przykładowego kodu można by to zrobić na przykład tak:
var septets = new List<int>();
int currentSeptet = 0;
int shiftBits = 0;
for (var i = 0; i < bytes.Length; i++)
{
currentSeptet |= bytes[i] << shiftBits;
shiftBits += 8;
while (shiftBits > 7)
{
// HACK: When we are using shift (1) we have one dump byte (it could be CR)
septets.Add(currentSeptet >> _shift);
currentSeptet >>= 7;
shiftBits -= 7;
}
}
Przynajmniej teraz jak siądę do kodu, to będę wiedział, po co mi to przesunięcie jest potrzebne. Reszta zaś jest spełniona przez punkt 3 - odpowiednia nazwa metody i lub <remarks> z linkiem do algorytmu opisanego słowami. Jest to rozwiązanie, które nie tylko powoduje, że kod wciąż jest przejrzysty ale także zachowujemy się fair w stosunku do klienta.
A wy jak podchodzicie do komentowania kodu?