Pull to refresh

Comments 213

До конца пока не дочитал, там просто реализация класса StreamReader не самодокументирующаяся :) Вместо
reader.Peek();

должно быть что-то вроде
reader.PeekAndAtTheSameTimeRecognizeStreamEncoding();

На правах шутки юмора :)
На правах шутки юмора :)

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


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

Какие там шутки, это и правда более логично
Вероятно, шутка в том, что кроме этих двух действий оно может делать ещё что-то (или не делать).
(Посмотрел в документацию:
Returns the next available character but does not consume it.
, про кодировку ни слова.
Откуда начинающий вообще может узнать, зачем добавлено «reader.Peek();», если (1) оно не меняет поток, а только смотрит на символ, и (2) полученное значение не используется? Вероятная мысль у него: «наверное, осталось от копипаста из другой функции, можно удалить». ¯\_(ツ)_/¯ )
Это всего лишь означает что у StreamReader неудачное API.

А то что в документации не слова про неявную функциональность как раз доказывает то что наличие документации и комментариев не обязательно делает код понятнее.

Как написано у Роберта Мартина в Clean code, ошибка может быть как в коде так и в комментариях.

Причём при исправлении бага или при добавлении новых функций комментарий может остаться не исправленным или не дополненным как видимо подучилось с методом Peek()
комментарий может остаться не исправленным или не дополненным

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

Так не помог же, разобраться-то. Если бы он и правда помог кому-то разобраться — там бы вместо вызова Peek() сейчас стоял сразу вызов ReadToEnd().

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


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

не придумали замены «листингу»
Так «распечатка» же
reader.PeekAndAtTheSameTimeRecognizeStreamEncoding();
Вот оно, то что нас в свое время (когда был выбор на что идти — на с или на яву) отпугнула от явы.
Монстроподобного вида названия функций которые невозможно запомнить и даже если запомнил в целом, то невозможно набрать с первого раза. При чем хинтинг в ide по большому счету это костыль. В результате программинг превращается в увлекательный квест «вспомни название очередной функции»:)
Респект всяким ява прогерам, мы бы так не смогли.
Почему хинтинг в ide это костыль?
Примерно по тем же причинам, почему код должен быть самодокументирующимся:)
Мы не отрицаем пользы хинтинга, ни в коем случае.
Но почему опытный разработчик пользуется хинтингом? Потому что название нужной функции или порядок аргументов или файл где хранится класс не очевидны сами по себе. Как по нам — полная аналогия с причинами для написания комментов.

1) просмотр вариантов доступных в принципе
2) меньше символов набирать
3) нюансы "правописания"
4) да, иногда не помнишь даже о чём думал, когда метод называл, может PeekAndAtTheSameTimeRecognizeStreamEncoding, а может .PeekAndRecognizeStreamEncoding или PeekWithRecognitionStreamEncoding. Синонимы, детализация, разные аспекты, разные уровни владения языком — человеческие языки слишком многообразны, чтобы воспроизводить идентично названия даже в походих контекстах (а идентичными они не бывают)

Можно было бы еще понять сравнение Java с C++, где можно перегружать операторы, назначать алиасы типам (typedef/using), но… си? Неужели функции с названиями вида strpbrk кажутся вам более понятными и запоминающимися?
reader.PeekAndAtTheSameTimeRecognizeStreamEncoding();
А заодно дописать к имени что ещё там подразумевается :)

Нужно всегда читать документацию к используемым функциям. Хотя бы для того, чтобы никто вас в этом не мог упрекнуть. Понятно, что должен быть компромис между временем чтения документации и написанием кода.
Повысить читаемость кода, можно используя прокси функции с домен-специфическими именами. В том же C# это делается легко и просто с использованием extension methods. Комментарии, не к публичному API, а по месту — это злое зло, препятствующее рефакторингу и нарушающее принцип DRY второго рода.
Да. Автор нагонят мути там, где хватило бы выделить один метод с понятным именем.

В стандартный класс воткнуть свой метод не так-то просто, если не писать что-то поверх, а стримридер именно что стандартный :-)

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

Хотя в случае С#, как раз есть extension methods…

docs.microsoft.com/ru-ru/dotnet/csharp/programming-guide/classes-and-structs/extension-methods
Разработка ПО — это не только ради удовольствия и крутых техник. Это про деньги и прибыль. Поддержка комментариев и документации — это затраты.
Требования меняются, надо быстро закрывать фичи. Все будут забивать на обновление документации и комментариев. Да даже юнит-тесты часто просто комментируют, потому что меняется бизнес-логика и тонны тестов нужно переписывать, а бюджеты под это не дают.
В идеальном мире с бесконечными бюджетами у каждого программиста должен быть свой технический писать, QA, массажистка и резиновая уточка. IRL это могут себе позволить только очень крупные и богатые компании.
Вообще, пора бы корифеям разработки задуматься над cost-эффективностью, а не только над гипотетической правильностью.
Тоже при чтении статьи в первую очередь подумал про то, что к каждому пункту нужно дописывать послесловие, начинающееся словами "… но не забывайте, что вам нужно будет поддерживать актуальность". Написали документацию? А теперь постоянно её обновляйте. Нарисовали схемы? Обновляйте! Выработали domain-specific language? Ну, вы поняли — сидите допиливайте его по каждому серьезному поводу, а потом переименовывайте всё в соответствии с доработками. Пишете комментарии? Лучше б вам их обновлять, а то пройдет пара итераций правок и старые комментарии будут уже вредить, а не помогать.

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

Возможно легко, но всё же надо будет.

Разработка ПО — это не только ради удовольствия и крутых техник. Это про деньги и прибыль. Поддержка комментариев и документации — это затраты.


Так комментарии и документация — это тоже про деньги и прибыль. Они призваны снизить стоимость внесения изменений в код. Представьте, к примеру, сколько времени затратит новый сотрудник на ознакомление с кодовой базой из тысячи модулей и сотен тысяч строк кода без единого комментария.
Вы серьезно считаете что новому сотруднику будет проще изучить сотни тысяч строк комментариев, диаграмм (в большинстве случаев неактуальных) для того чтобы начать фиксить багу?

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


А вот когда ключевые разработчики уже уволились (по тем или иным причинам) — документация бесценна.

Уточнение: актуальная документация бесценна...

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

Во многом это не так. Сейчас бизнесу нужны очень короткие циклы деливери новых фич. (О чём рассказывал Греф в своём знаменитом докладе про Angile). Релизы раз в 2 дня стали требованием. Будешь тупить — конкуренты уйдут вперёд.
Например, случается какая-то новость (уже не помню), на волне хайпа за несколько часов aviasales выкатывает свой промо-проект. Вот это круто, это то к чему стоит стремиться.
Есть ещё гейм. индустрия, где жизнь совсем не такая, как в мире кровавого интерпрайза, где ± полгода до релиза — только дополнительные деньги галере.
Быстрая выкатка случается если у вас много команд и разделенная архитектура сервиса. Но что самое важное — такая быстрая выкладка требует высокого качества исходного кода. Иначе будешь откатитываться в три ночи. Высокое качество — требует продуманной архитектуры. Высокая скорость разработки тем более. Так что да — «тише едешь — дальше будешь»
«высокое качество исходного кода» — это абстракция. Нужна конкретика.
Работал в таких web-проектах: IRL вместо документации API всё генерится Swagger.
Вместо юнит-тестов и необходимой им «архитектуры» только функциональные, тесты API через генерённые клиенты. Некритичные части тестами покрывать некогда, там во всю идут A/B тесты. Метриками обвешано всё, графики, логи, аналитика. Всё это позволяет выкатывать любой говнокод, метрики сразу позволяют понять ломает он что-то или нет. Линтеры позволяют отсеять совсем уж жесть.
Про «продуманную архитектуру» какого-нибудь успешного игрового проекта мы до сих пор читаем в книге «кровь, пот и пиксели»
image
Вот это круто, это то к чему стоит стремиться.

Нет, это не круто, стремиться к этому не стоит.

Мы живём среди растерянных людей, которые сами не знают чего хотят, ими манипулирают все, кому не лень (даже эта ваша священная корова — «бизнес»), в итоге покупают вещи, которые им не нужны, на деньги, которые им не принадлежат.

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

Бизнесу нужно то, бизнесу нужно это… важно что нужно людям на самом деле. Сходите на природу, погуляйте, подышите, попейте хорошего чая за хорошей книгой (лучше не при электрической лампе) и как-то желание проповедовать этот вот корпоративный bullshit само собой отпадёт.

P.S. Наверное, лучше всего мою позицию выражают слова Роберта Мартина: «the only way to go fast is to go well»
(лучше не при электрической лампе)

