Pull to refresh

Comments 48

В КДПВ можно спокойно заменить комментарий на class PalmTree не потеряв шутки.
А теперь давайте посмотрим, как можно переписать этот код, чтобы он передавал ту же информацию без использования комментариев:

Действительно! Стало гораздо понятнее, короче и читабельнее!

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


PAYOUT_APPROVAL_DATE = date(2020, 5, 1)
BANNED_COUNTRIES = ['Narnia', 'Odan', 'Maldonia']
ELIGIBLE_MONTHS = [11, 12]
today = date.today()

if (today > PAYOUT_APPROVAL_DATE) 
    and (today.month in ELIGIBLE_MONTHS) 
    and (not user.country in BANNED_COUNTRIES):

 stripe_payout_resp = callStripeToPayout(user)
 saveResponseAsync(stripe_payout_resp)

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


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

Мы сейчас перерабатываем с моей командой структуру доков и комментариев в кодовой базе.


Один из пойнтов — комментарий или док должен давать более абстрактное описание, нежели код или сообщение при коммите, отвечая на вопросы:
Зачем?/Почему именно так?, притом как минимум на уровне файла, а лучше — модуля.


Если знакомы с научными статьями, аналогия такая — комментарий это abstract вашего кода.


При это используется фоллбэк
Код -> коммит message -> комментарий.
Логика такая:
Если я читаю код — комментарий задает мне абстрактный контекст, в котором я его интерпретирую, а если мне не понятна конкретная строка — я могу залезть в историю чтобы ее отследить.
Обратно — если я меняю код, то комментарий должен меняться только при концептуальном изменении — рефакторинге, например. При минорных изменениях код поменяется в любом случае, а сообщение к коммиту ответит на вопрос «зачем меняли».


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


PS
Если кому-нибудь будет интересно, могу потом написать статейку о результатах, годика через пол)

> комментарий это abstract вашего кода.

Ой, спасибо! Хорошая аналогия для комментариев «в шапке» файла/программы.

Вообще всё очень сильно зависит от времени жизни кода, от того, как оно будет использоваться, сколько человек и т.д. Комментарии для прикладной программы с коротким временем жизни строятся одним образом — достаточно шапки, а для Stdlib — ну совершенно другое.
Комментарии нужны что бы в художественной форме рассказать что делает каждый участок кода, описать нюансы. Что бы по ключевым словам можно было найти нужный участок кода более оперативно. Помочь ревьюверу или новому сотруднику проникнуться этой историей.

Много воды плохо. Сухость — плохо. Не комментируйте сам код, комментируйте процесс.

Вот как выглядит код с хорошими комментариями:


    /// Creates the specified directory with the options configured in this
    /// builder.
    ///
    /// It is considered an error if the directory already exists unless
    /// recursive mode is enabled.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use std::fs::{self, DirBuilder};
    ///
    /// let path = "/tmp/foo/bar/baz";
    /// DirBuilder::new()
    ///     .recursive(true)
    ///     .create(path).unwrap();
    ///
    /// assert!(fs::metadata(path).unwrap().is_dir());
    /// ```
    #[stable(feature = "dir_builder", since = "1.6.0")]
    pub fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
        self._create(path.as_ref())
    }

