Комментирование кода: хорошие, плохие и отвратительные комментарии

«Хороший код — это самодокументируемый код». Вы слышали эту фразу раньше? Я тоже. Более чем за 20 лет написания кода я слышал эту фразу чаще других. Это уже клише.

У этой фразы, как и у многих других клише, есть доля правды. Но смысл этого предложения давно потерялся, многие люди уже не понимают, что значит это выражение из-за постоянного и повсеместного использования.

В этой статье мы рассмотрим хорошие, плохие и отвратительные примеры комментирования в коде.

Начинающие разработчики должны помнить, что существует два типа комментариев в коде: поясняющие и документационные.

Документационные комментарии

Документационные комментарии предназначены для всех, кто когда-либо будет использовать ваш исходный код, но вряд ли сможет понять или прочитать его правильно. Если вы создаёте библиотеку или фреймворк, которые будут использоваться другими разработчиками, то вам нужно будет разработать документацию для API.

Чем больше API-документация отдалена от вашего кода, тем больше вероятность того, что она станет неточной или устаревшей с течением времени. Хорошим способом преодоления этой проблемы является документирование в самом коде. Таким образом вы сможете извлечь документацию с помощью специальных инструментов.

Посмотрите на пример кода из Lodash — популярной библиотеки для JavaScript:

 /**
     * Creates an object composed of keys generated from the results of running
     * each element of `collection` thru `iteratee`. The corresponding value of
     * each key is the number of times the key was returned by `iteratee`. The
     * iteratee is invoked with one argument: (value).
     *
     * @static
     * @memberOf _
     * @since 0.5.0
     * @category Collection
     * @param {Array|Object} collection The collection to iterate over.
     * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
     * @returns {Object} Returns the composed aggregate object.
     * @example
     *
     * _.countBy(1, Math.floor);
     * // => { '4': 1, '6': 2 }
     *
     * // The `_.property` iteratee shorthand.
     * _.countBy(['one', 'two', 'three'], 'length');
     * // => { '3': 2, '5': 1 }
     */
    var countBy = createAggregator(function(result, value, key) {
      if (hasOwnProperty.call(result, key)) {
        ++result[key];
      } else {
        baseAssignValue(result, key, 1);
      }
    });

Если вы сравните эти комментарии с онлайн-документацией, то увидите, что они одинаковы. При написании документационных комментариев убедитесь, что они соответствуют общепринятому стандарту и что они отличаются от уточняющих и поясняющих комментариев в самом коде. Некоторые популярные инструменты и стандарты включают использование JSDoc для JavaScript, DocFx для .Net и JavaDoc для Java.

Недостатком этих комментариев можно назвать лишний шум в коде, который они создают. Этот шум создаёт сложности при чтении кода другими разработчиками, участвующих в его разработке и поддержании.
Однако в большинстве современных редакторов есть возможность скрыть комментарии, чтобы лучше сосредоточиться на редактировании кода.

Поясняющие комментарии

Поясняющие комментарии предназначаются для всех (включая вас в будущем), кто будет заниматься поддержкой, рефакторингом и расширением вашего кода.

Зачастую уточняющий комментарий к вашему коду является его отражением. По пояснению можно судить нагружен ваш код или нет. Следует пытаться удалять пояснения в коде, упрощая его, ведь «хороший код — самодокументированный код».

Приведу пример плохого, но забавного пояснения:

/* 
 * Replaces with spaces 
 * the braces in cases 
 * where braces in places 
 * cause stasis.
**/ 
$str = str_replace(array("\{","\}")," ",$str);

Вместо того, чтобы писать амфибрахий для украшения запутанного кода, автор мог бы с пользой потратить время и поработать над функцией, облегчив чтение и понимание кода.

Поймите меня правильно, бывают моменты, когда небольшая порция юмора помогает расслабиться, особенно когда вы просто утопаете в работе. Но не пишите забавный комментарий, чтобы приукрасить плохой код. Именно из-за таких шуток в будущем мало кто захочет исправлять код и заниматься его рефакторингом.

Вам действительно хочется приукрасить рифмой свой плохой код, чтобы кодеры ухмыльнулись и проигнорировали его, двигаясь дальше?

Также бывают случаи, когда авторы добавляют излишние пояснения. Например, если код очень прост и понятен любому, то нет необходимости добавлять пояснения.
Не совершайте подобные глупости:

/*
Устанавливает значение 32 для переменной age
*/
int age = 32;

Тем не менее, бывают случаи, когда поясняющие комментарии нужны, вне зависимости от того, как выглядит ваш код.

Обычно это происходит, когда вам нужно добавить контекст к неинтуитивному решению.
Вот хороший пример из фреймворка Lodash:

function addSetEntry(set, value) {   
  /* 
   Не возвращать `set.add`, потому что эта цепочка вызовов не сработает в IE 11.
  */  
  set.add(value);    
  return set;  
}

Иногда бывают случаи, когда оказывается, что на первый взгляд наивное решение проблемы является наилучшим после долгих экспериментов и размышлений. В подобных моментах почти неизбежна ситуация, когда другой разработчик, подумав, что он умнее, чем вы, начнёт копаться в коде, но в итоге выяснит, что ваше решение всё это время оставалось лучшим.

А иногда тем самым «более умным» кодером можете оказаться вы сами. Поэтому в таких случаях лучше оставлять комментарии к коду, чтобы в будущем сэкономить своё и чужое время и нервы.

Комментарий снизу полностью отражает суть мысли выше:

/**
Уважаемый разработчик:
 
Как только ты прекратишь пытаться «оптимизировать» этот код
и поймёшь, какую ошибку ты допустил взявшись за это дело,
пожалуйста, увеличь номер на счётчике ниже
для следующего разработчика:
 
количество_часов_потрачено_впустую_здесь = 42
**/

Опять же, комментарий сверху содержит больше юмора, чем пользы. Вам СЛЕДУЕТ оставлять комментарии, предупреждающие других от поиска какого-либо «лучшего решения», если вы сами уже пытались это сделать, но ничего хорошего из этого не вышло. В комментарии следует указать, какое решение вы пытались найти и почему вы решили, что оно не подходит в данной ситуации или не работает.

/* 
 не используйте глобальную функцию isFinite(), потому что она возвращает true для нулевых значений
*/
Number.isFinite(value)

Отвратительные комментарии

Мы уже узнали, что такое хорошие и плохие комментарии к коду. Теперь пришло время ознакомиться с отвратительными комментариями.

К сожалению, в любой работе бывают моменты разочарования. Такое может случиться и во время написания кода, когда у вас возникнет соблазн выразить всё своё разочарование в комментариях.

При работе с большими объёмами кода вы можете столкнуться с различными негативными комментариями, начиная от циничных и депрессивных до резко негативных и злых.

Относительно безвредный комментарий:

/*
Этот код — дно, я знаю.  
Можешь изучать его дальше и назвать меня тупицей.
*/

Откровенно яростный комментарий:

/* 
Класс, использующийся для обходного решения — Richard being a f***ing idiot
*/

Подобные моменты могут показаться забавными или, возможно, помогут вам разрядиться немного во время описания своего жизненного разочарования в комментарии. Но следует помнить, что иногда ваш код может дойти до продакшена. В таком случае вы не будете выглядеть умным или компетентным в своей сфере.

Уважайте себя и других разработчиков и не допускайте подобных комментариев в своём коде.

  1. 1, 4.2, 6.3

Перевод статьи «Putting comments in code: the good, the bad, and the ugly.»