Всем привет! Сегодня хочу представить статью о правилах комментирования в коде. Я не хочу сказать, что рекомендации, представленные в данной статье, являются аксиомами. Нет, всегда найдутся две стороны: на одной стороне будут те, кто согласен с представленными рекомендациями, на другой — те, кто не согласен. Разнообразие мнений — это нормально и прекрасно.

Моё мнение, отражённое в данной статье, основывается на книге «Чистый код» Роберта Мартина.

Неужели хороший код не нуждается в комментариях?

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

Хорошие комментарии

Если эти комментарии присутствуют в вашем коде — это хорошо.

Юридические комментарии

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

Например:

/*
 * animate.css -https://daneden.github.io/animate.css/
 * Version - 3.7.2
 * Licensed under the MIT license - http://opensource.org/licenses/MIT
 *
 * Copyright (c) 2019 Daniel Eden
 */

Большинство IDE позволяют автоматически сворачивать подобные блоки комментариев, чтобы они не загромождали остальной код.

Информативные комментарии

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

Например:

// Поиск по формату: kk:mm:ss EEE. MMM dd. yyyy
сonst timeMatcher = new RegExp('\\d*:\\d*:\\d* \\w*. \\w* \\d*. \\d*');

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

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

Прояснение, представление намерений, пояснения к поведению

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

Как бы разработчик ни старался, такой код не сможет ответить на вопрос «почему здесь сделано именно так». Такие комментарии позволят облегчить работу тех, кто будет заниматься поддержкой, рефакторингом или расширением кода в дальнейшем. Разработчики обязательно скажут вам спасибо!

Также такие комментарии часто помогают объяснить магические числа в коде.

Например:

// Для дополнительных задач требуется добавлять префикс перед идентификатором, чтобы он не совпадал с каким-либо из id выездов
orderStateDTO.setOrderId("f_" + additionalTask.getId());

Комментарии TODO

Содержат пометки на будущее, информацию о том, что важно сделать, но не сейчас. Например, когда в будущем нужно сделать какой-то рефакторинг или сделать что-то, когда будет реализована зависимая часть кода. Большинство IDE позволяют вывести эти комментарии TODO в отдельное окно, что очень удобно.

После реализации основной логики какой-то задачи можно вернуться к таким комментариям и реализовать то, что ещё не было сделано, но необходимо. То есть получаем чек-лист на будущую работу, чтобы не искать потом по всему коду то, что вы забыли дописать. Это классический вариант технического долга. Можно больше не записывать себе задачки на листочке, а писать их прямо в коде!

Например:

// TODO: Удалить после удаления класса GroupTaskPanel
// TODO: Проверить логику с приходящим значением null

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

Комментарии Jsdoc в общедоступных API (Это актуально также для аналогичных инструментов документирования в других языках)

Если код хорошо документирован в общедоступном API, с ним приятно и легко работать. В таком случае рекомендуется писать комментарии, которые будут упрощать формирование документации. Это документация метода или класса, которая показывается, когда вы генерируете документацию из вашего кода или когда вы наводите мышкой на название метода, и там показывается его Jsdoc (необходимые параметры, их типы, возвращаемое значение).

Jsdoc стоит использовать только с public методами и никогда с private методами. Private методы могут меняться без предупреждения, и в общедоступной документации они не описываются.

Например:

/**
 * Складываем числа
 * 
 * @param {number} first — первое число
 * @param {number} second — второе число
 * @returns {number}
 *
 */  
function add(first, second) {
        return first + second;
}

Усиление

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

Например:

// Вызов trim() важен. Он удаляет начальные пробелы, чтобы строка успешно интерпретировалась как список
let listItemContent = match.group(3).trim();

Предупреждение о последствиях

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

 Например:

// Don't lower more than 500ms, otherwise there will be animation-problems with the Safari toolbar
setInterval(function() {
	$(window).scrollTop(-1);
	resize();
}, 500);

Плохие комментарии

Бормотание или непонятные комментарии, шум

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

Например:

// WTF????? Refactor this!
// Не грусти, я тоже не понимаю, что здесь происходит:(

Избыточные комментарии

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

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

Например:

// вспомогательный метод: возвращает деление двух чисел, когда второе не равно 0
function numbersDivision(a,b) {
    // Если знаменатель равен 0, то возвращается null
    if(b==0) {
	return null;
    }

    // результат метода
    return a/b;
}

Журнальные комментарии, ссылки на авторов

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

Когда разработчик видит такой комментарий к методу, который ему нужно поправить, он начинает сомневаться, нужно ли его править и зачем здесь комментарий, а вдруг здесь что-то не так? Не надо так.

// Добавлено Иваном
// Изменено в рамках задачи Task-675
// Поправлено 05.10.2019

Закомментированный код

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

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

Позиционные маркеры

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

Например:

///////////////////////////////////////////////////////////////////
********************** комментарий **********************************

Комментарии за закрывающейся фигурной скобкой

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

В процессе рефакторинга эти скобки съезжают и обозначают уже конец другого блока, что может запутать разработчика. Любая современная IDE позволяет «схлопнуть» блок кода и увидеть, где его начало и конец. Если строчек действительно много — это хороший знак, что надо вынести часть кода в отдельный метод, чтобы повысить удобочитаемость.

 Например:

try {
   while (что-то) {
   	много строчек
   } // while 
   много строчек
} // try

Недостоверные комментарии

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

Обязательные комментарии

Некоторые компании заставляют разработчиков писать комментарии, считая, что код будет понятнее. Это неправильно. Это создаёт лишний мусор и не улучшает качество кода. Гораздо продуктивнее заставлять разработчиков правильно называть функции и классы, чем писать комментарии.

Заключение

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

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

  • Не комментируйте плохой код – перепишите его.

    • Код должен говорить сам за себя.

  • Комментарии не являются «абсолютным добром».

    • Код развивается, а комментарии – нет.

  • Комментарий – признак неудачи.

    • У вас не получилось написать код так, чтобы его можно было понять без комментариев.

  • Комментарии не компенсируют плохой код.

    • Каким бы хорошим ни был комментарий, он не оправдывает плохой код.

  • Хороший комментарий – объяснение своих намерений в коде.

    • Комментарий должен отвечать не на вопрос «что делает код», а на вопрос «зачем».

  • По-настоящему хороший комментарий – тот, без которого вам удастся обойтись.

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