(и далее по тексту: https://doc.rust-lang.org/src/std/fs.rs.html#91-93)


Hint: Это не совсем комментарии, это документация, превращающаяся в https://doc.rust-lang.org/std/fs/struct.File.html)


Вот это — хорошие комментарии.

Это аннотация к методу, а у неё могут быть довольно странные способы применения, вплоть до генерации роутов где-нибудь в Symfony.

Я показываю как хорошие институционализированные (т.е. не объяснение "нафига тут 42", а для каждого поля и метода) комментарии должны выглядеть.
Во-первых машиночитаемая структура (У Rust комментарий — 2 слеша, документация — 3).
Во-вторых она процессится в человекочитаемую форму и создаёт документацию к коду.
В третьих она может тестироваться.


И вот такие комментарии реально можно требовать ко всему публичному в коде.

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

  1. Не факт что компилируется, если он не был протестирован via cargo test
  2. Пример может вообще не иметь отношения к нижележащему коду или стать той самой пальмой, которую никто не обновлял — примеры и тесты также нуждаются в поддержке, как и основной код.
  1. Ну, тут всё просто, настраивается CI/CD, который и вызывает cargo test.
  2. Да, тут сложнее возможна ситуация с пальмой, но всё равно легче отловить такого рода ошибку и инструментарий подталкивает тебя делать правильно

А если метод немного поменял внутреннюю логику, но не изменилась суть, или условие немного сдвинулось, а нейминг функций оставили.

UFO just landed and posted this here
А теперь давайте посмотрим, как можно переписать этот код, чтобы он передавал ту же информацию без использования комментариев:

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

Комментарии менять просто и дешево, но вот менять имена функций… ну такое себе.
Итерация 1:
# This code removes the invalid control characters because the AS400 processor cannot handle them
def remove_incompatible_control_chars():
# ... vs ...
def remove_AS400_incompatible_control_chars():

На первый взгляд, все выглядит хорошо, но что если мы обнаружим, что в другом процессоре тоже встречаются ошибки, но на немного другом наборе управляющих конструкций?
В варианте с комментариями все достаточно просто — расширили набор, поменяли комментарий, написали тест.
А что делать со вторым? Рефакторить весь код, который вызывает remove_AS400_incompatible_control_chars, что ли?

Итерация 2:
# This code removes the invalid control characters because the AS400 and AS500 processors cannot handle them
def remove_incompatible_control_chars():
# ... vs ...
def remove_AS400_AS500_incompatible_control_chars():



Переименование функции внутри кода проекта — это пару нажатий клавиш.
Но в данном случае создаётся функция remove_AS500__incompatible, возвращается функция remove_incompatible_control_chars в которую помещаются вызовы функций remove_AS400 и remove_AS500.
Код описывает только то, что делает код. Зачем он это делает и какие были намерения из кода узнать невозможно.
Как бы эта самая мысль в самой статье и продвигается:

Я пользуюсь таким эмпирическим правилом: не использовать комментарии, отвечающие на вопрос «что?». Используйте их, чтобы объяснить «зачем?»


Потому что на вопрос «что делает код» лучше и надежнее всего всего отвечает сам код. А комментарии оставить как раз на описание «зачем» и каких-нибудь специфических/неочевидных (из самого кода) моментов.
Избыточные комментарии — это шум, способный сделать файлы в два раза длиннее, чем они должны быть.
Тут согласен. Но дальше пошло не про избыточные комментарии, а про то, что их вообще всегда нужно избегать. С этим не согласен. Такой подход на деле обернётся вечными спорами между программистами в команде.
И самое смешное, что в комментариях — ошибка

# 1. It's past 2020/5/1 when we got legal approval to make payouts

vs

if today > date(2020, 1, 1)
Почему именно в комментариях? Ошибка может быть и в коде. А может, и ещё веселее — не ошибка, а фича. Когда по каким-то внутренним причинам today в данной программе смещено от реальной даты на несколько дней, из-за чего и приходится сверять с несколько другой константой.
1. Комментарии устаревают
Вообще вся документация устаревает. Откажемся от документирования?
2. Программисты пишут плохие комментарии
… да и код тоже частенько так себе. Особенно полохими комментарии получаются когда разработчиков насильно заставляют их писать, как в приведённом примере про собачку.
В документации есть версионность, в комментах версионности нет. Понять что коммент больше не применим потому что функция которую он коментит изменилась невозможно.

Что за глупость говорите, есть там версионность, у кода же она есть, почему у комментариев, расположенных прямо рядом с кодом, её нет? Следить просто за этим нужно (как и везде).

А как понять, что документация не применима, потому что функция, которую она комментит изменилась?
На всех legacy-проектах с которыми я сталкивался документация не соответствовала действительности.
Более того. Само название классов, методов и полей могло означать всё что угодно.
Так что я лично скептически отношусь, к тому что написано и что нельзя проверить.

Код (с комментами или без) должен быть лаконичен. Это очевидно?
В примере:

is_user_from_banned_country


можно урезать знаки «верблюжачим методом»:

isUserFromBannedCountry

Но если нужна функция, которая вернет 0, если ok; 1, если «user_from_banned_country»; 2, если " past_approval_date"; 3, если «eligible_month», то как ее назвать, чтобы идентификатор отображал всю функциональность? И комменты были бы не нужны.
ИМХО уже пример статьи показал, что длинные имена способны раздуть код больше, чем одноразовый коммент. Сравним:

// is user from banned country

с повторами:

is_user_from_banned_country

is_user_from_banned_country

А еще бывает, что пришедший на смену кодер плохо знает язык, тогда получим «красивые» имена:

esli_ne_is_user_from_banned_country_ili_past_approval_date_to_vidat_oshibky_i_soxranit_v_loge
Но если нужна функция, которая вернет 0, если ok; 1, если «user_from_banned_country»; 2, если " past_approval_date"; 3, если «eligible_month», то как ее назвать, чтобы идентификатор отображал всю функциональность?

Псевдокод:


enum RejectionType {
    UserBannedFromCountry = 1,
    PastApprovalDate = 2
    EligibleMonth = 3
}

RejectionType? check_rejected(...) {
    if (not rejected) return null;
    ...
}

По вкусу можно переместить Ok в enum (и не забыть его переименовать), либо же возвращать Option/Maybe, если язык это позволяет. Но суть в том, что сигнатура функции (тип возврата) содержит информацию о том, какие именно проверки делаются внутри. При добавлении новой надо будет обязательно обновить перечисление, а при удалении старого статический анализатор заругается, что вариант никогда не создаётся. Плюс если его таки удалить, то придется пересмотреть все места использования, иначе не скомпилируется.

Иногда, если требуется написать что то очень витьеватое и запутанно, я сначала пишу комментарии.
Типа — сделаем это… потом сделаем это… тут не забудем учесть ещё и то…
А потом к комментариям пишу сам код.

Чаще же комментарии пишутся когда отлаживаешь-проверяешь, что бы не запутаться в логике происходящего. Причём в 90% случаях, эти комментарии читать буду я сам через несколько лет.

Это, конечно же, никак не отменяет применение осмысленных имен переменных и функций.

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

Супер-хинт. Берешь и выпиливаешь вредные комментарии во всем коде, которого касается твоя рука.
Код с большим количеством избыточных больших названий сложней менять — начинаешь путаться в названиях. Много комментариев так же не всегда хорошо, загромождают код, забываешь их править.

Когда я был в школе, мне говорили — краткость это сестра таланта. Но и такая тактика плохо применима к программированию. Вспоминается язык K, где в одну строку можно много чего сделать, но выглядит это как набор иероглифов.

По мне так луше где-нибудь в золотой серединке.

Да что тут спорить, комментарии — это просто такие спейсеры в коде (потому в большинстве редакторов они и показываются блеклым цветом), чтобы докблоки отделяли один метод в классе от другого посильнее. А то если докблоки не использовать, то несколько методов сливаются в сплошную кашу (потому что одной пустой строкой их разделять плохо — ибо пустая строка и в теле метода используется, а двойную пустую строку убирают автоформаттеры кода, которые стали сейчас особенно хорошо работать). Вот и все. Не важно, что там написано, это просто разделитель, выглядящий по-умному (так что трюк с «ничего не писать там, просто пусто» не прокатит, нужно хоть lorem ipsum, но текст). Не сарказм.

В python напротив ide будет подсказывать что надо бы две строки поставить вместо одной. Так что у вас частный пример

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


ну так в этом и проблема. Нужно за этим следить.
Обратите внимание, что информация, которая раньше передавалась в комментариях, теперь передаётся при помощи временных переменных (is_past_approval_date, is_eligible_month, is_user_from_banned_country),

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

на самом деле, надо воспринимать комментарии (там, где они нужны для понимания смысла) как часть работающего кода

изменился смысл — поправь комменты, делов-то

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

Статья очень напомнила главу из книги "Чистый код". Основные мысли схожи.

«Комментарии должны составлять 5% от общего количества баллов», — заявил мой коллега-преподаватель.

Ура, мудрый прохвессор наконец-то расскажет нам, как программы программировать!</sarcasm>


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

Щас появиться статья про то что тесты тоже зло. Кстати с тестами как и комментариями дело обстоит. Все подряд тестами покрывать точно не нужно, если за это конечно специально не платят. Цель тестов — корректно работающий код. Цель комментариев — улучшить понимание кода

Тесты — это другое. Есть случаи (и много), когда без тестов вообще невозможно что-то сделать даже теоретически. («Невозможно» = «в 100 раз сложнее», скажем; на практике это одно и то же.) Это многоцелевые библиотеки, биллинги и т.д.

Тесты на библиотеки ой как помогают понять как работает работать с кодом. И я иногда даже не знаю как обозначить код, как примеры, или как тесты (если речь опять о библиотеке).
UFO just landed and posted this here
Программа, даже если она написана так, что можно легко понять «что делается», не содержит информации «для чего это делается». Иногда читаешь код и думаешь «понятно, что делается — но зачем это делается?» Например, «понятно, что тут из заявки удаляются все, кто согласовал. Но зачем удалять именно сдесь?». И, после неудачно попытки рефакторинга (перенести удаление согласующих, которые согласовали на «попозже») приходится возвращать всё назад и добавлять небольшой комментарий «а если не удалить — то дальше возникает такая то проблема...» Конечно, можно взять и ту проблему тоже устранить. Но, нет гарантии, что возникнет ещё что то.
Идеальный код в реальных условиях, увы, часто не достижим. Тем и полезен комментарий.
Sign up to leave a comment.