Всем привет! Меня зовут Соня Гусева, я фронтенд-разработчик в Яндекс Практикуме (или фронтенд-капибара). Вместе с командой мы развиваем платформу practicum.yandex.ru. Например, сделали поиск по пройденным материалам — тот самый, где «найдётся всё». И тёмную тему — для комфортной учёбы даже ночью. Практикум появился в 2019 году и с тех пор растёт. В какой-то момент нам стало сложно погружать новичков в проект. Дело в том, что стиль кода всё время развивался, но правила оставались на уровне устных договорённостей. В итоге приходили новые тиммейты, видели легаси и более свежий код — и не понимали, какой написан правильно и почему они разные. Как следствие, код-ревью растягивалось, и тестирование проходило в разы сложнее. Люди чувствовали себя неуютно в таких процессах.  Нас спас единый гайд по стилю кода — документ, в котором мы закрепили правила по оформлению кода для всей команды. В этой статье я расскажу, от каких проблем вас сможет защитить такой гайд, и приведу подробную инструкцию, как его внедрить.  Если вы любите больше смотреть, а не читать, с этой темой я выступила на Frontend meetup, где рассказала подробно о внедрении стиля кода в проекты.  Представьте новичка, который видит ваш код в первый раз. Для погружения он получает маленькую задачу, с которой по идее должен справиться быстро. Но в процессе видит разные по стилю части кода, останавливается и пытается понять, как именно следует писать.  Например, в одном месте видит типы, а в другом — интерфейсы: Или видит иногда padding с использованием переменной spacing, а в других случаях — без неё:  Чтобы разобраться, как писать правильно, он будет либо каждый раз ходить к коллегам с вопросами, либо попробует угадать сам — и тогда о своей ошибке узнает на код-ревью. В обоих случаях на коллег ляжет нагрузка — все накопленные знания нужно будет передавать устно по крупицам. В итоге  онбординг будет проходить тяжелее и дольше, чем мог бы. И для нашего новичка, и для команды. Когда в Практикуме появился гайд, мы стали ссылаться на конкретные пункты. Теперь новички не пытаются угадать, как правильно. А опытным сотрудникам не нужно объяснять одно и то же каждому новому коллеге. Всем стало проще. Когда более опытный сотрудник проверяет код, чаще всего комментарии касаются именно стиля, а не ошибок. В итоге код-ревью становится похоже на картинку справа.  Комментарии по стилю тянут за собой долгие обсуждения предложений. Вместо того чтобы сфокусироваться на архитектуре проекта или обработке ошибок, сотрудники больше спорят о том, «как надо» оформлять код, — и разработка затягивается. Гайд сокращает количество споров, потому что каждое правило в нём проходит согласования с командой. Если правило зафиксировано, значит, вы по конкретному вопросу договорились и спорить об этом уже нет смысла. Можно фокусироваться на важном. С гайдом проверку стиля кода можно автоматизировать. И это хорошо, ведь правил обычно довольно много, запомнить их смогут в лучшем случае 3—5 человек.  Классическая пирамида тестирования, которую многие знают, игнорирует стилистику кода. Но несколько лет назад Кент Дотс предложил другой тип визуализации — кубок (или трофей).  Самое простое тестирование в кубке — статическое, его цель — подсвечивать опечатки и ошибки ввода по мере написания кода, в том числе стилистические. Проводить статическое тестирование можно с помощью анализатора кода ESLint или инструмента для линтинга стилей Stylelint. Дальше я расскажу, как их настроить и всё автоматизировать.   Самый простой способ — добавить в проект готовый конфиг и адаптировать его под свой проект. Есть два таких конфига — Google Style Guide и Airbnb Style Guide. Вы можете просмотреть набор правил, которые они содержат, и выбрать более подходящий лично вам.  Разумеется, не все правила вам могут подойти, а некоторых, которые вам нужны, в готовом решении не будет. Поэтому на старте вы можете отключить или изменить те правила, которые вам изначально не подходят, а дальше по мере работы добавлять свои.  В Практикуме мы в итоге остановились на Airbnb Style Guide, поэтому именно про него я и расскажу.  Вероятно, после первого прогона кода линтеры выявят множество ошибок. И вы начнёте обсуждать, какие правила оставите, какие отмените, какие измените. Когда ошибок 20–50, их можно быстро поправить вручную. Но если несколько тысяч, это уже совсем другая история. Что делать, если первый прогон выдал 1 000+ ошибок: Первый вариант — настроить прогон линтеров не на все файлы, а на изменённые в пул-реквесте, так при изменении файла появляются старые ошибки.  У этого варианта есть большой плюс — вы быстро исправите старый код. Но он тянет за собой другую проблему: если вы поменяете или добавите одну строку кода в каком-либо файле, то на вас свалится большое количество ошибок.  Мы у себя включили в пул-реквесте прохождение линтеров в ci, поэтому,  пока разработчик не поправит всё, он не сможет влить свой код в репозиторий. Однако это не очень удобно, потому что поначалу придётся больше времени тратить на исправление старого кода, чем на доставление пользователям какой-либо фичи. Второй вариант — создать скрипт, расставляющий в коде команду eslint-disable-next-line (отключение проверок отдельных строк). Преимущества этого подхода в том, что не нужно исправлять старый код при пул-реквесте. Разработчики могут сами решать, когда они исправят ту или иную часть кода. Ниже подробнее поговорим о втором варианте решения проблемы. Первым делом инициализируем eslint, прогоняем через lintFiles по всему проекту и записываем результаты в переменную results. Вот что у нас оказалось в переменной results: В этой переменной содержится массив таких объектов, и из всего этого нужно только: filePath — путь до файла, messages — массив сообщений, где мы смотрим на строки ruleId (какое правило нарушено) и line (номер строки, где была ошибка). Просматриваем все результаты поиска ошибок, а если в файле не нашлось ошибок, то переходим к следующему файлу: Если же ошибки нашлись, то проходимся по уникальным строкам, создаём массив сообщений и join'им через запятую с пробелом. Так же тут есть переменная index — она указывает, какое количество комментариев уже вставили, чтобы правильно вставить следующий.  Создаём комментарий eslint-disable-next-line, туда вставляем строку из правил и с помощью функции splice вставляем комментарий в файл: Какие в описанном способе могут появиться подводные камни: Комментарии в языке JSX пишутся в формате {/* {правило} */}. Тут надо учитывать, что если линтер среагировал на атрибуты, то комментарий будет обычным — и код не будет автоматически исправляться по обычному правилу.  При отключении или изменении правила надо не забыть удалить из кода комментарии, которые потеряли актуальность, чтобы не копился техдолг. Давайте посмотрим, как можно решить эти проблемы. Как и в предыдущем случае, создаём функцию eslint, результаты проверки линтерами записываем в переменную results и проходимся по результатам: Если в сообщении встречается ошибка jsx-no-comment-textnodes, меняем на комментарии нужного нам вида. Создаём массив ошибок, который нужно удалить. Находим в файле лишние комментарии, удаляем их и записываем в файл. Итак, Airbnb Style Guide подключён и настроен. Но нередко бывает так, что инструменты не покрывают все случаи вашего кода. В таком случае нужно создавать собственные правила в дополнение к плагинам.  Если в готовом гайде нет правил, которые вам нужны, создать свои их можно с помощью анализатора кода ESLint или инструмента для линтинга стилей Stylelint. Я расскажу про оба. Важно отметить, что ESLint использует абстрактное синтаксическое дерево (AST). Увидеть его позволяет сайт astexplorer.net, на нём можно вставить свой код — и посмотреть AST.  Сначала покажу, как в ESLint написать простое правило, а затем более сложное. Первый вариант мы напишем в конфиге ESLint, второй — с помощью отдельного плагина. Такое правило можно написать в конфиге ESLint. Для этого понадобится функция no-restricted-syntax. Сначала пишем тип ошибки, дальше в selector само правило, а в message — текст ошибки. Для примера возьмём правило, когда в файле компонента есть несколько интерфейсов и где-то внизу компонент.  Рассмотрим два примера кода. На мой взгляд, лучше написать один интерфейс пропсов, а не два. Во-первых, так проще читать код. Во-вторых, некоторые интерфейсы могут использоваться для блока внутри элемента.     Перейдём к написанию самого правила: Чтобы увидеть AST, я вставила свой код на сайт astexplorer.net, о котором писала в начале пункта. Сайт предлагает для интерфейсов тип TSInterfaceDeclaration, его можно взять для правила. В самом правиле так и напишем: если с одним интерфейсом такого типа встретился другой такого же типа → сообщи об ошибке. Если вы захотите изучить тему глубже, можете заглянуть на сайт no-restricted-syntax.  Чтобы создать такое правило, сначала нам нужно создать плагин, потом записать в него правило, а потом уже настраивать автофикс.  У вас есть папка для плагина, в ней есть package.json, файл index.js и папка rules, где будут храниться все кастомные правила. Файл package.json для плагина выглядит стандартно: В файле index.js есть функция getDirFiles, которая получает все названия файлов в виде массива. Дальше экспортируем объект правил, где ключ — это название файла, значение — содержимое правила. В package.json проекта записываете название вашего плагина и путь к нему, устанавливаете его: В eslintrc.js: пишете название плагина и дальше, если у вас есть готовое правило, можете поместить его туда и использовать, как вам нужно. Код будет выглядеть так: Сначала пишем метаинформацию (тип, описание и правило с автофиксом или без), далее идёт функция create. В качестве примера напишем правило, требующее экспортировать интерфейс пропсов (один из пунктов нашего гайда). Здесь используем тип TSInterfaceDeclaration. Если тип родительского элемента не ExportNamedDeclaration, тогда с помощью context.report сообщим об ошибке. В context.report есть функция fix с параметром fixer. У него есть несколько методов: «вставить перед», «вставить после», «удалить», «заменить». Мы будем использовать insertTextBeforeRange, для этого определяем позицию интерфейса и вставляем экспорт. Как проверить, что линтер отрабатывает нормально и ничего не ломает? В ESLint есть возможность писать автотесты. Для этого нам потребуется функция rule tester. В коде указываем название правила, его содержимое и текст, сообщающий об ошибке. В rule tester нужно указать парсер для TypeScript. Дальше с помощью run вызываем тест, указывая название правила и его содержимое. В объекте есть массив валидных и невалидных тест-кейсов. В нашем случае валидные — это export interface ComponentProps, невалидные записываются в такой же массив, только без экспорта. Ещё нужно указать, что должно произойти после автофикса. Запускаем автотесты через node, указывая путь до файла. Можно сделать отдельный файл, который перебирает все остальные тест-файлы. Итак, это всё, что нужно знать про кастомные правила в ESLint, чтобы начать их писать. А если вы хотите углубиться в тему, можете почитать больше о селекторах.  Плагин в Stylelint создаётся примерно так же, как и в ESLint, поэтому сразу перейдём к написанию собственно правила.  В примере выше margin и padding в одном файле записаны через переменные, в другом margin через переменную, а padding — уже числом, а в третьем файле есть и первый, и второй вариант. Такое странно отдавать на код-ревью, поэтому напишем правило.  В коде, приведённом выше, сначала записываем название правила в переменную в ruleName, потом создаём правило и в нём указываем, что нужно проверить параметры, которые ввёл пользователь в конфиге. Утилиты находятся в самом Stylelint. Затем с помощью walkDecls мы перемещаемся по строкам в CSS. В переменной decl CSS отдаётся по строкам. Для удобства я создала переменные key и value. Указываем условия, где margin написан через spacing, padding не через переменную. Если всё ок, идём к следующей строке. Если нет, тогда для padding через регулярное выражение присваиваем новое значение в пикселях. Для margin можем сообщить об ошибке через utils.report. Сообщения создаются через utils.ruleMessages. Туда передаём названия правила и текст ошибки. Всё, правило готово.  Итак, по этим инструкциям вы можете дополнять свой гайд каждый раз, как появляется повод для нового правила. Рекомендую не относиться к документу как к готовому и законченному раз и навсегда, а регулярно выносить на обсуждения какие-то изменения и обновлять его. Хоть договориться и не всегда просто. Я пришла в Практикум стажёром, и при онбординге мне давали небольшие задачи. Пытаясь найти похожие части кода в проекте, я сталкивалась с ситуацией, о которой рассказала в самом начале, — код был написан в разном стиле, я не понимала, где правильный. К счастью, к ментору я могла подойти с любым вопросом, и он подсказывал, как нужно написать в моём случае. При ревью моего кода сотрудники приносили большое количество комментариев, в основном по стилю кода, и было не очень понятно, почему они требуют писать так, а не иначе. Но споры были ни к чему, приходилось поправлять файлы. Когда я сама начала ревьюить пул-реквесты и писала похожие комментарии, опять возникало много обсуждений и споров. Мы постепенно приходили к решению, что эту ситуацию нужно менять, так не может продолжаться вечно. В один из вечеров мы с тимлидом написали наш стайлгайд. За основу взяли наработки одного из стажёров Практикума. Когда всё было готово, вынесли на общее обсуждение и согласовали. Получилось что-то такое. В преамбуле к документу инструкция, как вносятся изменения и новые правила. Теперь регулярно пересматриваем и меняем правила, как нам нужно. Наличие стиля кода в проекте значительно облегчает разработчикам жизнь. Так будет легче проводить онбординг, код-ревью будет проходить быстрее, проверку стиля можно будет автоматизировать. При внедрении в проект гайда по стилю пользуйтесь линтерами. Они сильно облегчают работу, о многих вещах можно не думать. В линтеры можно добавить свои кастомные правила. Так ваш стиль кода будет уникальным, подходящим только для вашего проекта. P.S. Не занимайтесь на код-ревью стилем, занимайтесь самим кодом 🖤