Угу, лучинку зажгите


Простите, не удержался.

Что-то вспомнилось на тему того, что у бизнеса никогда нет бюджета на качественную разработку ПО с первого раза, зато всегда есть деньги и время на переписывание получившегося нерабочего, гхм, «продукта» до более-менее рабочего состояния, которое с вероятностью 100% случится.

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

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

Проблема в том, что люди решают очень общую задачу «написания совершенного кода», а совершенный код — это код, который эффективно решает задачу в поставленных условиях. Те если пишете софтину для боинга, то логично комментировать и документировать всё, под это должны быть бюджеты. Если пишете MVP, то там даже архитектурой запариваться не надо, ибо ни вы, ни бизнес не знает, что должно получиться после N итераций.
И тут есть вопрос, а имеет ли смысл вкладываться в документацию того, что всё равно пойдёт под нож через полгода?

Через полгода или через 10 лет — это сложно предсказать, но любой программный продукт со временем обрастает функционалом который нигде кроме как в коде не описан и который всегда теряется при переписывании, вызывая стойкую неприязнь к различного рода обновлениям и “улучшениям” у большинства пользователей.
Требования меняются, надо быстро закрывать фичи. Все будут забивать на обновление документации и комментариев. Да даже юнит-тесты часто просто комментируют, потому что меняется бизнес-логика и тонны тестов нужно переписывать, а бюджеты под это не дают

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


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


Но ведь можно просто сразу так делать, нет?

Но ведь можно просто сразу так делать, нет?

Можно. А потом оказывается, что у вас кончились деньги и нет продукта. Вообще никакого. Зато есть онбординг за пару суток и покрытие тестами на все 200%. Удачи.
PS: И я ничего не говорю о том, что тестов не надо, а онбординг может быть сколь угодно убогим. Я говорю о том, что и тесты, и документация — вещи всегда небесплатные, и окупающиеся только на достаточном пробеге (по объему кода и по времени). Которого может и не быть.
Собственно, это и есть моя мысль за которую меня минусуют. Нужна разумная достаточность, нужны методологии, которые позволяют ей следовать.
Нужны признаки когда документация нужна, когда нет.

Как пример, даёшь человеку задачу которую нужна заделиверить через неделю. А человек вместо того, чтобы просто сделать начинает писать докуменацию, вертеть архитектурами и всячески усложнять ради «красоты» и «паттернов». В итоге, в конце недели человек зашивается с объёмами кода, которые нужны для 100500 лэйеров, а фича выходит сырой. Мне часто приходится воевать с «перфекционистами» и долго объяснять, что overengineering выглядит круто в петпроджектах и в тестовых заданиях, а для реального приложения это всё не нужно.
С другой стороны не нужен и говнокод.
Вот эта золотая середина между overengineering и говнокод и есть то, что «синьёры» находят «чуйкой». Но кроме чуйки хочется иметь методологии, как red-green-refactor.
Open source — не совсем про деньги. Computer science — не совсем про деньги. RnD — не совсем про деньги. Вообще большинство значимого кода получается не совсем про деньги.
Эмм… все перечисленное безусловно про деньги. То что это не про деньги Вас могут лечить добрые дяди с толстыми кошельками которые «от души» вынимают малую копеечку из своего кошелёчка и подают Вам на пропитание пока вы занимаетесь тем что Вам нравится. А вот когда из Вашего проекта что-то получится, ну может не из вашего а из проекта вашего товарища вот тогда они с удовольствием на этом проекте заработают денежку, причём так что Вы лично будете думать что осчастливили человечество.
Когда лень писать комменты — можно втереть заказчику про самодокументирующийся код. Иногда прокатывает.
Заказчику впаривают слегка адаптированную рыбу в качестве документации. Это и выгоднее и проще чем «продать» самодокументирующийся код
Но код пишут ДЛЯ машин.

Код пишут для людей. Машины вполне устраивает машинный код (удивительно, да?), но он труден для понимания людьми, поэтому пришлось изобретать языки программирования. Всякие абстракции, функции, ООП и прочие радости жизни — тоже для людей, чтобы им было проще сложностью управлять


reader.Peek();

Ну это явный косяк интерфейса StreamReader, который не имеет более понятного метода типа DetermineEncoding или что-то в таком духе. Неудивительно, что такие косяки приходится комментировать — на то они и косяки

золотой коммент, ++

статья бредовая:

1. автор пишет/использует несамодокументируемый код
2. указывает очевидную несамодокументированность кода
3. делает нелепые выводы

Роба Пайка бы лучше почитал/посмотрел
вначале назовут не понятно как, а потом это непонятно что непонятно что делает :)
reader.Peek
а виновато конечно же самодокументирование.
насчет некоторых языков, впрочем, согласен: выглядят они очень странно
По мне так пример из пальца высосан, вполне логично, что что-бы определить кодировку, нужно хоть что-то прочитать, а название свойства StreamReader.CurrentEncoding явно говорит нам, что оно в процессе чтения может меняться. Что действительно стоило бы описать в комментарии, так это почему не сделано просто
using (reader)
{
    fileContents = reader.ReadToEnd();
    fileEncoding = reader.CurrentEncoding;
}

Почему не было произведено считывание всего файла целиком для задачи определения кодировки?


прошу прощения, невнимательно прочитал код из статьи

В данном случае все равно желателен коммент, что геттер энкодинга нужно дергать после чтения. Если конечно компания не занимается разработкой библиотеки IO streams.
название свойства StreamReader.CurrentEncoding явно говорит нам, что оно в процессе чтения может меняться

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

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

Вот только StreamReader умеет детектировать кодировку только по первым байтам и никак иначе.

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

Нет, я декомпилировал его код. Именно для того, чтобы понять что он делает с кодировками...

Этап 1: Попробуйте написать документацию

Этап N+1: попробуйте отрефакторить документацию.

… но кто её читает, там же в коде всё понятно
По-хорошему, если функция написана правильно с понятными именем и интерфейсом, то читателя кода вообще не должно парить что там внутри и как именно она читает файл. Главное что она читает.

Ключевые слова «если» и «правильно». А если неправильно? И именно вы и должны разобраться – а что же именно там неправильно – и починить при необходимости. А то так, знаете ли, и до функции main() можно дойти: читателя кода вообще не должно парить, что там внутри и как именно она выполняет основную функцию программы. Главное, что она выполняет.

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

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


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


Также стоит вспомнить и о том, что документация может описывать требования к функции, а код — это реализация. И надеюсь, понятно, что выводить требования из реализации может быть неприемлемо. Пока еще ни в одном мейнстримовом языке различие "требование или реализация" через код никак не отражается. Разве только вы всегда явно будите выделять интерфейс и все требования описывать через него. Но в этом случае о каком самодокументирующемся коде будет идти речь — по определению в интерфейсе нет кода? Знаю, знаю, в Java 8 интерфейс и в Rust типаж могут иметь дефолтную реализацию, я считаю, что это прекрасно, но это опциональная возможность, а мы здесь рассматриваем проблему в общем виде.

Если код "правильный", то не иногда, а практически всегда этот алерт не нужен. Единый источник правды.


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

Если код "правильный", то не иногда, а практически всегда этот алерт не нужен. Единый источник правды.

Это "если". Проблема как раз том, чтобы определить, правильный код или нет. Это как с тестами. Можно сказать — нафига тесты? Мы и так знаем, что пишем корректный код (т.е. код — единый источник правды). А можно подтверждать это тестами.


В офисе без разницы, а вышел…

Поэтому документация технически должна быть частью кода (javadoc, doxygen и иже с ними). В современных языках (как Rust) это уже поняли и компилятор интегрирован с этой механикой из коробки.

И это так же как с документацией. Но, субъективно, вероятность того, что название сущности кода будет больше соответствовать её содержимому выше, хотя бы потому что в некоторых популярных темах для IDE названия подсвечиваются ярче основного кода, а комментарии наоборот тусклее, если вообще не скрыты по умолчанию. С другой стороны, я слабо верю в существование людей, которые напишут отличный doc комментарий к методу, но при этом дадут это методу плохое название. То есть диссонанс между содержимым комментария и кодом заметят, а между названием и кодом — нет.

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

Мне приходилось работать как в местах, где было необходимо комментить каждую строку плюс шапку писать, так и в таких, где в style guide не было ни одного слова про комменты. Заметил одну закономерность — качество кода определяется только качеством кодера, а не количеством комментов или форматированием. В последнем месте шапки-комменты ваще писала пиэм, а кодеры её ревьювили.
Короче говоря, я считаю что самодокументирующийся код — это хорошо. Пока он самодокументирующийся. Если в методе две-три строчки и название метода отражает его функцию, комментировать действительно нечего. Если же там есть и условия, и циклы (да-да, я знаю, если есть условия значит что-то не так и т.д., но часто нет смысла разносить целостный алгоритм по десяткам приватных методов, так читабельность только ухудшится), то лучше оставить в нужных местах памятку, что, где, как, откуда, куда, чем и зачем, — и для себя, и для тех кто будет с этим кодом работать в будущем. Хотя бы в шапке. Должна быть золотая середина, а то хейтеры комментариев дойдут и до того, что документацию к программам вообще писать не надо — там же и так все понятно. Да, идеальный интерфейс должен быть идеально понятен без всякой документации или контекстной помощи, а идеальный код должен быть абсолютно понятен любому, кто его первый раз видит. Только реальный мир не идеален. Как-то так.

P.S. (Это я уже чуть преувеличиваю.) Для совершенного, самодокументирующегося кода, наверное, и тесты писать не надо? Ведь если код самодокументирующийся, любому сразу каждая ошибка бросится в глаза. А ведь на тесты, как и на те ненужные комментарии, нужно тратить время, поддерживать их в актуальном состоянии.
reader.Peek(); //Вот так нужно заглянуть в файл, чтобы получить его кодировку.
fileEncoding = reader.CurrentEncoding;
fileContents = reader.ReadToEnd();

Я не настоящий сварщик, но, сдаётся мне, вы комментарием пытаетесь прикрыть говнокод.

Там же сразу после этого всё содержимое файла читается, автодетект на нем сработает. Берите CurrentEncoding после ReadToEnd() и никаких Peek не надо.

Если это действительно так, то вы сделали сильно хуже.
Отсутствие комментария может побудить кого-нибудь воскликнуть «WTF?», разобраться и исправить, а с комментарием вида «так надо» эта копипаста со StackOwerflow будет цвести еще очень долго, «накопляя мелкие детали и постепенно захламляя некогда чистый код».

Простите, но автор ерундой болтает.
Про причину №1.


var fileContents = “”;
Encoding fileEncoding;
using (var reader = new StreamReader(filePath, Encoding.Default,  true))
{
    fileEncoding = GetFileEncoding(reader);
    fileContents = reader.ReadToEnd();
}
...
private Encoding GetFileEncoding(StreamReader reader)
{
    reader.Peek();
    return reader.CurrentEncoding;
}

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


Про причину №2: автор утверждает, что непоследовательно исполняющийся код не может быть понятен по определению, не приводя никаких доказательств. Нет фактов — обсуждать нечего.


Про причину №3.


если взглянуть на это глазами программиста-новичка или не-программиста

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


И пару слов по основному посылу статьи: тут уже написали, что главная проблема документации в том, что её надо поддерживать в актуальном состоянии. Плюсую.
А т.к. эта поддержка человекозависима, то никогда не будет работать стопроцентно качественно, а значит, вы никогда не будете иметь абсолютно актуальную документацию.
Самодокументированный код возможен, документированный — нет.

У нас код документированный. У нас он постоянно актуален, так как не отделим от кода. Непонятно на чем строятся ваши утверждения.

так как не отделим от кода

Это как? Если я поменял код, то автоматически актуализируется документация?
Если нет (а мы с Вами знаем, что нет :) ), то мы получаем человекозависимый процесс и далее по тексту моего комментария выше.

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

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

У меня это вообще больная тема. потому что на работе сейчас я отвечаю за кусок ядра, достаточно большой, в котором много специфичных алгоритмов работающих в комплексе(глобальный асинхронный поиск пути с учетом разных типов поверхностей(поле->рельсы) плюс локальное маневрирование для объезда препятствий). В целом не рокетсайнс, но система сложная.
И вот ПМ решил, что надо это задокументировать. Идея здравая и я без проблем её поддержал. Сделал общее описание, практически пошаговое.
Довольный сдал документацию ПМу…
И получил шквал вопросов в духе: «А вот это что за характеристика, а какие значения у неё?».
Я говорю: «Все константы в коде есть, открываешь и вот они все».
Получил ответ: «Документация должна быть самостоятельной и понятной без кода»

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

В общем и целом, нормальная дкоументация — это в заголовке хидера описание что у нас тут за классы и зачем они нужны, у каждого публичного метода описание чего он делает и какие аргументы принимает и выдает, в начале реализации описание отдельных используемых алгоритмов, если оно того требует.
В идеале всё в формате какого-нибудь автоматического генератора документации.
Благодарю за развёрнутый ответ! Но давайте по пунктам:

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

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

Я говорю: «Все константы в коде есть, открываешь и вот они все».
Получил ответ: «Документация должна быть самостоятельной и понятной без кода»


Конечно, это глупости. Документация без кода — моветон.

Но я никак и никогда не могу согласиться с тем, что якобы достаточно «правильно» разбить код на +100500 классов и методов, и искусно их обозвать. Нет конечно, это заблуждение. Не достаточно. За свою практику я потратил много сотен часов, ковыряясь в чужих исходниках, чтобы разобраться в ньюансах и способах использования чужого кода, или его доработки. И я на практике смог оценить пользу от документирования кода, когда ввёл его на своих проектах и своими глазами видел, какую это приносит пользу. Огромную.

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

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

Не очень понятно про какой уровень документации вы говорите.


А вот переход на личностные характеристики типа лени, неспособности писать документацию (это как вообще? кто не способен разбить пробелами длинное название сущности и перевести их из CamelCase в нормальный вид? ) и отсутствия уважения задевает. Я вот обычно документирую код комментариями в минимально необходимом для прохождения код-ревью виде и в большинстве случаев считаю это время потраченным зря, просто ради прихоти начальника или заказчика. Надо — значит надо. Не надо — буду тратить время на что-то более полезное для проекта и/или себя.

Про причину №3.
если взглянуть на это глазами программиста-новичка или не-программиста
Всё, занавес.
Код не пишется для чтения поварами или астрономами. Мы должны писать код, понятный другим программистам.


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

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

Ряд задач тестировщиков и аналитиков без чтения кода не решается никак. Уверен, есть практики лучше, чем комментирование кода в самом коде. Уверен, что они ещё более трудозатратны для программиста, чем простое комментирование. Нежелание комментировать код это, почти всегда, лень и самоуверенность, а не что-то ещё.
Ряд задач тестировщиков и аналитиков без чтения кода не решается никак.

Внезапно. Каких же?

Особенно приятно отлаживать неработающий код, когда ты не программист, а аналитик или тестировщик

У QA есть тест-кейс. Если он валится, то причина тестера не должна заботить, просто заводится тикет с ссылкой на кейс, оценивая его критичность для бизнеса. Дебажить будет уже программист, который возьмется за исправление.
А аналитику-то код зачем? Он с формальными требованиями к ПО работает, оценивать соответствие этим требованиям — это не его работа.
Речь идёт про большое и самописное решение, в проекте на >100 человек (если считать с сотрудниками заказчика, то >200 человек) в большом международном инвестиционном банке. Среднее время сотрудника на проекте — два года (галера, большая текучка). Среди всех сотрудников, только 3 человека остались со времён начала проекта, ещё человек 5 можно назвать старожилами, но и они знают не больше 20% от всей базы кода.

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

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

Грустная история. Но это совсем не работа аналитика, а вполне прозаичная погоня за снижение затрат за счет «работников-универсалов»: оклад один, функции — три.
Ещё из той же оперы: за пять последних месяцев уволились три аналитика, из которых один — старожил проекта, технически на уровне архитектора. Руководство берёт толкового старшего БД-разработчика с хорошими софт-скилами и переводит его в старшие аналитики. Задачи по написанию БД-проецедур с него не снимут.

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

От того, что человек работает программистом на проекте, не следует, что он в состоянии прочесть 100% базы кода проекта. И даже 50% — тоже не факт.
Предположим, что решение самописное, число программистов не ограничено, но их нельзя использовать для документирования, так как новые задачи важнее документации. Вопрос: как системный аналитик сможет хорошо разбираться в решении, не читая кода?
Второй вопрос: как, не читая кода, узнать про многочисленные фильтрации и исключения, которые нигде, кроме кода, не описаны?

Если нет времени на документирование — то откуда возьмется время на написание комментариев?

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

А кто пишет ТЗ и кто отчитывается о работе перед заказчиком?) Вот эти архивы материалов и будут «базой знаний» аналитика. Он ведает функционал, а как оно там «под капотом» работает — это совсем не важно, да и может поменяться хоть завтра (это есть суть рефакторинга).

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

Все, что торчит снаружи и критично для бизнеса должно регламентироваться ТЗ. Контролировать фиксацию требований на бумаге — это задача аналитика. Если заказчику захотелось странного и задача требует оценки технической части, если вдруг нет системного архитектора, то привлекаются наиболее подкованные технические специалисты — тимлиды, либо даже целые команды. Им ставится вполне конкретная задача, они отвечают оценкой времени на реализацию и предложениями по оптимизации решения исходя из имеющейся кодовой базы. Результатом обсуждения будет ТЗ, удовлетворяющее обе стороны.

Заставлять человека делать то, что он не умеет и ожидать хорошего результата — по меньшей мере наивно.
А кто пишет ТЗ и кто отчитывается о работе перед заказчиком?) Вот эти архивы материалов и будут «базой знаний» аналитика. Он ведает функционал, а как оно там «под капотом» работает — это совсем не важно, да и может поменяться хоть завтра (это есть суть рефакторинга).

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

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

Аналитик или тестировщик не должен заниматься отладкой кода.

Кейс расписал комментарием выше.

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

Ещё пример, типичная задача для системного аналитика: есть поле в noSQL БД, которое заполняется в 10 разных местах в коде в 3 разных компонентах. Надо понять, почему на проде в случае X поле оказалось пустым, хотя в источниках у заказчика поле заполняется верно. Надо разобраться и описать задачу в джире, чтобы программист изменил логику так, чтобы и бизнес удовлетворить, и решение не сломать. Поломка решения или очередной кейс с незаполнением поля — ошибка аналитика, а не программиста.

Такие задачи всегда решал я (программист), а не аналитик.


Описание задачи от аналитика: на проде в случае Х поле оказывается пустым, найти почему.

Описание задачи от аналитика: на проде в случае Х поле оказывается пустым, найти почему.

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

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

Я бы выстроил это как единую шкалу: менеджеры не нужны < аналитики не нужны < комментарии не нужны < документацию надо изучать по остаточному принципу < тестировщики не нужны < документация не нужна < свои баги нельзя признавать < проект должен состоять только из сеньорных программистов-дедов.

Возможно, это не самоуверенность и не надменность, а какая-то отдельная характеристика. Большинство программистов, которые пишут комментарии на хабре, находятся правее отметки «менеджеры не нужны».
Следующий шаг: задачу нужно задокументировать в вики\конфлюенсе\документе, описать изначальные причины проблемы (в том числе и на доступном бизнесу уровне)

Описываю так, чтобы понял аналитик. Дальше аналитик описывает так, чтобы понял заказчик.


Следующий шаг в такой логике — аналитики не нужны, всё будут делать программисты.

Не вижу перехода.

Практика показывает, что разделение труда эффективнее, чем найм штучных гениев и мастеров на все руки.

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


Следующий шаг в такой логике — аналитики не нужны, всё будут делать программисты.

Я бы выстроил это как единую шкалу: менеджеры не нужны < аналитики не нужны < комментарии не нужны < документацию надо изучать по остаточному принципу < тестировщики не нужны < документация не нужна < свои баги нельзя признавать < проект должен состоять только из сеньорных программистов-дедов.

Странно как-то это у Вас вытекает из утверждения "Аналитик или тестировщик не должен заниматься отладкой кода."

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

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


  • Аналитики — общаются с заказчиком, делают постановки задачи. Получают от программистов оценки по трудозатратам, утрясают это с заказчиком.
  • Программисты — проектируют реализацию, реализуют, осуществляют юнит-тестирование.
  • Тестировщики — ручное тестирование + автоматизированное (кроме модульного).

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

"По такой картине" — я имею в виду Вашу ситуацию.

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

У меня ситуация обратная — везде, где я работал, было чёткое разделение ролей аналитика и разработчика. Аналитики являются специалистами в бизнес-анализе (и психологии, без этого работать с заказчиками невозможно :) ) и практически ничего не знают о внутреннем устройстве системы.
И это отлично работает.

описать изначальные причины проблемы (в том числе и на доступном бизнесу уровне)

Аналитик общается с командой, полагаясь на их мнение составляет описание.

описать способ реализации

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

описать как тестировать (включая критерии)

«Как тестировать» решает QA — пишет свои тест-планы и тест-кейсы. Задача аналитика — дать внятные ТЗ и юз-кейсы от заказчика.

показать, что решение и правда снимает проблему для всех X, а не только в одном конкретном случае, презентовать решение бизнесу.

Вполне в компетенции бизнес-аналитика.

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

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

Именно так:
А в реальном я не пишу комментарии (хотя из этого правила есть исключения) потому, что считаю, что от них больше вреда, чем пользы.


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

Да, протухают.


Чужие или свои — вообще не важно.


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


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

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

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

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

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

Чтобы этот код было проще понять.

Но комментарий устарел и не дает понять актуальный код.

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

Вред, как я понял, только один: комментарии протухают, они ещё и чужие, их редактировать — лень.

Неправильно поняли. Лишние комментарии:

1. Увеличивают размер кодовой базы, заставляют больше времени потратить на чтение собственно кода,
2. Требуют времени на написания, которое можно было потратить вместо этого на рефакторинг или юнит-тесты (которые сами по себе могут быть альтернативной документацией),
3. Могут только запутовать при расхождении комментария и кода, требуют времени на переписывания, код ревью и т.д.,
4. Использования комментариев для коментирования кода — тоже плохая практика, так как раздувает размер кода,

Хорошие комментарии, несомненно, полезны. Плохие — очень сильно вредят. Плохими можно считать те комментарии, которые написаны формально и просто пересказывают названия классов и переменных своими словами. К сожалению, часто правила автоматического код ревью настроены так, что приходиться писать комментарии вида

    /**
     * Convert InputStream to String.
     * @param inputStream  - InputStream variable
     * @return String from InputStream 
     * @throws IOException - we throw IOException if something failed
     */
     public String convertInputStreamToString(InputStream inputStream) throws IOException {


Много пользы вы от них видите, а вместо одной строчки код, тут их аж 7(!), причем каждому кто их читает придется пробежать глазами их все и убедится, что они не несут вообще ничего полезного.
У одного оппонента в руках пример плохого кода, у другого — плохих комментариев. Боюсь, так далеко в рассуждениях не уехать (=

Кстати, п.4 дублирует п.1.
Пунт 4 это про закомментированые куски кода вида

/**
public String convertInputStreamToString(InputStream inputStream) throws IOException {
....
*/
public String convertInputStreamToString2(InputStream inputStream) throws IOException {

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

Боюсь, так далеко в рассуждениях не уехать (=

Почему? :)

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

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

Где найти баланс это вопрос интуции, чувства меры и договоренностей внутри команды…
Да, пардон, привиделось другое. С сутью согласен, аналогично с мёртвым кодом «на будущее».
Комментарий, как и код, может быть как легким и понятным, так и запутанным. Вряд ли с плохим кодом рядом будет стоять великолепный комментарий, написанные в одно время.

Код как-никак контролируется компилятором/исполняющей средой, которая как минимум не допустит его несоответствия в разных частях программы.
Так без комментария непонятно зачем в GetFileEncoding() первой строкой идёт Peek() и можно ли его убрать.
  1. Зато теперь понятно, что Peek() является частью получения кодировки файла. Отсюда вытекает вывод, что убирать его нельзя.
  2. Как я уже написал выше, внутрь метода GetFileEncoding() читатель будет проваливаться лишь в том случае, если есть баг с получением кодировки. А т.к. бага нет — то никогда. Вряд ли разработчик будет бродить по коду системы и читать его из чистого любопытства.

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

1. Нет. От того что он там находится не значит что он там нужен. Да и вообще. Может то что он нужен — было в позапрошлой версии библитеки, а уже и не нужен вовсе. Или эта функция использовалась для отладки и этот вызов забыли удалить перед коммитом в репозиторий и это часть отладочного кода.
2. Читатель/писатель провалиться в этот метод если захочет скопировать функциональность в другое место.

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

Отсюда вытекает абстракция. Если вас не интуресуют тонкости имплементации, вы не станете лезть в эти дебри, а мухи останутся отделенными от котлет благодаря внятному именованию.

Читатель/писатель провалиться в этот метод если захочет скопировать функциональность в другое место.

Он просто скопирует вызов, оставив его как есть.

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

Краткие и лаконичные — это как раз о искусстве именования сущностей. И да, если не умеете — оправдываетесь в комментах.

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

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

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

unsafeSingleTreadDispatch? В любом случае, вы не привели пример необходимого краткого и лаконичного комментария. Логики.

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

Все остальные аспекты — внутри метода. В чем проблема? Но вы так и не привели пример, без этого нечего обсуждать.

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

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

То есть, в вашем «кратком и лаконичном комментарии описывающем логику» вы опишите и другие методы с компонентами? Интересно было бы увидеть пример… По моему, параметрами «кратко» «лаконично» и «логика» вы выстрелили себе в ногу, а сейчас вы делаете вид, что вам совсем не больно.
Тогда по-вашему и тесты писать не нужно, все же внутри метода можно посмотреть )

По моему, этого никак не следует из моих слов.

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

Покажите пример того, что именно программист увидит во всплывающей подсказке. Чтобы это было именно описание логики и кратко. Вот сейчас, в своей IDE я вижу всплывающую подсказку при наведении на класс, в которой содержатся его методы, что они принимают на вход и что возвращают. И это без каких либо комментариев. А как у вас?

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

Еще раз: покажите пример комментария, который «полностью отражают логику, нюансы и способ использования». Вот чтобы и «кратко» и «полностью». Вы заявили, что люди не пишут комментарии просто потому, что не умеют их писать. Покажите как надо. Утрите нос салагам.
Вопрос не ко мне, но постараюсь ответить. Возьмем (к примеру!, понятно что это за уши притянуто, можно было бы какой-нибудь алгоритм шифрования взять) quicksort (ну или какую-то довольно сложную бизнес-логику). Как бы Вы ни старались сделать его реализацию самодокументируемой, без описания не обойтись. Или возьметесь? С Вас самодокументируемый код на языке программирования по Вашему выбору, который реализует алгоритм быстрой сортировки. При этом любой человек, даже который не был знаком ранее с этим алгоритмом, должен с первого взгляда понять, что этот код делает, как, и какова его вычислительная сложность.

И на что вы, простите, ответили? Я не вижу никакого ответа. Как и примера краткого и, при этом, исчерпывающего комментария. Возможно вы просто не прочитали тред сначала.

Мне не следовало использовать слово «ответить», каюсь. Я хотел показать, что есть ситуации, в которых без документирования кода не обойтись, как бы ни стараться. Попробуйте меня переубедить, написав самодокументируемый quicksort.
UFO just landed and posted this here
Покажите краткое и лаконичное описание алгоритма quicksort, которое полностью отражают логику, нюансы и способ использования.

Это слишком тривиальная задача. Берется описание алгоритма из той же Википедии, сокращается по возможности и вуаля. Можно еще ссылку добавить. А вот написать самодокументирующийся quicksort — задача нетривиальная, вернее невозможная вообще. Или любой другой алгоритм. Я уже здесь писал, что если в методе пара строчек, то и документировать нечего, но если появляются условия и циклы, пора задуматься о том, чтобы объяснить себе в будущем, или другим кто будет разбираться в коде, что он делает и почему именно так, а не иначе. Я же не имею в виду подобное:
$index = 0; // здесь переменной $i присваивается 0

Самодокументирующиеся название реализации quicksort — это quicksort. Если уж совсем упарываться — wellKnownQuickSort.

Это как пример, понятно что в большинстве современных языков и так есть ее реализация. Я к тому веду, что в некоторых случаях без комментирования не обойтись.
UFO just landed and posted this here
Это слишком тривиальная задача. Берется описание алгоритма из той же Википедии, сокращается по возможности и вуаля. Можно еще ссылку добавить. А вот написать самодокументирующийся quicksort — задача нетривиальная, вернее невозможная вообще

Почему же? Описание на языке программирование это тот же английский просто немного в другой форме.

Вот тут есть пример программы на Java с комментариями. Те же самые комментарии можно записать так (обратите внимание, что все комментарии сохранены):

Код
public void sortPartOfArrayRecursively(int[] arrayForSorting, int startingIndex, int endingIndex)
    {
        if (startingIndex < endingIndex)
        {
            int partitionIndex = dividedArrayToPartitionSmalerAndGreaterThenPivot(arrayForSorting, startingIndex, endingIndex);

            sortBeforePartitonIndex(arrayForSorting, startingIndex, partitionIndex );
            sortAfterPartitonIndex(arrayForSorting, partitionIndex, endingIndex);
        }
    }

    private void sortBeforePartitonIndex(int[] arrayForSorting, int startingIndex, int partitionIndex) {
        sortPartOfArrayRecursively(arrayForSorting, startingIndex, partitionIndex - 1);
    }

    private void sortAfterPartitonIndex(int[] arrayForSorting, int partitionIndex, int endingIndex) {
        sortPartOfArrayRecursively(arrayForSorting, partitionIndex+1, endingIndex);
    }
    
    private int dividedArrayToPartitionSmalerAndGreaterThenPivot(int[] arrayForSorting, int startingIndex, int endingIndex)
    {
        int pivotElement = getLastElementAsPivot(arrayForSorting, endingIndex);

        int indexSmallerElement = (startingIndex-1);

        indexSmallerElement = placeAllSmalerThanPivotToLeftAndGreaterToRigthOfPivot(
                arrayForSorting, startingIndex, endingIndex, pivotElement, indexSmallerElement
        );

        swapTwoArrayElements(arrayForSorting, indexSmallerElement+1, endingIndex);
        return indexSmallerElement+1;
    }

    private int placeAllSmalerThanPivotToLeftAndGreaterToRigthOfPivot(int[] arrayForSorting, int startingIndex, int endingIndex, int pivotElement, int indexSmallerElement) {
        for (int i = startingIndex; i < endingIndex; i++)
        {
            int currentElement = arrayForSorting[i];
            if (currentElement <= pivotElement)
            {
                indexSmallerElement++;
                swapTwoArrayElements(arrayForSorting, indexSmallerElement, i);
            }
        }
        return indexSmallerElement;
    }

    private int getLastElementAsPivot(int[] arrayForSorting, int endingIndex) {
        return arrayForSorting[endingIndex];
    }

    private void swapTwoArrayElements(int[] arrayForSorting, int indexFirstElement, int indexSecondElement) {
        int tempStorage = arrayForSorting[indexFirstElement];
        arrayForSorting[indexFirstElement] = arrayForSorting[indexSecondElement];
        arrayForSorting[indexSecondElement] = tempStorage;
    }



Код с русскими именами перевенных и функций в стиле 1С
    public void СортировкаЧастиМассиваРекурсивно(int[] массивКоторыйСортируем, int начальныйИндекс, int конечныйИндекс)
    {
        if (начальныйИндекс < конечныйИндекс)
        {
            int индексБазовогоЭлемента = делимМассивНаЧастиСЭлементамиБольшеИМеньшеБазовогоЭлемента(
                    массивКоторыйСортируем, начальныйИндекс, конечныйИндекс);

            СортируемРекурсивноМассивДоБазовогоЭлемента(массивКоторыйСортируем, начальныйИндекс, индексБазовогоЭлемента );
            СортируемРекурсивоМассивПослеБазовогоЭлемента(массивКоторыйСортируем, индексБазовогоЭлемента, конечныйИндекс);
        }
    }

    private void СортируемРекурсивноМассивДоБазовогоЭлемента(int[] массивКоторыйСортируем, int начальныйИндекс, int индексБазовогоЭлемента) {
        СортировкаЧастиМассиваРекурсивно(массивКоторыйСортируем, начальныйИндекс, индексБазовогоЭлемента - 1);
    }

    private void СортируемРекурсивоМассивПослеБазовогоЭлемента(int[] массивКоторыйСортируем, int индексБазовогоЭлемента, int конечныйИндекс) {
        СортировкаЧастиМассиваРекурсивно(массивКоторыйСортируем, индексБазовогоЭлемента+1, конечныйИндекс);
    }

    private int делимМассивНаЧастиСЭлементамиБольшеИМеньшеБазовогоЭлемента(int[] массивКоторыйСортируем, int начальныйИндекс, int конечныйИндекс)
    {
        int базовыйЭлемент = беремПоследнийЭлементКакБазовый(массивКоторыйСортируем, конечныйИндекс);

        int индексНаименьшегоЭлемента = (начальныйИндекс-1);

        индексНаименьшегоЭлемента = размещаемВсеЭлементыМеньшеБазовгоСлеваВсеЭлементыБольшеБазовогоСправа(
                массивКоторыйСортируем, начальныйИндекс, конечныйИндекс, базовыйЭлемент, индексНаименьшегоЭлемента
        );

        меняемДваЭлементаМассиваМестами(массивКоторыйСортируем, индексНаименьшегоЭлемента+1, конечныйИндекс);
        return индексНаименьшегоЭлемента+1;
    }

    private int размещаемВсеЭлементыМеньшеБазовгоСлеваВсеЭлементыБольшеБазовогоСправа(int[] массивКоторыйСортируем, int начальныйИндекс,
                                                                                      int конечныйИндекс, int базовыйЭлемент, int индексНаименьшегоЭлемента) {
        for (int i = начальныйИндекс; i < конечныйИндекс; i++)
        {
            int текущийЭлемент = массивКоторыйСортируем[i];
            if (текущийЭлемент <= базовыйЭлемент)
            {
                индексНаименьшегоЭлемента++;
                меняемДваЭлементаМассиваМестами(массивКоторыйСортируем, индексНаименьшегоЭлемента, i);
            }
        }
        return индексНаименьшегоЭлемента;
    }

    private int беремПоследнийЭлементКакБазовый(int[] массивКоторыйСортируем, int конечныйИндекс) {
        return массивКоторыйСортируем[конечныйИндекс];
    }

    private void меняемДваЭлементаМассиваМестами(int[] массивКоторыйСортируем, int индексПервогоЭлемента, int индексВторогоЭлемента) {
        int временноеХранилище = массивКоторыйСортируем[индексПервогоЭлемента];
        массивКоторыйСортируем[индексПервогоЭлемента] = массивКоторыйСортируем[индексВторогоЭлемента];
        массивКоторыйСортируем[индексВторогоЭлемента] = временноеХранилище;
    }

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

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

причем возможно мне для решения моей задачи оно и не нужно было

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

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

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

Разумеется, описывать что-то обычно удобнее русским/английским языком, чем некоторым формальным языком, предназначенным для компиляции в машиный код. У самодокументрованного кода другая фишка — он автоматически менятся при любом обновлении (если программисты не подходят к ревью совсем халатно) и не устареевает.

P.S. Разумеется, во всем нужно искать золотую середину и не стоит уходить в крайности, я вот, например, считаю, что публичные интерфейсы бибилиотек всегда должны содержать полное описание. Да и от редких вставок комментариев, описывающих реально непонятные места из кода, тоже ничего плохо не будет. Нет необходимости добиваться 100% самодокументированости (это возможно, просто муторно и код станет плохим).
Я взял quicksort лишь как пример. Есть гораздо менее известные алгоритмы. И тут ну никак не помешает краткое (не надо мне приписывать желание многостраничные произведения в комментарии писать) изложение предназначения кода и описание его реализации, возможно со ссылками на более детальные источники.

Я тоже за золотую середину, и очень хорошо если код действительно самодокументируемый, я только за. Только вот может оказаться так, что чисто субъективно он будет самодокументируемым разве что для его автора. Здесь в обсуждениях те, кто за комментарии и документирование, вроде как не призывают комментировать каждую строчку (это другая крайность), а только потенциально сложные участки кода. В идеале, это коммент в шапке более-менее сложных методов (больше двух-трех строк), по необходимости — и внутри метода. Те же, кто здесь выступает за самодокументирующийся код, похоже вообще отрицают и документацию и комментарии в любом виде, типа у них код всегда так хорош, что ничего другим объяснять не надо, а кто не может с первого взгляда разобраться — дурак и всё тут.

Когда на одном из проектов были формальные требования к написанию комментариев типа "коммент в шапке более-менее сложных методов (больше двух-трех строк)", тов шапке просто "переводил" название метода со всяких camelCase'ов на более-менее человеческий английский. Есть ли польза от такой документации кому-то? Ведь если я субъективно считаю свой код самодокументируемым, то что-то свыше такого перевода я и в комментарии не напишу.

А вы попробуйте не просто переводить, а подумать, какие нюансы использования у вашего кода могут быть, которые невозможно выразить средствами языка (а почти все нижеперечисленное невозможно нигде выразить). Что произойдет, если передать в метод неправильные данные? Некорректная работа? Исключение? Возврат кода ошибки? Крах приложения? Если исключение или код ошибки, то как вызывающему понять, в чем проблема? Какие есть замечания по производительности? Рекомендации использовать что-то другое, если она критична. С какими классами/методами еще желательно ознакомиться. Если использовать объект из нескольких потоков, то что будет?

Если подумаю, то я это отражу в названии.


В целом, наверное, надо различать тут комментарии вообще и технические *doc комментарии, дублирующие или дополняющие средства языка для фиксации контракта. Одно дело написать что-то вроде


/**
 * @param int[] $ids
 * @return static
 * @throw NotFound
 * @throw TypeError
 */
function getByIds(array $ids): self {}

и совсем другое писать над этим сочинение

Вы, видимо, также не прочитали тред сначала. Человек заявил, что про самодокументируемый код рассказывают те, кто не умеет писать краткие и лаконичные комментарии. Затем он добавил, что комментарий должен описывать логику реализации. На что я, как мне кажется, вполне справедливо, попросил показать пример такого комментария (многократно). При этом пообещав написать понятное без комментариев имя метода. На каком этапе я вдруг стал обязан писать квиксорт? Зачем вообще комментировать реализацию квиксорта, что это за бред?
Пожалуйста:
/*
Quicksort is a divide and conquer algorithm. Quicksort first divides a large array into two smaller sub-arrays: the low elements and the high elements. Quicksort can then recursively sort the sub-arrays. The steps are:
1. Pick an element, called a pivot, from the array.
2. Partitioning: reorder the array so that all elements with values less than the pivot come before the pivot, while all elements with values greater than the pivot come after it (equal values can go either way). After this partitioning, the pivot is in its final position. This is called the partition operation.
3. Recursively apply the above steps to the sub-array of elements with smaller values and separately to the sub-array of elements with greater values.
More details here: https://en.wikipedia.org/wiki/Quicksort
*/

Повторяю еще раз для особо одаренных: quicksort взят только как пример, есть алгоритмы гораздо менее известные, и вовсе не о сортировке.

P.S. Я полагаю, имея подобный комментарий, тот кто читает исходник гораздо быстрее разберется, что там происходит, чем имея голый код, пусть даже и «самодокументируемый». Может быть, у него нет цели выяснить все нюансы его работы, а достаточно просто понять, зачем это здесь? Одно дело — прочесть краткое описание, совсем другое — копаться в исходнике.
Вы, я думаю, должны понимать, что в этой вашей «лаконичной и краткой» (нет) простыне не хватает всего нескольких слов, чтоб полностью описать алгоритм на обычном английском.
А когда у вас в комментариях полное описание алгоритма на естественном языке, то достаточно прозрачно написанная реализация в коде будет настолько же хорошим описанием, насколько и простыня текста.
Это достаточно краткое описание алгоритма, и имея его гораздо проще разобраться с вот этим «самодокументируемым» кодом (опять же, абстрагируемся от quicksort):
Код
public void sortPartOfArrayRecursively(int[] arrayForSorting, int startingIndex, int endingIndex)
    {
        if (startingIndex < endingIndex)
        {
            int partitionIndex = dividedArrayToPartitionSmalerAndGreaterThenPivot(arrayForSorting, startingIndex, endingIndex);

            sortBeforePartitonIndex(arrayForSorting, startingIndex, partitionIndex );
            sortAfterPartitonIndex(arrayForSorting, partitionIndex, endingIndex);
        }
    }

    private void sortBeforePartitonIndex(int[] arrayForSorting, int startingIndex, int partitionIndex) {
        sortPartOfArrayRecursively(arrayForSorting, startingIndex, partitionIndex - 1);
    }

    private void sortAfterPartitonIndex(int[] arrayForSorting, int partitionIndex, int endingIndex) {
        sortPartOfArrayRecursively(arrayForSorting, partitionIndex+1, endingIndex);
    }
    
    private int dividedArrayToPartitionSmalerAndGreaterThenPivot(int[] arrayForSorting, int startingIndex, int endingIndex)
    {
        int pivotElement = getLastElementAsPivot(arrayForSorting, endingIndex);

        int indexSmallerElement = (startingIndex-1);

        indexSmallerElement = placeAllSmalerThanPivotToLeftAndGreaterToRigthOfPivot(
                arrayForSorting, startingIndex, endingIndex, pivotElement, indexSmallerElement
        );

        swapTwoArrayElements(arrayForSorting, indexSmallerElement+1, endingIndex);
        return indexSmallerElement+1;
    }

    private int placeAllSmalerThanPivotToLeftAndGreaterToRigthOfPivot(int[] arrayForSorting, int startingIndex, int endingIndex, int pivotElement, int indexSmallerElement) {
        for (int i = startingIndex; i < endingIndex; i++)
        {
            int currentElement = arrayForSorting[i];
            if (currentElement <= pivotElement)
            {
                indexSmallerElement++;
                swapTwoArrayElements(arrayForSorting, indexSmallerElement, i);
            }
        }
        return indexSmallerElement;
    }

    private int getLastElementAsPivot(int[] arrayForSorting, int endingIndex) {
        return arrayForSorting[endingIndex];
    }

    private void swapTwoArrayElements(int[] arrayForSorting, int indexFirstElement, int indexSecondElement) {
        int tempStorage = arrayForSorting[indexFirstElement];
        arrayForSorting[indexFirstElement] = arrayForSorting[indexSecondElement];
        arrayForSorting[indexSecondElement] = tempStorage;
    }

абстрагируемся от quicksort

Практически любой текст на естественном языке можно перевести в его аналог на почти любом языке программирования.

Берем описание quicksort из вики

Общая идея алгоритма состоит в следующем:

1. Выбрать из массива элемент, называемый опорным. Это может быть любой из элементов массива.
2. Сравнить все остальные элементы с опорным и переставить их в массиве так, чтобы разбить массив на три непрерывных отрезка, следующих друг за другом: «элементы меньшие опорного», «равные» и «большие».
3. Для отрезков «меньших» и «больших» значений выполнить рекурсивно ту же последовательность операций, если длина отрезка больше единицы.


Записываем тоже самое в исходных кодах
public АлгоритмСортировкиQuickSort(...) {
   var базовыйЭлемент = взятьПроизвольныйЭлементКакБазовый(...);
   сравнитьОстальныеЭлементыСБазовымИПереставитьТеЧтоБольшеВлевоТеЧтоМеньшеВправо(...);
  рекурсивноПрименитьQuickSortДляЭлементовМеньшеБазового(...);
рекурсивноПрименитьQuickSortДляЭлементовБольшеБазового(...);

Да в исходном код, вы не добавите графиком и прочей визуализации, но этого вы не добавите и в комментарии. В результате разница только в возможности дать ссылку на статью с алгоритмом, поэтому весь необходимый комментарий будет
/* Реализация QuickSort https://en.wikipedia.org/wiki/Quicksort */

А это разве краткий и лаконичный комментарий?

То есть, по вашему, краткий и лаконичный комментарий может быть длиннее по количеству символов чем непосредственная реализация? Это, по вашему, кратко и лаконично? Ну ок. Спасибо, не нужно. Мне проще смотреть на исходник. А если пойти дальше, то мне вообще должно быть плевать на детали реализации, если мой контекст — снаружи метода. Если мне понадобилось разобраться — значит в реализации проблема, которую нужно решить и разбираться именно с кодом. Если вернуться к моим изначальным тезисам: да, иногда комменты нужны, но редко. Чаще всего они являются индикатором проблемы (плохой архитектуры, глючного окружения и т.д.) либо плохо написанного кода. В исходниках я часто встречаю комменты типа «масло масляное» и появляются они благодаря замечательным гайдам и конвенциям. По сути — это просто мусор, и именно против такого я выступаю.

А если вместо этой простыни будет только ссылка на описание алгоритма?


Ну ведь бывает, что реализуешь что-нибудь эдакое с графами. Вот назову я метод GetStronglyConnectedComponents. Вы сходу определите, что там внутри алгоритм Тарьяна а не Косарайю? А если добавить фамилию в название метода — это уже implementation details, не важные для вызывающего кода.

UFO just landed and posted this here
перепишет алгоритм на другой, а ссылка или описание останется прежним

А код-ревью тогда зачем?


Зачем человеку знать какие-то детали реализации при вызове метода?

Ну так я об этом и говорю, что не надо. Только вот потом кто-то захочет узнать например memory cost и time complexity вашего алгоритма. И как он это должен понять? Вдумчивым чтением исходников? (я сейчас не про библиотечные сортировки).


Вообще бред какой-то получается: в этом треде нет ни одного совета комментить все в стиле "капитана". Зато регулярно слышны призывы "все комменты зло".

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


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

public static void Splice(QuadEdge a, QuadEdge b)
{
	var alpha = a.ONext.Rot;
	var beta = b.ONext.Rot;

	var t1 = b.ONext;
	var t2 = a.ONext;
	var t3 = beta.ONext;
	var t4 = alpha.ONext;

	a.SetNext(t1);
	b.SetNext(t2);
	alpha.SetNext(t3);
	beta.SetNext(t4);
}

Похоже на объединение двух четырехсвязных списков. Или наоборот, на вырезание куска (это одно и то же, ведь операция — самообратная).


UPD: упс, двусвязных, конечно же. Название QuadEdge меня как-то смутило. Но тут точно проблема не в отсутствии комментариев, а в странных названиях.


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

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

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

И может тогда это кусок кода легко будет понять без всяких комментариев. А так понятно, вообще любой кусок фигового кода не будет сам по себе понятным.
ОК, переименуем мы a и b в firstEdge и secondEdge. Splice — в AttachTwoEdgesTogetherOrBreakThemApart. Можем даже разбить на несколько методов. Только я сомневаюсь, что станет сильно понятнее.

А можно в комментарии дать ссылку на оригинал публикации, в которой не только разъясняются, для чего нужен QuadEgde, и что такое Splice, но и математически доказывается корректность алгоритма. И тому, кто будет разбираться с кодом намного удобнее будет видеть a, b, alpha, beta, Splice, Rot (1-в-1 соответствующие примерам в статье), а не наши искуственно выделенные функции и переименованные аргументы.
UFO just landed and posted this here
Отсутствие комментариев вовсе не самоцель, если без ссылки на статью ваш замысел никак не объяснить, значит давайте ссылку на статью.

Как я уже писал должна быть золотая середина, а не крайности, 100% самодокументровать (вплоть до отсутствия в репозитории файла readme.md), как и 100% покрытие комментариями вообще всего вплоть до очевидных вещей — одинаково нелепые идеи.

Просто у автора этой статьи, например, попытка доказать, что из-за того, что встречаются редкие случаи когда комментарии действительно НЕОБХОДИМЫ, самодокументированный код не нужен вообще НИКОГДА. Но это логически неверно.
UFO just landed and posted this here
Как по мне, так подобные четырехэтажные названия еще больше засоряют код, чем комментарии, и при этом хуже объясняют происходящее. В данном случае как минимум необходим комментарий, объясняющий, по каким причинам этот метод был реализован в таком виде.
При хорошей композиции таких методов и не должно появляться. А на вопрос «почему так» ответом может быть многотомный талмуд. Причем, полностью состоящий из ошибочных предпосылок.
При хорошей композиции таких методов и не должно появляться.


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

А на вопрос «почему так» ответом может быть многотомный талмуд.


Талмудов никто и не требует. Достаточно пары фраз.

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

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

Ну и есть разница между "может" и "хочет". Обычно разработчикам не всё равно даже какой код писать.

В 99% случаев причина не безопасности такого метода будет: потокобезопасности в требованиях не было.

// Due to bug in version 2.5.345 we have to explicitly add [something] so that clients using that version would not crash


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

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

Покажите пожалуйста, как переписать этот код, чтобы он был самодокументируемым?


process_singleton_posix.cc


bool SymlinkPath(
  const base::FilePath& target, const base::FilePath& path
) {
  if (!base::CreateSymbolicLink(target, path)) {
    int saved_errno = errno;
    if (ReadLink(path) != target) {
      errno = saved_errno;
      PLOG(ERROR) << "Failed to create " << path.value();
      return false;
    }
  }
  return true;
}

Скрытый текст
bool SymlinkPath(
  const base::FilePath& target, const base::FilePath& path
) {
  if (!base::CreateSymbolicLink(target, path)) {
    // Double check the value in case symlink suceeded
    // but we got an incorrect failure due to NFS packet loss & retry.

    int saved_errno = errno;
    if (ReadLink(path) != target) {
      // If we failed to create the lock,
      // most likely another instance won the startup race.

      errno = saved_errno;
      PLOG(ERROR) << "Failed to create " << path.value();
      return false;
    }
  }
  return true;
}

chrome_service.cc


void BindConnector(
  service_manager::mojom::ConnectorRequest connector_request
) {
    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

    base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO})
        ->PostTask(FROM_HERE,
            base::BindOnce(
                &IOThreadContext::BindConnectorOnIOThread,
                base::Unretained(this),
                std::move(connector_request)
            )
        );
}

Скрытый текст
void BindConnector(
    service_manager::mojom::ConnectorRequest connector_request
) {
    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

    // NOTE: It's not safe to modify |connector_request_| here
    // since it's read on the IO thread. Post a task instead.
    // As long as this task is posted before any code attempts
    // to connect to the chrome service, there's no race.
    base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO})
        ->PostTask(FROM_HERE,
            base::BindOnce(
                &IOThreadContext::BindConnectorOnIOThread,
                base::Unretained(this),
                std::move(connector_request)
            )
        );
  }

