Комментарии 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
Если кому-нибудь будет интересно, могу потом написать статейку о результатах, годика через пол)
Ой, спасибо! Хорошая аналогия для комментариев «в шапке» файла/программы.
Вообще всё очень сильно зависит от времени жизни кода, от того, как оно будет использоваться, сколько человек и т.д. Комментарии для прикладной программы с коротким временем жизни строятся одним образом — достаточно шапки, а для 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).
Во-вторых она процессится в человекочитаемую форму и создаёт документацию к коду.
В третьих она может тестироваться.
И вот такие комментарии реально можно требовать ко всему публичному в коде.
Главный плюс этого комментария — пример в нем компилируется, а значит проверяется на достоверность. :)
- Не факт что компилируется, если он не был протестирован via cargo test
- Пример может вообще не иметь отношения к нижележащему коду или стать той самой пальмой, которую никто не обновлял — примеры и тесты также нуждаются в поддержке, как и основной код.
github.com/HighSchoolSoftwareClub/Windows-Research-Kernel-WRK-/blob/26b524b2d0f18de703018e16ec5377889afcf4ab/WRK-v1.2/base/ntos/mm/addrsup.c#L606
А также старая добрая классика "PSD is not my favourite file format."
А если метод немного поменял внутреннюю логику, но не изменилась суть, или условие немного сдвинулось, а нейминг функций оставили.
А теперь давайте посмотрим, как можно переписать этот код, чтобы он передавал ту же информацию без использования комментариев:
Пожалуй, я лучше продолжу писать комментарии...
Итерация 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():
Я пользуюсь таким эмпирическим правилом: не использовать комментарии, отвечающие на вопрос «что?». Используйте их, чтобы объяснить «зачем?»
Потому что на вопрос «что делает код» лучше и надежнее всего всего отвечает сам код. А комментарии оставить как раз на описание «зачем» и каких-нибудь специфических/неочевидных (из самого кода) моментов.
Избыточные комментарии — это шум, способный сделать файлы в два раза длиннее, чем они должны быть.Тут согласен. Но дальше пошло не про избыточные комментарии, а про то, что их вообще всегда нужно избегать. С этим не согласен. Такой подход на деле обернётся вечными спорами между программистами в команде.
# 1. It's past 2020/5/1 when we got legal approval to make payouts
vs
if today > date(2020, 1, 1)
1. Комментарии устареваютВообще вся документация устаревает. Откажемся от документирования?
2. Программисты пишут плохие комментарии… да и код тоже частенько так себе. Особенно полохими комментарии получаются когда разработчиков насильно заставляют их писать, как в приведённом примере про собачку.
Более того. Само название классов, методов и полей могло означать всё что угодно.
Так что я лично скептически отношусь, к тому что написано и что нельзя проверить.
В примере:
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, но текст). Не сарказм.
Разработчики забывают обновлять комментарии при рефакторинге кода и создании новых функций. Когда такое происходит, комментарии становятся первопричиной того, чему должны были препятствовать, а именно запутанности.
ну так в этом и проблема. Нужно за этим следить.
обратите внимание, что читать это стало невозможно, плюс эти безумные переменные с кошмарными именами раздуют код
на самом деле, надо воспринимать комментарии (там, где они нужны для понимания смысла) как часть работающего кода
изменился смысл — поправь комменты, делов-то
Пример, конечно, на пять баллов. Из короткого, четкого, читаемого кода с комментариями сделали трудночитаемую "войну и мир".
Статья очень напомнила главу из книги "Чистый код". Основные мысли схожи.
«Комментарии должны составлять 5% от общего количества баллов», — заявил мой коллега-преподаватель.
Ура, мудрый прохвессор наконец-то расскажет нам, как программы программировать!</sarcasm>
Вообще ту же логику можно применить и к тестам. Тесты устаревают и их приходится обновлять, некоторые программисты пишут их плохо, и вообще, совершенный код не должен всегда работать правильно. Почему же никто не кричит, что тесты надо перестать писать? А просто когда код без тестов работает неправильно, то спорить с этим трудно, придётся исправить код и признать, что надо было делать тесты. А когда код без комментариев непонятен, то, ну… ты просто не умеешь читать код, да и в конце-концов же во всём разобрался, и вообще, всё равно на комментарии сейчас нет времени.
Щас появиться статья про то что тесты тоже зло. Кстати с тестами как и комментариями дело обстоит. Все подряд тестами покрывать точно не нужно, если за это конечно специально не платят. Цель тестов — корректно работающий код. Цель комментариев — улучшить понимание кода
Тесты — это другое. Есть случаи (и много), когда без тестов вообще невозможно что-то сделать даже теоретически. («Невозможно» = «в 100 раз сложнее», скажем; на практике это одно и то же.) Это многоцелевые библиотеки, биллинги и т.д.
Идеальный код в реальных условиях, увы, часто не достижим. Тем и полезен комментарий.
No comments