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



Что тут вообще происходит?

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

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

Плюсы и минусы простого кода


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

Как-то раз я писал модуль для обогащения данных на C++, а это язык, который сам по себе сложнее для чтения, чем прочие, в силу своей многословности. В начале работы у меня было только два файла (.h/.cpp), весь код для имплементации размещался в них. Результатом стала ужасная перепутанная мешанина внутри, которая снаружи, однако, выглядела вполне рабочей. Никакую инспекцию подобный код в жизни благополучно не прошел бы.



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



xkcd 1513

Я разделил имплементацию на тридцать с лишним diff-ов – в то время я работал в компании, которая использовала стекированные diff-ы. Кстати сказать, вышло так, что в этом проекте я поставил личный рекорд – выстроил цепочку diff-ов максимальной длины. Поэтому я испытывал известную гордость за итог своей работы.

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

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

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

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


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

Есть такая известная поговорка: «Устранять баги вдвое сложнее, чем писать код». Именно поэтому, когда ChatGPT выдает какую-то ерунду, проще попробовать еще раз с новым запросом или просто написать с нуля самому, вместо того чтобы выискивать ошибки в его ущербном коде.

Мудреный код сложнее для чтения и доступен только «посвященным».
Ясный код прост с виду, но писать его труднее.

Еще пара соображений о понятном коде





Год №1: Напишу-ка этот код в одну строку со сложными абстракциями. До чего же я умный!
Год №Х: Напишу-ка этот код так, чтобы в будущем мне всё было в нем понятно.


  • Единственный метод, благодаря которому у меня стало получаться писать понятный, читабельный код – активная практика и следование конкретно изложенному руководству по стилю. Плюс помощь более опытных разработчиков, которые инспектировали мой код чуть ли не с лупой в руках. Поначалу было сущим мучением получать от них кучу замечаний и «придирок» по, казалось бы, несущественным стилистическим вопросам, но в конце концов мои страдания окупились.
  • Стиль имеет гораздо более существенное значение при написании кода, чем я думал изначально. Я зашел в программирование с «продуктового» конца и уже в процессе стал перемещаться на техническую сторону. Код я начал писать исключительно ради возможности открыть свой бизнес, и поэтому рассматривал его только как инструмент для достижения цели. Результатом стал никудышный код, не поддающийся поддержке. Только с опытом разработки и работе в команде ко мне пришло понимание, насколько важен ясный, читабельный код. И я в этом не уникален. Любой, кто профессионально писал код хотя бы год, приходит к тем же выводам.
  • В 2007 году Джон Кармак написал длинное электронное письмо о руководствах по стилю. Оно представляет немалый интерес.
  • Наиболее открытое руководство, пожалуй, принадлежит Google. Компания Vercel также недавно выложила свое руководство в открытый доступ. Ну а линтерами и стилизаторами в наше время пользуются уже практически все.

Комментарии (7)


  1. Fedorkov
    15.12.2023 18:47

    Код должен быть выразительным.

    Простой код должен выглядеть просто.

    Сложный код — достаточно сложным.

    Грязный хак должен выглядеть как грязный хак.

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


  1. D7ILeucoH
    15.12.2023 18:47

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


    1. duke_alba
      15.12.2023 18:47

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


    1. somagic
      15.12.2023 18:47

      В моем универе на 1 курсе был жёсткий код ревью


  1. murkin-kot
    15.12.2023 18:47

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

    Что это значит? Это значит, что в тексте программы мы должны видеть все относящиеся к делу мысли. Если нужно мысли там нет - всё, примитивная нейросеть страдает от невозможности понять, зачем нужен весь этот код. Поэтому обладатели слабеньких нейросетей постоянно танцуют с бубном вокруг "красоты" кода. Они не понимают, зачем нужна "красота", но на уровне инстинктов чувствуют - без неё не выходит приличный код. Поэтому они требуют от окружающих "красоты".

    Но поможем немощным мозгам, дадим наконец определение этой таинственной субстанции.

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

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

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

    Если смысл можно охватить одним взглядом - вот вам и счастье. Если охватить нельзя - это плохой код. Логично?

    Как охватить одним взглядом много смысла? Есть два направления - выразительность кода и его компактность. Тема текста статьи - про второе.

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

    Смысла.


    1. Johnneek
      15.12.2023 18:47

      Думать - это работа, а работа - это энергозатраты. И нейросеть, даже "сильная", обрабатывает и выдает результат не из сферического вакуума, а задействуя процессорное время. И энергию. Человек (да и не только он, в принципе все живые существа) исторически/эволюционно заточен на экономию и своего "процессорного времени", и энергии, так как в предыдущие тысячелетия этого требовало банальное физическое выживание. И хотя в наше время свободного "процессорного времени" достаточно много, а энергия дешева и легко доступна, на уровне своей самой базовой прошивки человек стремится ни процессор свой не перегружать, ни энергию попусту не тратить. И простой и понятный код, не требующий напряжения, этому человеку легок и приятен ;)


  1. Andy_U
    15.12.2023 18:47

    Так то Вы, конечно, правы, но я вот не увидел в статье вашего решения с картинки, понятного без наличия в коммментариях исходного условия задачи. И не увижу. С другой стороны, как на постановку этой задачи посмотришь, так и алгоритм решения сразу становится очевидным, и даже подсказка в решении становится видна (переворот "елочки"). Единственно, что я бы написал zip(a[:-1], a[1:], b).