Komentarze w kodzie – Czy i w jaki sposób je pisać?

Prawidłowe zastosowanie komentarzy jest kompensowaniem naszych błędów przy tworzeniu kodu. (…) Obecność komentarzy zawsze sygnalizuje nieporadność programisty. Musimy z nich korzystać, ponieważ nie zawsze wiemy jak wyrazić nasze intencje bez ich użycia, ale ich obecność nie jest powodem do świętowania.

Robert C. Martin – Czysty kod

Krótki cytat z jednej najbardziej popularnych książek traktujących o praktykach tworzenia dobrego oprogramowania, w dużym stopniu odzwierciedla problem jakim jest przeładowanie kodu komentarzami. Wykorzystujemy je aby uzasadnić pewnego rodzaju lenistwo, brak czasu, albo po prostu chęć pisania dużej ilości kodu, która w ostateczności okazuje się złudnym sukcesem.

// Checking if the employee should get the bonus
if ($employee->age > 65 && $employee->status === 'active') {}Code language: PHP (php)
if ($employee->isEligibleForBonus()) {}Code language: PHP (php)

Analizując powyższy przykład, możemy zauważyć, że kod wyglada zdecydowanie schludniej jeśli zamiast niejasnego warunku wykorzystamy czytelną funkcję, której sama nazwa zniweluje potrzebę tłumaczenia o co chodzi.


Błędne Komentarze

Paplanina

Nie wyjaśniaj rzeczy, które są już jasne.

Niepoprawnie
/**
 * @var Product Product object;
 */
private Product $product;

/**
 * Function returns the price that is loaded from the product object bla bla bla.
 */
public function getProductPrice() {
    return $this->product->getPrice();
}Code language: PHP (php)
Poprawnie
private Product $product;

public function getProductPrice(): int {
    return $this->product->getPrice();
}Code language: PHP (php)

Doc Blocks

Nie umieszczaj niepotrzebnych informacji w komentarzach blokowych.

Poniższy przykład kalkuluje finalną cenę, która jest wynikiem mnożenia buforu oraz wskaźnika ryzyka. Ale co dokładnie jest źle?

Niepoprawnie
/**
 * Function returns the final price.
 * @param int $a Total price.
 * @param int $b Buffer miltiplier.
 * @param int $c Risk miltiplier.
 * @return int Final price.
 */
public function getPrice($a, $b, $c) {
    return $a * $b * $c;
}Code language: PHP (php)

1. Należy wyjaśnić znaczenie argumentów funkcji, ponieważ są domyślnie niezrozumiałe.
2. Nazwa funkcji informuje o tym, że zwracana jest *jakaś* cena, a dokładne wyjaśnienie czym ona jest znajduje się w komentarzu.
3. Informujemy o typach komentarzu blokowym mając możliwość wykorzystania typów argumentów.

Poprawnie
public function getFinalPrice(int $totalPrice, int $bufferMultiplier, int $riskMultiplier): int {
  return $totalPrice * $bufferMultiplier * $riskMultiplier;
}Code language: PHP (php)

1. Poprawne nazewnictwo argumentów jednoznacznie wyjaśniło ich znaczenie.
2. Dokładna nazwa funkcji zniwelowała potrzebę wyjaśnienia działania poprzez komentarz.
3. Wykorzystanie typów również wyeliminowało potrzebę pozostawienia komentarza.

Punkt 3 zależy od projektu, ale wymaganie te musi być spełnione jeśli mamy możliwość pracy z językiem wspierającym typy i nie przejmujemy się systemami automatycznie generującymi dokumentacje.

Author

Nie informuj o autorze w komentarzu blokowym.

Niepoprawnie
/**
 * @author PH
 */
class Product {
    /**
    * @author PH
    */
    public function getPrice() {}

    /**
    * @author PH
    */
    public function getName() {}
}Code language: PHP (php)
Poprawnie
class Product {
    public function getPrice() {}

    public function getName() {}
}Code language: PHP (php)

Jak w takim wypadku poznać autora?

1. Wykorzystaj GitLens – https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens
2. Wykorzystaj git blame – https://gitbetter.substack.com/p/how-to-use-git-blame-effectively.
3. Wykorzystaj github.com / bitbucket.com aby sprawdzić kto edytował plik.

Notatki

Nie pozostawiaj w kodzie niepotrzebnych adnotacji.

// Done by Andrew
$test = '';Code language: PHP (php)

Kod

Nie pozostawiaj niepotrzebnego kodu.

// public function getPrice($a, $b, $c) {
//     return $a * $b * $c;
// }Code language: JSON / JSON with Comments (json)

Komentarze akceptowalne

TODO

Czasami dobrym pomysłem jest umieszczenie komentarzy @todo w celu poinformowania o zamierzeniach, albo ważnych elementach, które zostaną zmienione w przyszłości.

/**
 * @todo Remove when XY will be ready.
 */Code language: PHP (php)

Konsekwencje

Jeśli kod tego wymaga, umieść informacje o konsekwencjach w celu ostrzeżenia innego dewelopera.

/**
 * This function will burn your machine.
 */
public function importData(): void {}Code language: PHP (php)

Wyjaśnienia

Jeśli sytuacja tego wymaga – np. przez brak budżetu – umieść komentarz wyjaśniający istnienie danego rozwiązania.

/**
 * Function fixes the problem with FacetWP and Postfinance plugin.
 *
 * Symptoms:    The FacetWP plugin doesn’t work correctly when the Access plugin is also active. For example, if you add a custom
 *              search and try to filter by taxonomy, the FacetWP filter is ignored and a full list of posts is rendered. The issue
 *              is actually caused by the FacetWP plugin, which is running the init filter before everything else.
 *
 *              If you use FacetWP while Access is active basically all FacetWP’s AJAX-based features like search results and pagination will fail.
 *
 * Workaround:  FacetWP modifies the Main Query with additional parameters and runs another WP_Query instance to show search results.
 *              Here is a short explanation of what happens:
 *
 *              - When Toolset Access tries to check/set permissions for the Custom Post Types, it runs the toolset_access_get_current_page_id
 *                function that, in turn, runs the url_to_postid WordPress core function.
 *              - That url_to_postid function runs another WP_Query that replaces the result from the FacetWP’s query and completely breaks the
 *                results from FacetWP.
 *
 *              This happens because of the late priority of Access and the early priority of FacetWP.
 *
 * Fix:         Theme tells the plugin that the payment page is the one with `woo_postfinancecw` slug.
 *
 * @filter woocommerce_postfinancecw_is_plugin_page
 * @see https://toolset.com/errata/compatibility-issue-between-facetwp-and-access-plugins/
 */
public function fixFilters(): bool
{
  global $wp;
  return 'woo_postfinancecw' === $wp->request;
}Code language: PHP (php)

Nie znaczy to, że powinieneś umieszczać takie komentarze w każdym jednym miejscu w kodzie, które wydaje się skomplikowane. Zawsze powinieneś podchodzić do tego tematu ostrożnie wcześnie potwierdzając, że nie zostaje Ci nic innego.


Kilka reguł wystarczy, aby znacznie odchodzić kod. Najważniejsze w tym wszystkim jest to, aby rozumnie podchodzić do wykorzystania komentarzy.

Za każdym razem gdy napisałeś komentarz, przeczytaj go jeszcze raz krok po kroku czytając również kod. W wielu przypadkach w tym tekście wyłonią się od razu nazwy funkcji, na które z pewnością można rozbić skomentowaną funkcję.