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

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

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

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

Код без комментариев

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

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

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

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

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

Медленно работающий код

Быстрый код = простой код. Но очень быстрый код — это сложный код. Поиск золотой середины в этом вопросе — задача нетривиальная.

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

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

Иногда проще и медленнее — это лучше, чем суперинтеллектуально и супербыстро.

Витиеватый код

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

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

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

Нелюбовь к сверхкомпактному коду имеет исторические предпосылки. Языки программирования, вроде APL, задуманные как очень компактные и эффективные за счёт применения собственных символов, почти исчезли. А вот другие языки, вроде Python, которые изобилуют разными скобками, продолжают набирать популярность.

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

Старый добрый код

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

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

Чрезмерное разнообразие свойств может всё запутать. 

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

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

Код собственными руками

Эксперты по эффективности любят повторять: «Не изобретайте велосипед». Используйте готовые протестированные библиотеки. Не брезгуйте унаследованным кодом — он проверен временем.

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

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

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

Предварительная оптимизация

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

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

Неправильно подобранные схемы и структуры данных порождают архитектуру, которую в дальнейшем едва ли можно будет оптимизировать. 

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

Беззаботность

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

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

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

Несогласованность

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

У такой согласованности есть своя цена. Прежде всего, это время, а иногда ещё и сложность. 

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

Более серьёзная проблема возникает в отношениях между разными разделами. В некоторых проектах используется унаследованный код. В других — есть зависимости от библиотек. Многие проекты не смогут работать без API, написанных совершенно другими людьми в других компаниях.

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

Любовь к техническим новинкам

Согласованность встаёт на пути у инноваций, ведь она подразумевает строгое следование заведённому порядку вещей.

Иногда, чтобы добавить новые функции, встроить новые библиотеки или интегрировать стек с новыми API, нужно отойти от старых схем. 

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

Несоблюдение правил

Ради шутки я спросил у Google Gemini, нарушали ли программисты какие-то правила, когда создавали его. Gemini ответил: 

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

«Большие языковые модели обучаются на массивных объёмах данных, и в том, как модель обучается на этих данных, всегда присутствует элемент неизвестности», — сказал Gemini. — «Некоторые приёмы, используемые при создании больших языковых моделей, могут быть очень эффективными. Но трудно точно понять, как именно модель формулирует ответы».

Вот оно! LLM (с англ. large language model — большая языковая модель) лучше нас знает, что правила устаревают и меняются. Возможно, если можно загрузить датасеты для обучения, уже не нужно тратить столько времени на понимание алгоритмов. Так что идите вперёд и будьте людьми. А о правилах пусть думают LLM.

Ближайшие курсы по программированию:

