Каждый код уникален. Несмотря на работу линтеров, спустя несколько лет вы с уверенностью сможете определить, что писали вы, а что — другой разработчик. Даже если не помните, что это была за задача. А ещё, код может рассказать об авторе едва ли не больше, чем разговор с ним. Например, какие книги он читал, на каких языках писал раньше. Можно сделать выводы о характере и привычках автора и предположить, как быстро он сможет вырасти.
Я Юрий Митус, фронтенд-разработчик в Сбере. Предлагаю поговорить о коде, который мы пишем, и практиках его улучшения. Расскажу, на что обращать внимание, покажу типичные ошибки, которые «портят» код и как их избегать. Научу писать код так, чтобы вас хотели нанять и перенять ваши практики.
Я занимаюсь веб-разработкой с 2011 года. На каком бы языке ни писал, я всегда чувствовал, что могу лучше. Есть тонкая грань между хорошо и быстро и множество книг о том, как писать красиво. Но проблема в том, что найти красивый код в реальных проектах невероятно сложно. Считается, что только на одном Github больше 370 миллионов репозиториев.
Возьмём для примера Code Climate и проанализируем репозиторий VScode. Получаем отчёт о 14-ти годах технического долга.
Кажется, что VScode всегда был с нами. Но он появился только в 2015 году. И это лишь один из многих репозиториев, принадлежащих опытным специалистам, которые на собеседованиях задают вопросы о красно-чёрных деревьях.
А представляете, сколько технического долга соберётся на всём Гитхабе и других ресурсах? Это проблема, для которой пока нет реальных решений. Если в будущем придут роботы и всё перепишут, код не станет сильно лучше. Я хочу читать и писать красивый код и получать удовольствие от работы.
Я поделил статью на четыре части. Первая — лирическая. В ней разберём, что роднит разработчиков с писателями и какие выводы для нашей работы из этого следуют. Вторая часть — полезная. Здесь поговорим, зачем писать красивый код и как его улучшить. Следующая часть — терапевтическая. Она о том, как не потеряться в море информации, и какие практики использовать на пути к улучшению кода. В самом конце будет небольшой план с выводами.
Лирическая часть. Писательство и программирование
В старшей школе я мечтал стать писателем, а стал программистом. Казалось бы, совершенно разные профессии, но если вникнуть, они не особо отличаются. И мы, и они пишем, каждый в индивидуальном стиле.
Благодаря стилю даже через много лет вы сможете отличить свой код от чужого. Как по качеству литературного произведения можно судить о том, что за писатель его написал, так и по коду понятно, что это был за программист. Кто-то пишет более архаично или более современно, но каждый уникально и по-своему.
У меня есть классификация программистов по стилю кода:
Ретрограды. Они всё ещё используют
var
и знают, чтоfor
— самый быстрый.
Адепты ООП любят
self
иthat
и считают, что точно знают всё в программировании.
Аккуратисты-писатели. Эта категория разработчиков пишет не код, а книги, и любит примеры прямо в комментариях, которых существенно больше, чем кода.
Модернисты используют самые новые фишки в языке, но не всегда понимают, для чего они нужны.
Минималисты-изобретатели. Они как будто говорят своим кодом: «Смотри, как я умею!». И его действительно интересно читать, но зачастую совершенно непонятно.
У этой классификации есть небольшое «но». Это, конечно, Stack Overflow. Возможно, вы сталкивались с ситуацией: читаете код, всё примерно одинаково, но внезапно встречается невероятная функция или зубодробительный oneliner. Иногда ещё добавляются комментарии на английском. В таком случае разговор о стиле кода становится неуместным. Сначала стиль программиста может казаться пёстрым одеялом из разорванных кусочков, но он быстро формируется и становится узнаваемым.
Из чего формируется стиль кода
Стиль, используемый программистом, формируется из нескольких характеристик. То, как он называет переменные, управляет циклами, именует функции, использует ли точки с запятой, оставляет ли комментарии и так далее.
Переменные
Работая с фильтром или reduce
, вы, вероятно, даёте имена параметрам. Кто-то предпочитает использовать «A» как универсальное название переменной, или «Item», или «AR». А кто-то более оригинальный, и тогда в переменной мы видим целую историю:
Циклы
Есть программисты, предпочитающие использовать циклы типа for
, кому-то ближе forEach
, map reduce
, а некоторые вообще используют рекурсию вместо циклов:
Названия функций
Все из вас передавали функцию А, В, С, Item или I. В JavaScript вообще пять способов заданий функций, и у каждого программиста свой любимый. В этом контексте вы наверняка сталкивались с кодом, который вызывает желание закрыть вкладку или переписать сразу же, а может и вовсе перестать общаться с его автором.
А бывает наоборот: вы решаете задачу на Codewars, пишете страницу кода, а потом выясняете, что эту задачу можно было уместить в одну строку. И хочется так же.
Менять стиль больно
Сформированный стиль трудно менять. Например, разработчик переходит в соседнюю команду или компанию и выясняется, что там принято ставить точки с запятой. Если раньше он писал по-другому, то раз за разом на каждом peer-review придётся страдать.
Полезная часть: зачем писать красивый код
Код, который вы пишете — ваше отражение. По нему вас будут оценивать как программиста и как человека. И поскольку мы все немного писатели в мире программирования, важно думать о тех, кто будет читать ваш код, и о том, смогут ли они его понять.
Процитирую известного человека: «Любой дурак сможет написать код, который поймёт машина. Хорошие программисты пишут код, который поймёт человек». И ведь действительно, современная IDE будет орать на вас до тех пор, пока код не запустится. Но если запустится, то будет ли он хорошим?
Также в этой фразе есть ответ на вопрос, зачем писать красивый код. Компьютеру, по большому счёту, всё равно, красиво вы написали или нет. Он этот код интерпретирует и перепишет. Продукт-менеджеру важно, чтобы всё быстро заработало. А конечному пользователю до деталей реализации вообще нет никакого дела. Тогда зачем?
Писать красивый код нужно, чтобы вас понимали, и вы сами, спустя время, понимали, зачем это написали. Это может звучать банально, но если в процессе создания кода вы вспомните, что им будет пользоваться кто-то, кроме вас, то вы уже сделаете его лучше.
Во-вторых, писать красивый код нужно для себя. С вероятностью в 20 % вы вернётесь к этому фрагменту и будете его дорабатывать. А кроме того, не знаю, как вам, а мне нравится возвращаться к ранее написанному коду и видеть, что я молодец. И если кто-то скажет: «А так можно было?», то вовсе приятно, что не зря старался.
Красивый и некрасивый код
Какой код хороший, а какой красивый, и есть ли разница? Давайте посмотрим на пример. Есть функция, которая перемешивает массив. Вроде написано неплохо, но красивой я бы её не назвал.
А вот эта же функция. Код красивый, но слишком усложнён. Это писал точно не новичок, он знает, как работает map reduce. Но если приглядеться, мы видим, что здесь вложенный map reduce
, ещё один map
и spread
-оператор. Вроде бы красиво, но работать будет медленно.
Что мы можем сказать о человеке, который написал так? Здесь то же самое, но сделано изящнее и работать будет быстрее.
Сложный код
Красив ли сложный код? Сосредоточимся на самых простых, интуитивно понятных метриках.
Ниже пример. Не пытайтесь в него вникать. Здесь сразу видно, что происходит много всего. И кажется, что код сложный, а значит некрасивый. Можно подумать что автор кода не заботился о нас, ему было плевать, что мы сюда полезем и попытаемся разобраться. Вряд ли мы станем хорошо относиться к нему как к специалисту.
Понятная метрика: если код простой, то он более красивый и более-менее линейный, его легко читать. А если вот такой, то сразу видно, что код не очень.
Эти примеры взяты не с govnokod.ru, а из того же репозитория VS-кода.
Красивый код
Я считаю, что красивый код — несложный, и его приятно читать. Но должен ли он быть оптимальным? Уверен, что нет, потому что оптимизация, особенно преждевременная, может сделать ваш код совершенно нечитаемым. И тогда он не будет красивым.
Процитирую Мартина Голдинга: «Всегда пишите код так, будто сопровождать его будет склонный к насилию психопат, который знает, где вы живёте». Советую всегда держать эту мысль в голове.
Несколько советов, как сделать код лучше
Одна функция — одно действие. Суперуниверсальная функция, которая может всё — это здорово, но в теории. А если она сломается, вы так думать уже не будете.
Чем меньше кода, тем лучше. Так мучительно лениво разносить код по модулям, но подумайте о моменте, когда вам нужно будет срочно что-то исправить, и вы скажете себе спасибо.
-
Неопределённость — источник проблем. Проблемы возникают, когда мы используем не до конца изученные инструменты, библиотеки или функции. Лучше потратить несколько минут, почитать документацию, чем потом опасаться, что в ответственный момент это выстрелит не лучшим образом.
То же самое про функцию. Если вы не знаете, что она возвращает, и как ведёт себя в разных ситуациях, всё развалится в неподходящий момент.
Понятные названия. Сегодня, завтра и, возможно, на следующей неделе вы будете помнить, что
arr
— этоcards
. Но через месяц вам придётся перелопатить весь код, чтобы понять это. А можно было просто дать название переменной.
Между декларативным и императивными стилями выбирайте декларативный. Напомню, что императивный стиль — это когда вы сообщаете компьютеру последовательность команд, необходимых для достижения цели. А декларативный — когда вы говорите, чего хотите добиться, не вдаваясь в детали реализации.
Важно не смешивать все стили, потому что получается ерунда.
ESLint-правила. Когда понимаете, что плохо именуете функции или переменные, идите в правила в ESLint. Если пишете слишком большие функции — снова правила в ESLint. Прежде чем что-то закоммитить, опять же — ESLint и тесты. Не знаете, как писать правила? Сейчас расскажу.
Пишем свои правила
Написать правило можно за пять шагов. Допустим, вы поняли, что изменяемые переменные — пережиток прошлого. Соответствующим образом называете папку, добавляете конфиг с настройками. В index.js будет само тело правила. Добавляете в проект одним из двух способов, в зависимости от того, NPM у вас или Yarn. И добавляете в ESLint конфиг.
Ниже само правило. Оно простое. Обычно правила немного сложнее, но тут главное — начать и подобрать правила под себя, потому что универсального решения не бывает.
Тесты
Не буду настаивать на том, чтобы вы учились писать тесты, но расскажу, зачем это делать. Возможно, вы слышали фразу: «Ваши тесты ничего не проверяют», и, во многом, это правда. Тесты — тоже код, громоздкий и некрасивый, и, значит, в нём легко совершить ошибку. И программа с ошибкой в тестах становится ещё более опасной, чем код без тестов.
Их нужно писать. Как и в любой другой области, практика делает мастера. Тесты помогают взглянуть на функцию со стороны. Если ваш код сложно протестировать, это явный сигнал, что с функцией проблемы и надо пересмотреть подход.
И безопасность, конечно. Тесты помогают выявить ошибки в коде и предупредить, что он больше не работает должным образом. Так можно остановиться и исправить проблему, пока это ещё дёшево.
Важно не оказаться в плену заблуждений
Но ведь это работает, можете возразить. Это же MVP, давайте выпустим его, а потом всё перепишем. Но это неудачная стратегия. Скорее всего, вы ничего не перепишете. И сами это понимаете. Самая долгая часть разработки — не написание кода, а поиск ошибок и их исправление. Поэтому лучше сразу писать код хорошо, по крайней мере, насколько это возможно.
Раньше я сам не задумывался о том, как пишу. Работало и ладно. У меня был старший коллега, который, увидев мой код, сказал: «Юра, а ты знаешь про такой язык, как Питон? Там отступы — это часть синтаксиса, там ты так не напишешь». И я подумал, вот это язык, на нём же нельзя быдлокодить. Но вскоре стал писать на Python и понял, что дело вообще не в языке.
Подытожу: делайте хорошо для себя, больше это никому не нужно. Используйте правила, хотя бы базовые, но потом можно добавить и другие.
Терапевтическая часть: как меняться
Здесь поговорим о том, как не потеряться в море информации и какие практики я рекомендую использовать.
Технологии вокруг нас — это огромный поток информации. Мы встречаем множество страшных аббревиатур: SOLID, KISS, YAGNI, DRY, BDUF и многие другие. Особенно начинающему программисту может стать сложно и непонятно, куда бежать и что делать. Но не переживайте, я принёс вам свет. И, как вы могли догадаться, это функциональное программирование. Возможно, вы подумали: «Очередной зануда, который будет твердить о функциональном программировании. Я уже читал книги по этой теме, и они не сработали». Что я могу на это сказать? Во-первых, вы читаете эту статью достаточно долго, чтобы дать мне шанс. Во-вторых, я не буду обучать вас функциональному программированию. Но если вы прочли эти книги, то вы уже на верном пути.
Берите лучшие практики
Я провёл более сотни собеседований с фронтенд-разработчиками, и на каждом из них спрашивал, что такое «чистая функция». Большинство честно признавались, что не знают. Но были и те, которые рассказывали, что чистая функция — это функция без параметров.
Работает ли функциональное программирование во фронтенде? Фактически мы используем чистые функции, каррирование, функции высшего порядка, изменяемость и ссылочную прозрачность. Большинство из этих концепций можно понять с помощью простой логики.
Допустим, вы меня послушали и стали писать вот так:
Это интересный подход, но будьте готовы: за это могут побить коллеги. Чтобы полноценно писать на ФП, его должны понимать не только вы, но и те, кто вас проверяет и сопровождает в проекте. А если переборщить, то код может стать совершенно нечитаемым, а значит, результата вы не добились.
Меняйтесь постепенно. Ниже четыре варианта, как могут проходить ваши изменения: переход от первого к последнему должен быть мягким.
Разделите ответственность. Ниже пример маленькой функции, всего 12 строчек, но здесь очень много происходит: данные достаются из формы, проверяются и отправляются на сервер.
Вполне логично написать три:
Отдельно логин.
Отдельно запрос.
Отдельно извлечение данных из формы.
Давайте посмотрим, как было и как стало.
Теперь удобочитаемо, но если хотите добиться такого в своём коде, готовьтесь пройти долгий и сложный путь.
Другие полезные советы:
Нормально называйте переменные.
Начните писать тесты.
Где вы писали одну функцию, напишите три.
Проверьте, что ваши тернарники понимаете не только вы.
Попробуйте переписать ваши функции и сделать из них чистые.
Скорее всего, вы всё это уже слышали раньше, но код от этого не стал красивее. Комбинация хороших практик, ESLint-правил и тестов не решает проблему, ведь, чтобы начать писать красиво, нужно менять не код, а, в первую очередь, себя.
Меняйте себя
Здесь нам поможет профессор Би Джей Фогг из Стэнфорда, психолог и эксперт в области поведенческих наук, а также автор метода маленьких шагов (ММШ) или «Tiny Habits». Идея в том, что начать новую жизнь с понедельника почти невозможно. Нужно менять поведение постепенно. Начать с простого и постепенно увеличивать сложность или объём действий.
Например, вы меня послушали, переписали один модуль, второй, переключили все ваши ворнинги на эрроры. Надолго ли вас хватит? Думаю, дня на три. Потом навалятся задачи, проблемы, вы переключите всё обратно, и на этом закончится.
Доктор Фогг предлагает ставить маленькие, легко достижимые задачи и вплетать их в рутину. Таким образом вырабатываются правильные привычки, в нашем случае — красивый код. И только так это работает.
Допустим, вы ставите задачу: нормально именовать переменную или писать маленькие функции, добавлять правила в ESLint. И каждый раз, когда приносите за свой стол чашку чая, пять минут правите свой код. Легко и просто. Через какое-то время вы свой код не узнаете, и начнётся новая жизнь. Вы подниметесь на ступенечку вверх.
Подведём итоги
Все мы немножко писатели. И код, который мы пишем, читают другие, а значит, важно думать о читателе. И делать хорошо для себя. Это нужно в первую очередь вам.
Формируйте правильные привычки. Это требует времени, концентрации внимания и усилий, но результат того стоит.
Берите лучшие практики. Необязательно ФП, но если вы знаете, что для чего использовать, то вы уже более ценный программист, чем тот, кто раз за разом копирует код, не понимая, как он работает.
Меняйте себя в первую очередь, потому что иначе ничего не выйдет.
План действий: лучшая версия себя
Читайте код. Чем больше вы читаете, тем грамотнее и лучше вы пишете и говорите. С кодом это тоже работает.
Пишите код. Практикуйтесь.
Снова читайте код, потому что нельзя останавливаться.
Решайте задачи. Вы видите алгоритмы и, как правило, видите красивый код.
Пишите тесты, если ещё не начали.
Задумайтесь о рефакторинге.
Комментарии (9)
ivlevAstef
09.04.2024 11:35+6Я побуду занудой, но последний пример shuffle функции, является не валидным. На многих языках он не приведет к печальным последствиям, но на С++ похожий код может привести к крашу.
А все почему? Так-как нарушено правило: если a > b, то b < a. Ну и а == а, и транзитивность, и...
На всякий открыл какую-то документацию по JavaScript и нашел там целый список правил:
Stable: The comparator returns the same result with the same pair of input.
Reflexive: compareFn(a, a) === 0.
Anti-symmetric: compareFn(a, b) and compareFn(b, a) must both be 0 or have opposite signs.
Transitive: If compareFn(a, b) and compareFn(b, c) are both positive, zero, or negative, then compareFn(a, c) has the same positivity as the previous two.
JavaScript в общем не исключение из правил.
Код, может и красиво выглядит, но очень рискованный, и использовать его в продукте, я бы 100 раз подумал, стоит ли оно того.
И опять же - а можно не валидный код называть красивым? Но это больше философский вопрос. Квадратный корень в квейке считается не валидно, а приближенно, но многие пример назовут красивым.
zagayevskiy
09.04.2024 11:35+3С учётом того, что шаффл делается одним проходом по списку, код вообще дно. Как и статья в целом. Хабр уже совсем не торт.
werevolff
09.04.2024 11:35+1код вообще дно. Как и статья в целом
Это вы ещë, уважаемый, мальчиков с бэкграундом Сбера не собеседовали.
zagayevskiy
09.04.2024 11:35Я – собеседовал:) Выборка небольшая, но мне попадались очень неплохие ребята. Не надо всех под одну гребёнку мести, Сбер сколько лет пылесосит рынок.
werevolff
09.04.2024 11:35+1Я так понимаю, у Сбера есть различные проекты в области IT, и на самые массовые, с точки зрения команд, они активно набирают джунов и студентов. По крайней мере, один мой знакомый "вкатившийся" коллега начинал со Сбера. И это начало, как я понял, было не слишком приятным и успешным с точки зрения опыта.
Поэтому, после очередной волны сокращений, либо, волны увольнений по собственному, Сбер стабильно бомбардирует рынок молодым мясом. Ну и от коллег я слышал не раз, как им доводилось с этими "воспитанниками сбера" общаться на собеседованиях. И, в большинстве случаев, я слышал именно негативные оценки людям, вышедшим с трамплинных проектов Сбера. Не спорю, что у банка есть много иных проектов, в которых задействованы прекрасные специалисты, но ситуация здесь напоминает проблему шотландца, который не может понять, почему его не называют "Мак-Фларен, Строитель Мельниц"?
kozlov_de
09.04.2024 11:35+1IsValidLogin
Не надо так писать: подсказок пользователю ноль. Не валидный и всё тут
Write ones, read many
Как минимум, код придется читать один раз на ревью. И потом при рефакторинге. И при блохоловстве.
У нас read/write в среднем 3, а не ваши 1.2 в статье
danilovmy
09.04.2024 11:35Мне понравилось, спасибо!
Вот только "С вероятностью в 20 % вы вернётесь к этому фрагменту и будете его дорабатывать" - а можно узнать как считалась вероятность? A то тут Мартин Фаулерс беспокоится, он то в статьях о рефакторинге совсем другие величины указал.
kellas
09.04.2024 11:35Уверен что если 10 произвольным программистам, показать одну произвольную кодовую базу , каждый скажет что там 90% надо переделать и только вот эти 10% хорошо написаны, и у каждого это будут свои 10%
Нам нравятся разные писатели, разные фильмы, не просто разные стили, но и разные смыслы, разные логические приемы, это особенности личного восприятия основанные на индивидуальном опыте. Нет и не будет общепризнанного красивого кода, всем понятного кода итп
Но есть характеристики которые можно измерить, и это не субъективные "читабельность" и "поддерживаемость" которые только падают из-за снижения порога входа в айти и новые менее умные люди хуже умеют в чтение и поддержку, а такие как время исполнения, количество слов (которые нужно напечатать и прочитать), вес файлов, количество потребляемой памяти , отказоустойчивость, итп
Я это всё к тому, что так или иначе, такое внимание к форме, негативно скажется на содержании, давайте сконцентрируемся на более важных аспектах разработки
Drucocu
Вы рассуждаете о качестве кода в общем, но все ваши примеры о конкретном языке. Притом ни указания на язык в тексте (кроме того, что Юрий Митус - фронтенд-разработчик), ни в тегах статьи нет.
Не надо так: проставьте, пожалуйста, релевантные теги и напишите хоть одно предложение о том, о каком языке пойдёт речь.