Да вроде все и так понятно.

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

Потому что оно не имеет к коду никакого отношения. Эта информация уместна на уровне проекта или класса, но в отдельном методе это мусор.

Ок, приведите пример, как бы вы это описали на уровне проекта или класса? Особенно фразу "It's not safe to modify |connectorrequest| here".

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


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

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

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


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

Ну на самом деле в первом случае проверяется, нет ли уже существующей ссылки куда надо — типа, если мы не смогли создать ссылку, но только потому, что она уже есть, считаем нашу работу выполненной. Либо нашим вызовом ссылка создалась, но после него было еще какая-то работа, скрытая от нас абстракцией (как в данном случае — пересылка результата по сети). Все это действительно так очевидно? Вот вы этого не написали и полагаю, что в своем ответе вы имели ввиду вовсе не это. А еще можно поменять местами ReadLink и CreateSymbolicLink. На первый взгляд вроде можно, но потом вспоминаешь о многопоточности — и уже нет — если поменять вызовы местами, то в ситуациях с гонкой он сломается, а в текущем виде нет. Что, действительно все это описывает код?


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

Все это действительно так очевидно? Вот вы этого не написали и полагаю, что в своем ответе вы имели ввиду вовсе не это.

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

И впрямь, абсолютно невозможно догадаться что делает функция peek(). Надо написать рядом комментарий что peek заглядывает в файл. А то с таким названием хрен догадаешься.
И вообще надо дальше пойти. fileContents = reader.ReadToEnd(); Вот что это могло бы значить — абсолютно невозможно догадаться. Надо написать коммент, что это читает файл до конца, и что = приравнивает, и что скобки делают, и вообще все написать. А то как же
UFO just landed and posted this here
Какой-то странный спор получается, как спорить что лучше — молоток или отвертка и доказывать что и отверткой можно забивать гвозди.
Оба инструмента хороши к месту, до определенной степени взаимозаменяемы, но с попыткой на 100% заменить одно другим растет и цена такой замены. Чем меньше что-то подходит под данную цель, тем дороже и сложнее его использовать.