Топ бесплатных курсов и занятий:

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


  1. debet-credit
    24.05.2024 13:52

    Скажите, пожалуйста: "расшили" - это опечатка (и имелось в виду - "расширили")?

    Или это так и задумывалось (абзац "«Я бы не сказал, что программисты нарушили те или иные правила. Скорее, они расшили границы передовых методов работы при создании больших языковых моделей, таких как я». ")?


    1. ENRUStudio Автор
      24.05.2024 13:52

      Опечатка, исправила :) Спасибо, что заметили :)


  1. Ramayasket
    24.05.2024 13:52
    +8

    В вопросе правил две крайности:

    • правило не соблюдается вообще (100% исключений)

    • правило соблюдается абсолютно (0% исключений)

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


  1. Xeldos
    24.05.2024 13:52
    +26

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

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


  1. Aizz
    24.05.2024 13:52
    +3

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

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


    1. 1dNDN
      24.05.2024 13:52
      +3

      А я считаю наоборот. Описать, что делает внешний интерфейс и как это использовать - задача документации. ///summary перед методом - это тоже документация.

      А вот описать, зачем возникло какое-то неочевидное место - это как раз задача комментария.

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


      1. Aizz
        24.05.2024 13:52

        Каждую строчку кода описывать и не надо. Документировать саммари с описанием что делает интерфейс - это именно то, что я и написал. Но это не должно быть "Интерфейс предназначен для автоматической обработки инвойсов по запросу от ФНС со ссылкой на оригинальный заказ, в случае если бла-бла-бла" - это предназначено для документации. Это должно быть "Интерфейс обработки инвойса и возврата связанных документов". Причем, в идеале, каждый блок в этом интерфейсе должен быть описан таким же коротким комментарием. Код, конечно, лучше за себя говорит, но комментарии тебе как минимум помогут понять, что делает функция с логикой на 1000 строк раз в 10 быстрее.


        1. Wesha
          24.05.2024 13:52
          +7

          Есть очень простое правило: код описывает, что мы делаем, а комментарии описывают, почему мы это делаем.

          Например
          def some_method
            self.some_value = perform_some_calculations()
            save! # Если в perform_some_more_calculations() произойдёт ошибка,
                  #   то потеряются *все* результаты вычислений — а мы хотим, чтобы
                  #   в базе остались хотя бы результаты perform_some_calculations(),
                  #   чтобы потом не вычислять их ещё раз  
            self.some_other_value = perform_some_more_calculations()
            save!
          end  


          1. khajiit
            24.05.2024 13:52
            +2

            А @doc описывает, что мы собирались сделать.


  1. SergeiMinaev
    24.05.2024 13:52
    +8

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

    Документация и комментарии - разные вещи.

    Одному моему коллеге нравится применять все новые умные операторы в JavaScript, например, ellipsis

    Он появился лет 9 назад - ну совсем недавно :)


    1. flx0
      24.05.2024 13:52
      +3

      Документация и комментарии - разные вещи.

      Doxygen, cargo doc, и прочие генераторы доков с вами не согласны.


      1. SergeiMinaev
        24.05.2024 13:52

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


  1. Nikollor48
    24.05.2024 13:52
    +6

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

    Код как газ. Заполняет весь предоставленный ему объем.


  1. HardWrMan
    24.05.2024 13:52
    +1

    10 вредных привычек, которые программисты втайне обожают

    Мне не нравится ни одна из приведённых привычек. Правда, я и не настоящий программист, так что всё ОК.


  1. Dolios
    24.05.2024 13:52
    +13

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

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


    1. ManGegenMann
      24.05.2024 13:52
      +1

      Два чая. Обычно что он делает ясно из имени, кода, наименования переменных и параметров, но вот зачем тайна.


  1. TrueRomanus
    24.05.2024 13:52
    +1

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

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


  1. waxtah
    24.05.2024 13:52
    +1

    Я бы все перевел к более простой модели: при написании кода нужно соблюдать баланс между читаемостью / разумной утилизацией ресурсов / скоростью разработке. Ну и опыт разработчика помогает найти компромисс.


    1. duke_alba
      24.05.2024 13:52

      Ещё более короткую формулировку я увидел, когда пришёл на свою первую работу. Над входом висел лозунг: "Думай всегда головой" :-) Стараюсь придерживаться.


  1. andy_p
    24.05.2024 13:52
    +1

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

    Ну это смотря в какой стране они живут.


    1. tolich_the_shadow
      24.05.2024 13:52
      +2

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


      1. geher
        24.05.2024 13:52
        +1

        Даже на дороге с односторонним движением лучше посмотреть в обе стороны, причем лучше постоянно посматривать в обе стороны (для дороги с двусторонним движением это тоже правильно).

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


  1. Panzerschrek
    24.05.2024 13:52
    +1

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


    1. VanKrock
      24.05.2024 13:52

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


  1. NutsUnderline
    24.05.2024 13:52
    +4

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

    Аналогично и с версиями ОС, хотя это ситуация посложнее, в силу большого разнообразия


    1. Alexey2005
      24.05.2024 13:52
      +1

      Тут просто работает принцип "чужие ресурсы не жалко". Когда за железо платит пользователь, у разработчиков попросту нет мотивации что-то там оптимизировать: работает - и ладно.

      А вот когда за вычислительные ресурсы платит сам разработчик (точнее, заказчик проекта) - вот тут-то всё делают как надо.

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


  1. Wohlstand
    24.05.2024 13:52

    Код без комментариев

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