Как стать автором
Обновить

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

Этот комментарий — ложь

Это верно для любого комментария (:
А где кот?

кот мертв. или жив

как говорят гуру, лучший код — это код, которого нет (в контексте дефолтных конструкторов и т.д.)

Код которого нет — чеширский код.
Читал я как-то такой код и ничего в нём не понял, потому что его не было

Обычно в таком случае наобот, все понятно (кода нет).

Краткий перечень контраргументов (неполный и с самопересечениями, то есть, довольно сумбурный):
  • Какого-то кода нет, но есть тот, который не стал идеальным (в смысле отсутствия)
  • Кода нет, но он работает
  • Строго говоря, код есть, только он выше по иерархии классов

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

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

Я лично в основном пишу todo комментарии, а так стараюсь не писать их. Лучше постараться написать код так, что бы выглядело понятно и читаемо, чем писать комментарии, которые потом быстро устареют. Да и отвлекает и просто лень. Зачем делать двойную работу.


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

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

Формулу Пифагора можно не комментировать, а быстрое разложение Фурье надо. То есть необходимость комментирования связана с тезаурусом разработчиков. Это пункт 1.
Не надо комментировать сам код, но обязательно надо комментировать, почему (отчего, какие требования) привели к созданию именно такого кода. Это пункт 2.
Многим программистам нет нужды до пункта 1 и пункта 2, поэтому могут говорить любую чушь про комментарии.

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

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

Упрощенно:
P1 * T1 > T2
P1 — вероятность, что комментарий будет нужен.
T1 — трудоемкость повторной добычи информации, которая могла бы содержаться в комментарии.
T2 — трудоемкость написания комментария при наличии добытой информации (в первый раз).

"Documentation is like sex: when it is good, it is very, very good; and when it is bad, it is better than nothing."

ничего избыточного в том примере нет, это надо было вынести из кода 100% потому что тогда намного проще покрыть код юнит тестами. вообще, думать о том "а как это покроется бнит тестами" очень полезно, в итоге kiss и solid сами собой выходят. кстати, это и к самим тестам применимо

блок комментариев не будет скопирован и вставлен слишком много раз

Интересно, зачем вообще копировать и вставлять блок комментариев? Не понял этот момент.
Главный принцип, которым следует руководствоваться — это здравый смысл. Нет никакого смысла комментировать код, если там и без комментариев всё понятно (т. е. хорошие идентификаторы, грамотная архитектура и т. д.). Есть смысл делать xmldoc или javadoc комментарии для публичных интерфейсов и их членов — так тем, кто их использует, не придётся каждый раз лезть в исходники (или декомпилятор) просто для того, чтобы понять, какие есть ограничения на входные данные, что будет на выходе и в каких ситуациях, какие исключения можем получить и в каких случаях (привет C#).

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

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

Слышал фразу «лучшая документация-это код». Пусть не всегда понятная, зато всегда доступная и актуальная.

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

// тут складываем 2 и 2
a = 2 + 2;


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

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

Из-за того, что _некоторые_ разработчики ленятся поддерживать описания _публичных_ методов в актуальном состоянии, предлагается отказаться от документирования методов вообще? Т.е. теперь пользователям этой условной библиотеки нужно будет смотреть код конструктора, чтобы узнать, что длина name ограничена 50 символами?
Однако есть другой вариант примерно той же фразы: «настоящие программисты не нуждаются в комментариях: текст программы все объясняет» (из древней статьи "Настоящие прграммисты не используют Паскаль")
И опубликован он там в таком контексте, от которого любой сторонник совершенного кода пришел бы в ужас.
Так что, «не все так однозначно»(с).

Вроде бы у Apache Foundation видел наставление, что комментарий должен отвечать на вопрос "почему?" (зачем?). А "что?" и "как?" должно быть ясно из кода. С тех пор пользуюсь этой практикой.

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

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

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

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

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

И вот именно там и должны быть комментарии: они должны описывать внешние, по отношению к коду, вещи.

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

О каких внешних по отношению к коду вещах идет речь? Общение с внешним миром — это такой же код.

Зато внешний мир — это уже не код. Или код — но не ваш. Что-нибудь типа:
  GetStatus(x); // GetStatus lazily creates instance of drill.
  SetDrillSize(x, 10); // Otherwise this may crash.
Да, в идеальном мире — API будет устроен по другому. И включение железки и опрос её состояния — будут разделены.

Но мы живём в реальном мире и наши возможности по изменению кода ограничены.

Вот на стыке того, что мы можем рефакторить и того, что не можем — комментарии вполне уместны.

Ибо из кода эту информацию, зачастую, увидеть нельзя.

Зато вы можете создать функцию GetStatusAndCreateDrill и избавиться от первого комментария. Со вторым тоже, думаю, что-то можно сделать.

Зато вы можете создать функцию GetStatusAndCreateDrill и избавиться от первого комментария.
Ультрагениальность восьмой степени.

А ничего, что при этом вам всё равно придётся оставить этот комментарий внутри функции GetStatusAndCreateDrill (иначе совершенно неяно, зачем вообще вызывать в ней GetStatus и зачем она нужна в принципе), и, кроме того, в том месте, где вы её вызываете — нужно будет написать почему просто CreateDrill вам не годится, а вот нужен обязательно GetStatusAndCreateDrill?

Нет, можно, конечно, завести функцию CreateDrillSafely, CreateMaceSafely и так далее и пойти по пути leftpad'ов (когда «красота кода» приводится в жертву эффективности). Но и даже в этом случае без специального комментария у вас нет никакого способа понять — когда вы можете заметить CreateDrillSafely на CreateDrill, а когда — не можете. Придётся, наверное, кеш заводить, чтобы цепочка CreateDrillSafely/CreateMaceSafely вызывала GetStatus один раз, а не дважды… и вот у вас уже вместо пары комментариев огромная сложная система… которая всё равно менее эффективна, чем исходное решение с несколькими комментариями.

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

А реально — правило простое: комментарий не должен описывать то, что и так видно из кода. То есть, как чуть выше писали: как правило хороший комментарий отвечает на вопрос «зачем?» или «почему?». А такие вопросы возникают как раз «на границах» вашей зоны ответственности.

А уже вопросы «как?» и «что?» — они видны из кода, да.

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

Вы выше писали, что GetStatus автоматически создает drill.
Не совсем так. Сверло не создаётся, как другие физические объекты. Они просто у станка есть.
Но манипулировать с ними нельзя, пока станок не инициализирован.
А инициализация его внутреннего состояния осуществляется, в частности, если запросить его статус.
GetStatus при этом, как по мне, напрямую вызывать вообще не стоит. Или вы при каждом ее вызове будете добавлять подобные комментарии?
Зависит от того, сколько раз её нужно вызывать. Если много (больше двух) раз с целью инициализации — то можно создать функцию, назвать её InitState и вызвать GetStatus там. Комментрий, в этом случае, всё равно останется, просто переедет в эту функцию.
Однако скорее всего в таком режиме (сделать устройство доступным) — она будет у меня вызываться ровно в одной функции.
Которая инициализирует всю систему и делает так, что состояние программы о внешнем мире правильно отражено в её внутренних переменных.

P.S. GetStatus — дорогая процедура, там целая самодиагностика запускается.

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


array.object.energy = array.object.mass * constant.magic.no_one_knows_for_what_reason_value_weird ** constant.exponentiation.form_c2_dont_know_why2_not256

Тут совершенно ничего не понять:


e = m*c**2

До идиотизма можно довести любую идею.


energy = mass * speedOfLight ** 2

Это правда :)


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


associate ( e => some%object(something%array_slice)%energy, &
            m => array%object(something%array_slice)%mass, &
            c => constant.magic.no_one_knows_for_what_reason_value_weird )

  e = m * c ** 2 

end associate
Это лишь при учёте, что вы знаете что такое энергия, что такое скорость света, и что такое масса.

Тот же пример в формате
dinglebob = GetBunch(shleem);
shluumyObject = PushToFlumbo(dinglebob)  +  fleeb.GetJuice();
blamps = shluumyObject.Rub;

Что всё это значит, и зачем нужно?
Мне нужно тратить пол часа, чтобы разобраться что делают все эти классы и методы?
А хватило бы одного комментария
//Создать первую часть плюмбуса

Этот комментарий номинируется на имя метода

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


// Calculate side length using the Pythagorean Theorem
// and put the value into variable "r2"
double delta = h*h-r1*r1;
double r2 = Math.Sqrt(delta);

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


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


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


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

double lengthSideB = Math.Sqrt(
Math.Pow(hypotenuse,2) — Math.Pow(lengthSideA,2);
)


Xmm. Вот реальный код из моего проекта 3d reconstruction. Ищет длины ребер прилежащих к вершине тетраэдра по трем углам и трем сторонам основания. Просто некуда всунуть длинные имена:
inline double qu(double a)
{
	return a*a; 
}
// 
// calculate 3 length of apex adjacent edges  from known 3 edges and 3 angles of base. Returns up to 4 solutions
int    getTetraFromLenghtsAndAngles(Point* OutPoints,double a,double b,double c,double cosA,double cosB,double cosC)
{
		double dd[5];
		dd[4] = 4*qu(b)*qu(c)*qu(cosA) -qu(qu(a)-qu(b)-qu(c));
		
		dd[3] = -4*c*c*(a*a+b*b-c*c)*cosA*cosB
					-8*b*b*c*c*qu(cosA)*cosC
					+4*(a*a-b*b-c*c)*(a*a-b*b)*cosC;
		
		dd[2] = 4*c*c*(a*a-c*c)*qu(cosB)
				       +8*c*c*(a*a+b*b)*cosA*cosB*cosC
				       +4*c*c*(b*b-c*c)*qu(cosA)
				       -2*(a*a-b*b-c*c)*(a*a-b*b+c*c)
					-4*qu(a*a-b*b)*qu(cosC);
		dd[1]= -8*a*a*c*c*qu(cosB)*cosC
				       -4*c*c*(b*b-c*c)*cosA*cosB
				       -4*a*a*c*c*cosA*cosB
				       +4*(a*a-b*b)*(a*a-b*b+c*c)*cosC;
		dd[0] = 4*a*a*c*c*qu(cosB)-qu(a*a-b*b+c*c);
		double sol[4];
		double soli[4];
		int         numOfSol;
		quartic(dd,sol,soli,&numOfSol);
		
		for(int i=0;i<numOfSol;i++)
		{
			double u = sol[i];
			double v= ((a*a-b*b-c*c)*u*u+2*(b*b-a*a)*cosC*u+a*a-b*b+c*c)/(-2*c*c*(cosA*u-cosB));
			double s1 = sqrt(a*a/(u*u+v*v-2*u*v*cosA));
			double s2 = u*s1;
			double s3 = v*s1;
			OutPoints[i].x = s1;
			OutPoints[i].y = s2;
			OutPoints[i].z = s3;
		}
		return numOfSol;
} 

Очень много оптимизировать можно — не считать повторно всякие a*a и подобное.

Это компилятор и сам умеет. Речь шла об говорящих именах. Если удлиннить имена, то текст станет совсем нечитаемым
Тут жалко, что нельзя вставить прямо в исходный текст чертеж тетраэдра. Потому что на чертеже сразу можно было бы подписать стороны основания и углы — и имена a, b и c сразу стали бы говорящими.
Впрочем, это как раз и был бы тот самый запрещенный автором статьи комментарий.

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


var a = tetrahedron.Side[4];

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


Во-вторых, если эти короткие имена, это общепринятые обозначения, то они читаемы. Например, мы пишем XMLDocument в коде вместо ExtensibleMarkupLanguageDocument.


В-третьих, можно частично сделать читаемые имена. От расшифровки dd, numOfSol, код не очень распухнет.

“I spent some time this weekend looking at very well-named, very clean, uncommented code implementing a research algorithm. I’m high-level familiar with it, the guy sitting next to me was the inventor, and the code was written a few years ago by someone else. We could barely follow it.”
Эта цитата про то, что без комментариев, даже с чистым кодом, хорошими названиями и автором алгоритма рядом, они еле-еле могли разобраться с реализацией этого алгоритма, написанной кем-то ещё. Т.е. цитата топит ЗА комментарии.
Про первый пример.
Несмотря на то, что кодовые операции в этом фрагменте легко понять, значение кода неясно.

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

И ещё одна мысль на аналогичную тему, но уже про имена.
В коде ASP.NET Core (ну, полез я его смотреть по какой-то надобности) мне встретилась такая вот строчка, и она мне запомнилась:
requestContext = await Listener.AcceptAsync().SupressContext();
Вроде нормальная строчка, без явных несовершенств — и имена осмысленные, и прочее.
Но я вот, читая ее (а читал я в обычном редакторе), вынужден был полезть выяснять, что это за SupressContext такой — у Task такого метода точно нет, к примеру. Нашел, конечно — и это оказался метод расширения, который тупо вызывает ConfigureAwait(false). И сразу — мысль: «ну вот что бы не написать было так, по-простому?». Ибо про ConfigureAwait(false) я по гроб жизни, наверное, не забуду — как, думаю, и любой, походивший по граблям с синхронизацией в асинхронных программах в окружениях, в которых число потоков, способных продолжить выполнение кода после ожидания, ограничено одним (ASP.NET Framework, конкретно, в моем случае) — а вот, что он имеет дело со штукой, называющейся «Context» — оно уже как-то подзабылось (не удивительно — в .NET у потоков, задач и т.д. разнообразных контекстов — не один и не два), потому сразу и не проассоциировал.
Так что хорошее для кого-то другого (например — того, кто с ASP.NET Framework или WPF с WinForms дела не имел) красивое название метода лично для меня оказалось менее понятным, чем некрасивое, но до боли привычное.

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

с_одной_стороны_вы_правы.НоСДругойСтороны(человеческий_язык ! всегда_позволяет_подобрать_достаточно_короткий && ёмкий_термин для ОписанияКакойТоСущности)

Если это заменяется на условный
zxc.AsDfg(qwe !a && f)
и всем сразу понятно что происходит, то коммент не нужен, иначе приходится жертвовать понятностью терминов в коде в угоду визуальной неперегруженности.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.