Комментарии (24)


  1. gecube
    13.01.2022 16:26
    +2

    В целом, согласен, только вот пример выбран неудачный:

    // Поиск по формату: kk:mm:ss EEE. MMM dd. yyyy
    сonst timeMatcher = new RegExp('\\d*:\\d*:\\d* \\w*. \\w* \\d*. \\d*');

    комментарий и код вообще разные


    1. Firz
      13.01.2022 17:06
      +7

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


    1. Free_ze
      13.01.2022 22:24
      +1

      Эталонный пример плохого комментария)

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


      1. petropavel
        14.01.2022 00:55

        Да ладно, где тут сложные регулярки :)


        1. Free_ze
          14.01.2022 01:14
          +2

          Простые регулярки не нужно комментировать, сложные — вовсе использовать не стоит.


    1. petropavel
      14.01.2022 00:53
      +1

      А почему разные-то? Регулярка распарсит формат из комментария, всё, кажется, нормально.

      Регулярка разрешит и много чего другого, это плохая, слишком общая регулярка, факт. Но вполне можно представить что какой-то джун написал именно эту регулярку под именно этот коммент. Что «совсем разные» вроде непохоже.


      1. ainoneko
        14.01.2022 07:58
        +1

        То, что регулярка «распарсит» и :: . . , тоже нормально?


  1. Ulys-ses
    13.01.2022 18:16

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

    Проблема в том, что комментарии обычно пишет автор кода. Ему код понятен - он же его написал. Точнее, в данный момент понятен.

    А будет ли он понятен другим? Или ему же через пару лет?

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


    1. Felan
      13.01.2022 18:35
      +1

      Даешь comment driven development! :)


      1. Free_ze
        14.01.2022 12:20

        Todo-Driven Development


    1. Zoolander
      14.01.2022 10:01

      Видимо, человек читает любые Regex выражения, как родной язык. Завидую.


    1. Free_ze
      14.01.2022 12:42

      Если конкретной реализации еще нет, то какой смысл вы комментируете? Todo-комментарии обычно выпиливают взамен на реализацию, ведь будет дублирование смысла.


      1. Ulys-ses
        14.01.2022 12:43

        Смысл того, что здесь будет сделано.


        1. Free_ze
          14.01.2022 12:48

          Когда сделаете — комментарий начнет копетанить относительно кода, к которому он относится.


          1. Ulys-ses
            14.01.2022 17:42

            "копетанить"?


            1. Free_ze
              14.01.2022 22:15

              Мемас
              image

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


  1. vkni
    13.01.2022 19:03

    Журнальные комментарии, ссылки на авторов

    Название главы не соответствует содержанию. :-)

    Есть, кстати, возражение на "На самом деле, современные системы Git и IDE позволяют узнать, кем и в рамках какой задачи изменён код.". Это всё ещё далеко не везде так.

    Мы с коллегой буквально на прошлой неделе столкнулись с проблемой: из сторонней организации прислали код на VBA, в котором реализован некий метод монотонной интерполяции. Он похож на Fritsch-Carlson, но не совсем то (возможно Fritsch-Butland). И вот будь там ссылка на статью, которая взята за основу, нам бы было значительно проще.


    1. d_ilyich
      14.01.2022 09:23
      +1

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

      Ох, не факт. Вспоминаю 2 случая.

      1. Необходимо было реализовать расчёт коэффициента сжимаемости газа на С. Купила наша организация соответствующий ГОСТ (вроде, 30319.2-96), в котором расписаны различные методы, приведены формулы и примеры кода на фортране с результатами. Написал программу, сверяю с примерами — не сходится. Скачал стороннее приложение — результаты сходятся (с ГОСТ). В итоге выяснилось, что в одной из формул нашей копии ГОСТа была ошибка. Кстати, если кому интересно, в новом ГОСТ (кажется, 30319.2-2015) коэффициент сжимаемости — это величина, которая в предыдущем ГОСТе называлась фактором сжимаемости; а фактор сжимаемости в новом ГОСТе отсутствует.

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

      Вывод: простого указания на источник не всегда достаточно, в идеале — прикладывать сам источник (если это законно, конечно).


  1. Free_ze
    13.01.2022 22:30
    +1

    // Для дополнительных задач требуется добавлять префикс перед идентификатором, чтобы он не совпадал с каким-либо из id выездов
    orderStateDTO.setOrderId(«f_» + additionalTask.getId());
    Почему бы не заложить это поведение в сам тип?


  1. OkunevPY
    13.01.2022 23:04
    +1

    Хороший код в комментариях не нуждаеться.

    Нормальным считаеться комментирование публичных классов, методов и их параметров. Всё остальное хлам и каша.

    Комментарий в середине метода самое большое днище, писать надо так что-бы понятно.

    Не писать c = a*b, а нормальное totalElementCount = rowCount × rowCapacity. И тогда не надо писать коммент что это и откуда.

    Переменные должны отражать своё назначение.

    Методы должны соответствовать тому что делают и это должно быть понятно по имени метода.

    И всё)))) Чистый код, минимум комментов = прекрасное далёко.


    1. Zoolander
      14.01.2022 09:56
      +2

      Это слишком упрощенный подход.

      Безусловно, можно разбить код на методы, и каждый метод поименовать понятно.

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

      Автор статьи написал практически все по делу, чувствуется большой опыт.



      1. questor
        15.01.2022 10:42

        Ох, спорно это. Я не уверен, что подобные проблемы должны решаться комментариями.

        почему здесь используется такая формула, а не такая?

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

        зачем вы соединили методы именно таким образом?

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

        почему тут так все плохо? Или наоборот, хорошо?

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

        если надо изменить некий функционал, где именно надо изменить?

        Ну это вообще вопрос на миллион. Человек пишущий код - должен думать о том, как решить текущую задачу, он понятия не имеет о будущих людях, кто будет этот код потом изменять и дополнять. Да, если у пишущего есть архитектурное мышление - он как правило будет писать код, который в некоторых пределах будет допускать расширение по типовым направлениям, но не всем. (Универсальной архитектуры подо всё нет; бесконечно универсальный код стоит бесконечно много).


        1. Free_ze
          16.01.2022 13:48

          нафига задаваться такими вопросами читаюему и нафига пишущему код об этом задумываться и подбирать комментарии

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


  1. courage_andrey
    14.01.2022 10:57
    +1

    Немножко юмора из личного опыта (комментарии в реальном Enterpsise-коде):

    • // Массив вместо ArrayList использован здесь просто из-за лени.

    • // Something about dark magic. Do not delete this emty "if", because it causes view invalidation.

    • (Моё любимое, for ever, судя по истории - коммент на месте удалённой строки кода) // Юра, извини.