Мне кажется все остальное — из области лингвистики, наш разговорный язык к сожалению построен на бинарной логике правильно/неправильно, хорошо/плохо и т.д. что влияет и на наш стиль мышления.
Если бы он использовал более плавные переходы, как например в нечеткой логике, и споров вроде этого было бы намного меньше.
Кстати на эту же тему есть еще одно интересное наблюдение. Периодически хожу на собеседования и задаю вопрос — «есть ли у вас джуниоры». Примерно в половине случаев слышу ответ, нет, мы набираем только сеньоров. Вроде бы все здорово, но одно НО мне не дает покоя. Джун, он ведь как канарейка в угольной шахте. Если сдох (не может работать с кодом), то проблемы уже в проекте и они нешуточные. Джун мотивирует сеньоров писать более понятный код без экзотических выгибонов. Сеньор может написать лютую херню и сказать «ну мы же все здесь сеньоры, что здесь непонятного». А потом три месяца страдания с багами вокруг этого кода.
Как раз сеньоры и пишут очень понятный код, в котором, вроде, нет ничего сложного. Но именно поэтому они и сеньоры.
Сеньоры могут писать простой и понятный код, но далеко не всегда они это делают. Очень часто желание попробовать модный фреймворк перевешивает. В некоторых компаниях например, после посещения разработчиком конференции, он садится на две недели фиксить баги. Чтобы хайп выветрился.
Работаю системным аналитиком. Часто от программистов слышал тезис, что хороший код (который они или уже пишут, или уже завтра будут писать) должен быть читабельным и без комментариев. Причём, все, кто так говорят, возражают не только против комментариев в коде, но и против цитирования кода в документации. А документация, по их мнению, нужна всем, кроме, собственно, программистов, которым и так всё ясно.

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

Возражения против цитирования кода в документации схожие: код всё равно изменят без предупреждения авторов документации, документацию сложнее читать неподготовленным людям, всем надо вникать, зачем приведён именно этот код, наличие кода в документации навязывает программисту некоторые рамки, наличие в документации примеров (например, примеров запросов к БД) отнимает много времени при чтении.

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

На мой взгляд, утверждения о хорошем коде, который просто читается без всяких комментариев и документации, это признак незрелости, в стиле, я всё сейчас сам один сделаю как надо и объясню вам, почему нужно именно такое решение, а ваши изначальные требования более не важны.
Вообще то что хороший код не нуждается в комментариях это цитирование одного из тезисов из книжки Clean code за авторством Robert Martin человека весьма уважаемого и без всякого сомнения зрелого.
Есть существенная разница между «Хороший, чистый код не нуждается в комментариях» и «Комментарии в коде — плохая практика, нужно избегать комментирования кода». Если первое, хотя бы, интуитивно понятно, то второе выглядит как очередной новомодный максимализм. Наверное, в какой-нибудь книге комментарии в коде назовут антипаттерном.

Вообще, нужно написать правила для pre-commit hook, запрещающего использование комментариев в коде. А еще проверяющего именование функций и методов. Имя каждого класса и метода должно представлять законченное предложение на английском языке. Это сделать будет посложнее, но применив ИИ, думаю, и тут вскоре достигнут успеха, может даже будет проверяться соответствие смысла того, что делается, названию метода. И будет у нас прекрасный самодокументируемый код, как раз к восстанию машин. Непонятно только, зачем им надо будет что-то объяснять мешкам с мясом...

А это не максимализм. Это просто слишком привлекательная концепция для разработчиков. Примерно как "обсуждать чужую зарплату неприлично" привлекательна для работодателей.


"Зачем я буду пояснять, что имел в виду? Мой код и так божественный. А всех, кто не согласен, отправлю читать Мартина" =)

Что-то я не припомню чтобы вместо документации кода фреймворка или библиотеки я бы читал сам код… спасибо за перевод

Я вот, признаться, тоже удивлен количеством противников документации. С одной стороны никто писать ее не любит (сам такой). С другой стороны, давайте для примера рассмотрим гитхаб — да на репу без Readme или Wiki никто и не посмотрит. Получается какое-то ханжество: читать чужие доки мы любим, а поддерживать свои — нет.


Документирование как минимум уместно в публичном API. А комментарии к коду — при вызове внешних API. Для внутреннего кода как правило достаточно высокоуровневого описания архитектуры. Например, в виде диаграмм.


Вспомните какой-нибудь качественный open-source проект. Скорее всего, в нем будут и доки и диаграммы. А потом откройте исходники — скорее всего вы увидите комментарии к фиксам и воркэраундам вокруг сторонних API. Поскольку полезные проекты существуют не в вакууме.


P. S. Разумеется, это все не относится к одноразовым проектам, прототипам или тем, где описанием может служить сам интерфейс. Сначала код, потом доки. Если останется время.

ИМХО, люди бросились в две крайности — либо комментируем(документируем) весь код, либо не комментируем вообще. Наверное это связано с тем, что есть люди, которых если не заставлять описывать каждый чих, то они не будут описывать вообще ничего, в том числе нетривиальные для понимания костыли(workarounds) и «публичные» API.

P.S. На мой взгляд баланс он как обычно где-то посередине, но ближе к «не писать».
P.P.S. Люди, пишущие тяжелочитаемый код, пишут такие же тяжелочитаемые комментарии — исключительно личное наблюдение, которое не стоит экстраполировать на все.

На самом деле, мое мнение тоже где-то "посередине". Но когда в сообществе наблюдается явный перекос, все равно приходится принимать противоположную сторону.

Вы пишите правильно и никто не спорит, что если мы создаем публичный API с которым будут работать как с черным ящиком — описать все методы имеет смысл.

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

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

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


Так-то за несколько циклов вдумчивого ревью и рефакторинга можно добиться и самодокументированности. Но есть ли на это время? И хватит ли квалификации?

Ну а я так постоянно делаю. Только я еще и декомпилирую бинарники, чтобы не терять время на поиск исходников (благо пишу на .net где это не проблема).


В коде намного проще найти ответ на вопросы вида "можно ли передавать null в аргумент bar метода Foo" и "вызывает ли класс Baz метод Dispose у переданных зависимостей".

Я пишу комментарии о том, что и как делает мой код в сообщении к коммиту.
Это гарантирует, что комментарий не устареет относительно кода, к которому он относится.

А потом сидеть копаться в истории спустя пару лет? Это ж неудобно

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

Неплохой вариант, но я так понимаю вы еще пока не сталкивались с миграцией репозитория когда вся история схлопывается в один коммит.

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

Есть такое правило «хороший код в комментариях не нуждается». То что в этом месте нужен комментарий говорит только о том, что API у StreamReader — могло бы быть и получше. А комментарии для того кода что мы не можем поменять это более чем нормально.
Sign up to leave a comment.