У программистов очень странные отношения с правилами. С одной стороны, программный код — это не что иное, как огромный свод правил — правил, которые бесконечно применяются покорными кремниевыми вратами без страха и упрёка и почти всегда без сбоев из-за альфа-частиц. И да, мы хотим, чтобы транзисторы безукоризненно выполняли эти правила.
Но есть и другой, не столь сакральный уровень. В отличие от законов для ПО, правила, которые мы создаём для себя, довольно гибкие. Некоторые из них сформулированы скорее из соображений стиля, другие же делают кипу строптивого кода системной. Это набор правил, которые применяются к нашим делам и поступкам, а не к работе оборудования.
Вопрос вот в чём: могут ли люди нарушать собственные правила? Есть ли у нас право на лету менять эти правила? Может быть, те или иные правила — это выходцы из другой эпохи. Может быть, некоторые правила с самого начала были сырыми. Может, кое-какие казались в своё время очень классными. Может, некоторые правила на самом деле — не правила, а привычки.
В этой статье хочу рассказать вам про десять привычек программирования, настолько вредных, что иногда они оказываются полезными.
Код без комментариев
Все знают, что разбираться в незадокументированном коде и заниматься его отладкой — это кошмар на улице Вязов. На уроках программирования нас учат, что писать понятные комментарии очень важно. Грамотное программирование — стиль программирования, объединяющий код и человеческий язык, — придумал Дональд Кнут, возможно, величайший из всех программистов на Земле. Кто мы такие, чтобы спорить с этим авторитетом?
Но горькая правда состоит в том, что иногда комментарии всё только портят. Порой документация не имеет ничего общего с кодом.
Может, команда, ответственная за документацию, расположена далеко от команды разработчиков. Буквально на другой планете. Может, авторы кода выкатили критически важное обновление, но забыли сказать об этом техписам, или техписы в курсе, но у них ещё руки не дошли обновить документацию. Иногда разработчики не обновляют даже общий комментарий про метод, который они поменяли. Нам остаётся только гадать.
Но есть и другие проблемы. Может, комментарий написан на иностранном языке. Может, на описание концепции уйдёт как минимум несколько абзацев, а разработчик заканчивает agile-спринт. Может, тот, кто писал комментарий, просто ошибся.
По всем этим и не только этим причинам некоторые разработчики считают, что лучше всего не перебарщивать с бесполезными комментариями, а то и вовсе обходиться без них. Вместо этого они отдают предпочтение простым, довольно коротким функциям, в которых в качестве подсказки используют длинные описательные имена переменных а-ля «горбатый регистр». Если компилятор не содержит ошибки, код должен быть наиболее точным отражением того, что делает компьютер.
Медленно работающий код
Быстрый код = простой код. Но очень быстрый код — это сложный код. Поиск золотой середины в этом вопросе — задача нетривиальная.
Так или иначе, это компромисс. В целом, нам хочется, чтобы наши программы работали быстро. Но если позднее никто не сможет разобраться в коде, такая сложность приведёт к медленной работе.
Если скорость не так уж критична, имеет смысл писать код, который работает немного медленнее, но в котором проще разобраться.
Иногда проще и медленнее — это лучше, чем суперинтеллектуально и супербыстро.
Витиеватый код
Одному моему коллеге нравится применять все новые умные операторы в JavaScript, например, ellipsis. Код получается более компактным — в его понимании он проще и лучше. Любое ревью кода в исполнении таких программистов пестрит предложениями о том, где ещё можно переписать код, чтобы вставить в него новый синтаксис.
А другие коллеги не разделяют мнение, что чем проще код, тем легче в нём разобраться.
Чтобы прочитать такой код, надо распаковать новые операторы, часть которых может использоваться совершенно по-разному. Чтобы понять, как именно использовался оператор, приходится брать паузу и погружаться в размышления — здесь недостаточно пройтись по верхам. Чтение кода становится головной болью.
Нелюбовь к сверхкомпактному коду имеет исторические предпосылки. Языки программирования, вроде APL, задуманные как очень компактные и эффективные за счёт применения собственных символов, почти исчезли. А вот другие языки, вроде Python, которые изобилуют разными скобками, продолжают набирать популярность.
Любители новейших и величайших абстракций будут и дальше вставлять новые компактные функции везде, где получится, и хвалиться своей краткостью. Они же современные и модные. А их коллеги будут и дальше вставлять в стек удобочитаемый код подлиннее: в конечном счёте такой код читать легче.
Старый добрый код
Создатели языков программирования любят придумывать умные абстракции и синтаксические структуры, с помощью которых те или иные задачи можно решить в мгновение ока. Эти языки переполнены абстракциями, и именно по этой причине мануалы к ним иногда переваливают за тысячу страниц.
Некоторые специалисты верят, что применять такие функции — это к лучшему. Дьявол кроется в деталях, а без практики навыки не отточишь. Разве нам стоит отказываться хоть от одной крупинки синтаксического сахара, описанного в этом рецепте на тысячу страниц? И всё же это правило не всегда идёт на пользу.
Чрезмерное разнообразие свойств может всё запутать.
Сейчас появилось так много умных синтаксических штук, что знать их все просто невозможно. Да и зачем? Сколько способов нужно, чтобы проверить поле на пустое значение или сделать наследование многоуровневым? Неужели только один из этих способов правильный и намного лучше остальных? Конечно, в команде всегда найдётся сотрудник, который устроит скандал по этому поводу и испортит обеденный перерыв или летучку.
По крайней мере, одна группа разработчиков языков программирования решила ограничить набор характеристик. Создатели языка Go говорят, что хотели создать язык программирования, который можно освоить очень быстро, буквально за день. То есть все разработчики в команде смогли бы читать весь код. Чем меньше свойств, тем меньше путаницы.
Код собственными руками
Эксперты по эффективности любят повторять: «Не изобретайте велосипед». Используйте готовые протестированные библиотеки. Не брезгуйте унаследованным кодом — он проверен временем.
Но иногда не стоит отказываться от нового подхода. Библиотеки чаще всего пишут для специалистов широкого профиля и распространённых сценариев применения. Они перегружены «перестраховочными» тестами, позволяющими убедиться в согласованности данных и в том, что пользователь не застопорит всю систему, если введёт не те параметры.
Но если у вас особый случай, несколько строк специализированного кода могут кардинально ускорить работу. Он не сможет делать всё, на что способна библиотека, зато то, что вам нужно, он сделает вдвое быстрее.
Конечно, тут есть свои риски. Бывает настолько сложный и эзотерический код (например, в системах шифрования), что лучше его не трогать, даже если вы более или менее понимаете, как он работает. Но когда это уместно (например, когда библиотека заметно увеличивает рабочую нагрузку), несколько удачных строк кода от себя творят чудеса.
Предварительная оптимизация
Программисты часто пишут код на скорую руку и оправдываются давним нравоучением о том, что предварительная оптимизация — это просто пустая трата времени. Они считают, что только после запуска всей системы станет понятно, какая именно часть кода окажется проблемной. Глупо тратить часы работы на идеальную функцию, если её будут вызывать только раз в год.
Простое и надёжное, как швейцарские часы, правило. Да, некоторые проекты так и не доводят до конца из-за бесконечного планирования и избыточной оптимизации. Но, с другой стороны, есть множество примеров, когда капля предусмотрительности кардинально изменила бы ситуацию.
Неправильно подобранные схемы и структуры данных порождают архитектуру, которую в дальнейшем едва ли можно будет оптимизировать.
Иногда структура разбросана по стольким частям кода, что даже грамотный рефакторинг не спасёт ситуацию. И такую проблему решила бы предварительная оптимизация.
Беззаботность
Переходя дорогу, хорошие программисты смотрят сначала налево, потом направо. Они добавляют многочисленные строки кода, которые перепроверяют данные дважды, а то и трижды. И только потом с этими данными выполняются те или иные действия. Мало ли, а вдруг сюда пробрался пустой указатель!
Увы, все меры предосторожности могут заметно снизить скорость выполнения кода.
Иногда из соображений производительности лучше в упор не видеть потенциальных рисков и просто написать код, который перепроверяет всё не слишком тщательно. Если нам нужно, чтобы код работал быстро, придётся обойтись абсолютным минимумом.
Несогласованность
Люди любят порядок, а программисты убеждены, что для всей массы кода нужно использовать одни и те же приёмы, алгоритмы и синтаксис в каждой его части. Такие старания упрощают жизнь каждому, кому придётся работать с этим кодом впоследствии.
У такой согласованности есть своя цена. Прежде всего, это время, а иногда ещё и сложность.
Чтобы устранить различия, придётся вернуться и переписать весь код, для которого применяли другие приёмы и алгоритмы. Из-за этого можно выйти далеко за рамки намеченного бюджета.
Более серьёзная проблема возникает в отношениях между разными разделами. В некоторых проектах используется унаследованный код. В других — есть зависимости от библиотек. Многие проекты не смогут работать без API, написанных совершенно другими людьми в других компаниях.
Часто сгладить различия между этими группами невозможно, а переписать весь стек в соответствии с новейшим видением можно только ограниченное число раз. Странный закоулок нашего мозга требует добиться идеального порядка, но иногда лучше заключить перемирие с несогласованностью.
Любовь к техническим новинкам
Согласованность встаёт на пути у инноваций, ведь она подразумевает строгое следование заведённому порядку вещей.
Иногда, чтобы добавить новые функции, встроить новые библиотеки или интегрировать стек с новыми API, нужно отойти от старых схем.
Да, это усложнит жизнь тем, кому при чтении кода придётся перейти на другую скорость, но такова цена прогресса. Ведь именно это делает работу программиста такой интересной.
Несоблюдение правил
Ради шутки я спросил у Google Gemini, нарушали ли программисты какие-то правила, когда создавали его. Gemini ответил:
«Я бы не сказал, что программисты нарушили те или иные правила. Скорее, они расширили границы передовых методов работы при создании больших языковых моделей, таких как я».
«Большие языковые модели обучаются на массивных объёмах данных, и в том, как модель обучается на этих данных, всегда присутствует элемент неизвестности», — сказал Gemini. — «Некоторые приёмы, используемые при создании больших языковых моделей, могут быть очень эффективными. Но трудно точно понять, как именно модель формулирует ответы».
Вот оно! LLM (с англ. large language model — большая языковая модель) лучше нас знает, что правила устаревают и меняются. Возможно, если можно загрузить датасеты для обучения, уже не нужно тратить столько времени на понимание алгоритмов. Так что идите вперёд и будьте людьми. А о правилах пусть думают LLM.
Курсы по программированию для получения новой специальности или повышения:
Топ бесплатных курсов и занятий: