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

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

И все же, о чем тут. Снова про комментарии, большие и маленькие буквы, суффиксы и префиксы в именах? Ставить ли точку с запятой, если это не обязательно? Глобальные переменные — хорошо это или нет?
int apply_to_page_range(struct mm_struct *mm, unsigned long addr,
…
        unsigned long end = addr + size;
        int err;
 
        if (WARN_ON(addr >= end))
                return -EINVAL;

Я бы больше типизации добавил для наглядности, да и во избежание множества проблем при развитии продукта и ошибок от невнимательности (хотя при 100% тестовом покрытии второе не так критично)
result_t apply_to_page_range(struct mm_struct *mm, addr_t addr,
…
        addr_t end = addr + size;
        result_t err;
 
        if (WARN_ON(addr >= end))
                return -EINVAL;
Слабая типизация C/C++ не очень спасёт от ошибок от невнимательности.
Ну какая-никакая, а даже enum иногда спасает, в отличие от int или даже unsigned long.
Только если enum class, а не просто enum.
Второй пример совершенного кода хотелось бы разобрать подробнее. Это код ядра Linux.
Простите, но качество кода Linux весьма далеко от совершенного; приводить его в пример я бы точно не стал. Там неплохой код для промышленного проекта такого масштаба и круга разработчиков (включая множество компаний, в которых код драйверов и ядра пишут разномастные программисты на зарплате с различными отношением к своей работе и представлением о прекрасном), но не более того. Вот смотришь, например, на дифф (это фикс CVE-2017-14497) и видишь, что как был до фикса говнокод, таким после фикса и остался.

Как-то в одном интервью Костя Белоусов, один из ведущих разработчиков FreeBSD, хорошо сказал про код Linux:
После этого интересного опыта я уже даже подумывал бросить аспирантуру, и возможность “покрутить код” под Unix-like-системы выглядела интересно.

Но чтение кода Linux’ а было мне неприятно: я бы так не писал.
Вот только дальше он пишет:
Я уже не помню детали, и в Linux’ е тот код, который вызвал у меня такое резкое неприятие, наверняка переписан уже несколько раз – нужно помнить, что это все происходило 12–15 лет тому назад.
Тот код, может, и переписан давно, но мой личный опыт последних нескольких лет подтверждает слова Кости. Количество (и качество) уязвимостей тоже как бы намекают.
Попросил Игоря ответить на вопрос. Вот:
Это вопросы масштаба. Как и любой другой код, код ядра содержит уязвимости разного кода. Уязвимости в коде не находят тогда, когда им не пользуются. Как и код Windows, код ядра распространен настолько широко и используется в настолько разных сценариях, что баги и уязвимости в нём находятся часто. Как сказал Глеб Жеглов, «Правопорядок в стране определяется не наличием воров, а умением властей их обезвреживать!».

Код ядра, подход коммюнити к разработке, подход к релизам (http://kroah.com/log/blog/2018/02/05/linux-kernel-release-model/) предоставляют всё, что нужно разработчикам, чтобы минимизировать количество подобных проблем.
Бог с ним, с количеством уязвимостей, это как раз хорошо ложится на «не находят тогда, когда не пользуются» — Linux популярное ядро, всё логично. У меня больше претензий к самим уязвимостям и закрывающим их патчам, и вот тут к «умению обезвреживать» возникают вопросы. Пять уязвимостей в реализации сокетов AF_PACKET за год — ну куда это годится? Справедливости ради, во фре такое тоже бывало: так, в своё время procfs(5) отключили по умолчанию из-за long history of security vulnerabilities and other problems, но в общем и целом, когда смотришь на фрёвые фиксы, почти всегда сразу понятно, в чем была проблема и как была пофикшена. В случае Linux зачастую приходится сидеть и раскуривать код (мне этим часто приходится заниматься, т.к. мы вынуждены поддерживать несколько версий ядра из разных веток, и вручную бэкпортить фиксы; постоянные изменения форматирования и переименования каких-нибудь констант, из-за чего оригинальный патч не прикладывается, — это отдельная грустная песня).
Попросил Игоря ответить.

Вот:
Качество кода — понятие субъективное, как я упомянул в начале статьи, у каждого разработчика свои примеры. Лично мне код ядра нравится и работать с ним было приятно, но дело не в этом. Этот код не идеален. Но это код, над которым работает, наверное, одно из самых больших и активных сообществ разработчиков. И это код, который, думаю, работает на наибольшем количестве устройств в мире. Я не могу с лету придумать другой пример кода, который работал бы в данный момент времени на сопоставимом количестве процессоров (я имею в виду процессоров в целом, всего, в мире), как код ядра Линукс. Это говорят о качестве кода и его сопровождаемости — плохой, некачественный код, который тяжело сопровождать, не получил бы такого распространения.
Это абсолютно не связанные вещи. Одно дело — какая либо библиотека, которая используется непосредственно программистами.
И другое дело — операционная система, которая «выстрелила», поскольку позволила закрыть целую нишу в системном ПО. Решения об использовании системы принимают все же не разработчики.
Я согласен с тем что код мог бы быть лучше, по стандартам 2018.

Но и как с какой-нибудь библиотекой — тут вопрос приоритетов:
1. она должна работать без багов,
2. она должна работать быстро
3. она должна быть хорошо написана

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

Так и с линуксом — я в его код не лезу и пока он развивается и работает — меня все устраивает.

Во многих компаниях разработчики вполне себе принимают решения какую систему использовать. Бизнесу зачастую не важно что там в технологическом стеке.
Чем больше людей участвует в разработке, тестировании и эксплуатации, тем больше вероятность того, что баги будут выявлены. Чисто математически.
Быстро — это понятие относительное. С чем сравнивать? На каких задачах смотреть?
А вот хорошо написано — это не важно для ОС, но важно для библиотеки. Поскольку у библиотеки должен быть удобный и понятный интерфейс, грамотно выстроеная логика и удобство просмотра исходников (полностью или частично) сторонними программистами.
Хороший код — это не бизнес требование для ОС. Поэтому разработчики не должны давать советы, основанные на этом критерии бизнес-пользователям.
У Linux есть важные преимущества с точки зрения бизнеса, именно они обычно и влияют на принятие решения.
Хороший код — это не бизнес требование для ОС

С этим не поспоришь. Я имел ввиду выбор ОС как таковой. Например, под линукс проще собирать опен-сорс код, а под windows — головная боль, а многие проекты и не заработают вообще. Это уже аргумент бизнесу.

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

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

Вы про проекты, изначально заточенные под Unix?
Да. Но, это не всегда очевидно. Одна библиотека тянет за собой кучу зависимостей и среди них может оказаться одна, заточенная под nix.

Я не раз сталкивался с багами под вин из-за того что разработчики банально не тестировали под нее (например рассчитывали только на линуксовые разделители файлов) и не собирались этого делать в будущем. По началу я думал: «ок, исправлю, сделаю пулл реквест», но потом надоело — зачем плыть против теченья?

Но это сервера и бэкенд. Наверное если бы я занимался каким-нибудь gamedev, то наоборот нужна была бы win.
(например рассчитывали только на линуксовые разделители файлов)


Под Windows есть специальные системы сборки типа Mingw/Cygwin, способные автоматически учитывать, то, что разработчики не учли подобные вещи. И даже автоматически учитывать куда как более серьезные отличия.

Вообще не очень то хороший пример «совершенного кода»
static void tlb_remove_table_one(void *table)
Т.к. из одной функции просто вызывается другая. Ещё надо долго покликивать, чтобы понять что она всё-таки делает(если человек до этого не знает).
«Макрос BUG_ON обычно вызывает печать содержимого регистров» — но в коментариях к коду это не прописано. Может в данном случае он делает то, что «необычно».
Лично у меня вызывает восхищение мой код, написанный мной более года назад. И восхищении из серии: «Тот кто это написал — сказочный д… б».
И наоборот, если через год понимаешь, что понаписал, значит код в целом удался.
Я выделяю три основных признака. Перечислю в порядке убывания значимости лично для меня.
— Красив визуально и эстетически, вне зависимости от языка. На него просто приятно смотреть!
— Красив алгоритмически. Решения и паттерны, используемые в коде, не должны быть корявыми, не должно быть разбитых окон.
— Лаконичен. Чем меньше комментариев — тем лучше, код должен стремиться к самодокументированию и емкости.
Вот согласен, только для себя я бы второй и третий пункт поменял местами. Просто потому что алгоритмическую красоту сложнее установить.

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

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

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

Ну а если нужна, то не пожертвовал бы. Это же очевидно.

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

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

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

Какая-то путаница в красивых и понятных алоритмах.

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

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

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

Отсутствие результата функции — как следствие не тестируемость — использование void — это плохо.

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

А если ее заходят вызвать оттуда, где не проверяют?

Ее проверяют прямо перед вызовом или там есть еще пару строк кода, внутри которых могут изменить адрес функции?

А если это сделают через пару лет, когда забудут что она внутри не проверяются?

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

В функции, имеющую одну строку тоже нету смысла — зачем ее иметь? Вызывайте внутреннюю функцию из внешней или сделайте ее макросом.

Отсутствие результата у функции — это принт?

Если нет как вы собрались ее тестировать?

Приведенные образцы кода будут забракованы в любой более-менее приличной компании с mission critical кодом.

Первая часть твоего комментария — это параноя, причём в самом отрицательном смысле. «А если захотят» — не захотят, «А если сделают» — нет, не сделают: есть экспортируемые функции, а есть внутренние, если программист использует внутренние функции, то он знает, что делает и он знает свои данные.

Однострочники в ядре — это часто обёртки над более общими вещами, а макросы не всегда уместны.

И далеко не всё можно тестировать, даже не так, далеко не всё нужно тестировать. Это тоже параноя.

Эти образцы могут быть забракованы только если рассматривать их вне контекста в котором они используются. И то, для этого нужно быть двинутым на всяких code complete настолько, что бы воспринимать их как обязательные к исполнению законы, а не как рекомендации.
Эта параноя, порой даже летает на самолетах.
Я вам привожу аргументы из практики компаний Forbs-100 где я обычно работаю.

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

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

Ну, всяким разным оупенсорсом и конкретно кодом ядра Linux интересуются чуть больше чем 2.5 человека, несмотря на мнение, что он ужасен. А минусы, увы, не мои.
Только тогда, когда естественный язык придет в программирование, мы станем читать классы и модули как рассказы и присуждать литературные премии лучшим программистам. :)
>> когда естественный язык придет в программирование
Не дай бог. Мне с лихвой хватает уже того, что задачи ставят на естественном языке. Если мы ещё и код начнём на нём писать, то человечество обречено.
Пайтон уже очень похож на controlled NL. И, во-многом, поэтому любим. И, да, я должен Вас огорчить, лет через пять-десять миллиардов наступит тепловая смерть вселенной, мы обречены…
Честно скажу, не вижу разницы между естественным языком и языком программирование, если хорошо владеть и тем и другим…
лет через пять-десять миллиардов наступит тепловая смерть вселенной
Нет! Через пару-тройку млрд лет Солнце гарантированно уничтожит Землю, но Вселенная просуществует до тепловой смерти ещё очень долго, на порядки больше. Правда не надо забывать вероятность большого разрыва или фазового перехода вакуума, что может случиться ещё раньше, если ускоренное расширение продолжится теми же темпами.

Да полноте… мы не знаем что будет на самом деле. Вы перечислили ГИПОТЕЗЫ, которые вовсе не являются 100% сценариями развития событий в проекции, известной нам по локальному световому конусу 4-мерного пространства-времени

лет через пять-десять миллиардов наступит тепловая смерть вселенной

А чё так нереально быстро?!
Тепловая смерть — это то когда погаснут все звёзды.
А через пять, или даже десять миллиардов лет всё ещё будет материал для появления новых звёзд.
— хорошая новость: материала для новых звёзд хватит как минимум ещё на триллион лет,
— плохая новость: Большой Разрыв ожидается гораздо раньше — уже через 80 миллиардов лет, согласно Итану habr.com/post/369327 (у Итана оценка 2014 года, в Википедии оценка 2011 года, согласно которой ожидалось через 22 миллиарда лет)
image
Вижу много критики в комментариях. Возможно соглашусь отчасти. Хотя меня интересует другое. Почему никто из комментаторов сам не приводит примеры хорошего кода? Это же гораздо полезнее. Критиковать то проще всего.
Потому, что в статье говориться о «божественном коде», а не просто хорошем. Такой писать не все умеют, а если и умеют, то его писать слишком долго, чтобы в реальной задаче позволили это сделать. Ну по крайней мере за себя говорю — не было, чтобы мог сказать, что вот здесь, всё идеально.
«Критиковать то проще всего.» — нет, хорошая критика требует большой работы.
void foo(void)
{
return;
}
Ни одной ошибки! Шутка но отчасти. Из комментариев видно, что у каждого свои представления о прекрасном, спорить можно, переубеждать не стоит. Вы в статье попытались провести анализ темы, и это видно, я лично именно это оценил. От себя добавлю, я научился читать программы, где то лет через 5-6, как стал получать деньги за то что их пишу. Может я ошибаюсь, но думаю у многих было так же. Вспомните «мама мыла раму».
Чем лучше код, тем он реже применяется, т.к. понять его могут немногие.
Или бывает: код очень хорош, но выглядит просто, и его считают простым, хотя это не так. При попытках изменения кода всё ломается, и другие говорят, что «вот, плохой код был!». Хоть это и не так.
У Linux — точно несовершенный код. Хотя бы из-за размеров и скорости разработки таковым его сделать не удастся.
Мой пример: код компилятора (или переводчика?) языка Eiffel от Бертрана Мейера. Обычно язык ограничен в средствах, т.к. создатель его смог написать только такой компилятор. У Eiffel же всё по максимуму.
«и другие говорят, что «вот, плохой код был!»» — и правильно. Хороший код это когда быстро можно понять, что он делает(если раньше его не разу не видел) и быстро внести изменения.
Второй пример совершенного кода хотелось бы разобрать подробнее. Это код ядра Linux. Код, который на момент написания статьи управляет работой 500 суперкомпьютеров из top 500, код, который работает в каждом втором телефоне в мире и который управляет большей частью серверов в сети Интернет.


Это говорит о нетребовательности к ядру ОС в различных системах. Какая по сути разница что там? Linux, FreeBSD или Mach? Ну работает и работает, свои функции выполняет.

Просто проще поддерживать обвязку всего прочего программного обеспечения с одним ядром ОС. Без относительно качества этого ядра.

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

Что по «качеству» ядра Linux — то просто посмотрите, что значит действительно мгновенная загрузка безо всяких там SSD и standby-in-memory на примере ОС Haiku.

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

НЛО прилетело и опубликовало эту надпись здесь

Мне кажется, сначала нужно определить, что считать хорошим кодом. Быстрый? Понятный? Лаконичный?


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


Другими словами — прежде чем оптимизировать, давайте определимся с оптимизируемым параметром:)

Зарегистрируйтесь на Хабре, чтобы оставить комментарий