Для JavaScript'а, который долгое время оставался «за бортом» большой разработки, настала золотая эра быстрого развития и появления все новых и новых технологий на его основе, а приложения становятся все комплекснее с каждым днем. Учитывая, что принятие ежегодных стандартов, появление нового синтаксического сахара и «плюшек» делают его очень привлекательным для большего числа разработчиков, данная тема будет актуальна не один год. Новички в JavaScript с энтузиазмом берутся за его изучение, пробуя все новые и новые фишки, однако в большинстве своем они забывают об оформлении кода и о такой вещи, как технический долг.
Что с кодстайлом на данный момент? Все плохо: качество кода страдает в угоду скорости разработки, и этот процесс восходит истоками к тому моменту, когда JS не считался полноценным языком программирования. Однако стоит учесть комплексность задач, которые сейчас решаются с помощью JavaScript, и становится ясно: этот подход в корне неверный и должен быть искоренен.
И это не говоря о том, что в интернет заливают петабайты материалов, связанных с JavaScript, зачастую весьма сомнительного качества. В некоторых обучающих материалах, рассчитанных на новичков, творится форменный ад. Не удивительно, что, когда нанимаешь на работу даже разработчиков с опытом работы и программирования на JavaScript, ждать сформированного стиля и уж тем более приверженности к определенному стилю оформления кода не приходится. О понимании “best practices” речи вообще не идет.
Отмечу, что с ростом количества программистов, качество их подготовки, знаний и “ламповость” падает с ужасающей скоростью.
Однако, когда ты имеешь дело с коммерческой разработкой на JavaScript, разработкой приложений на разной стадии готовности и многочисленными ротациями разработчиков — в конечном итоге ты понимаешь, что без единого кодстайла в рамках одного проекта не обойтись.
Что он дает:
- Читабельность. Структурированный код всегда читабельнее неструктурированного, иначе б мы сразу писали код, близкий к минифицированному, и не парились.
- Единообразие. Немаловажный аспект, о котором многие почему-то не думают. Мозг человека — штука ленивая, он формирует некий инлайн кэш для чтения исходного кода, и как результат переключение между разными по стилю кусками или файлами требует времени для формирования новых паттернов чтения. Как по вашему, будет здорово, если разработчик при переходе с файла на файл будет «подвисать», формируя новую модель чтения? Вряд ли.
- Единый стиль оформления кода дает возможность освободить ресурсы разработчика, которые он тратит на чтение и переопределение стиля оформления для их более продуктивного использования. Например, кроме безусловного ускорения разработки, он может увидеть «узкие» участки в коде, потенциальные потери производительности, дефекты и прочий мусор и соотвественно увидеть возможности для улучшений.
- Быстрое вхождение нового разработчика. Этот пункт, как и пункт 3, скорее следствие единообразия и читабельности. Новому разработчику все же придется построить инлайн кэш для чтения, но в силу единообразия этот кэш будет строиться один раз для одного проекта. А в идеале — если все проекты следуют единому стилю — то единожды.
В чем минусы:
- Каждый разработчик должен знать кодстайл
- Уходит много времени на поддержку кодстайла.
- Уходит время на разрешение конфликтов по поводу того, какие кавычки правильнее или на какой строке ставить скобки.
Я не зря вынес это в название, как «Bracket Wars». Это была чуть ли не основная проблема первого этапа внедрения единого кодстайла. Именно отсутствие единого мнения на некоторые вопросы оформления порождала настоящие холивары. Тему холиваров я освещать не буду, а постараюсь предоставить решение данной проблемы.
Чаще всего можно встретить две противоположных точки зрения: «Работает же» и «В строке 21 7 пробелов в начале вместо 4». Обе точки зрения одинаково спорны. В них нет золотой середины, одна выльется в жуткий говнокод с огромным техническим долгом и кучей разъяренных разработчиков, которым придется разбираться с таким наследством, вторая может надолго застопорить разработку и выкатку новых релизов. Они обе не оправданы и ведут к рискам и недовольству клиентов.
Просто у первой эффект отсрочен — пусть сейчас это работает и мы, возможно, опередили сроки, но в будущем, когда функционал будет расширен или изменен, можно столкнуться с кучей проблем вроде сложности отладки, невозможности расширения или таких дефектов, которые не устранить без переписывания всего написанного ранее.
У второй практически мгновенный — мы не успели реализовать запланированные фичи в срок, потому что разработчик правил отступы в 70 измененных им файлах и менял 7 пробелов на 2 таба, занимался незапланированным рефакторингом, правил не понравившийся ему код вне поставленной задачи — тут можно перечислять очень много вариантов, когда перфекционизм ставит под вопрос само развитие проекта.
Поскольку ни одна из таких ситуаций недопустима, необходимо искать альтернативный путь и подход к оформлению кода уже сейчас.
К счастью, на сегодняшний день эти проблемы прекрасно решаются инструментами разработчика — самими IDE или плагинами к ним. Собственно автоматизацию процесса проверки и поддержки кодстайла и рассмотрим далее на примере трех редакторов/IDE, с которыми приходится сталкиваться чаще всего: Sublime Text, Atom, WebStorm.
Еще немного теории: то, что освещается дальше, относится к статическому анализу и линтингу (проверке) кода, соответственно плагины — это линтеры (парсеры) и/или их интеграция в тот или иной редактор или IDE.
Ранее я бы рассмотрел два основных, на мой взгляд, инструмента — это JSHint (статический анализ кода на предмет ошибок) и JSCS (JavaScript CodeStyle — анализ оформления кода на основе соглашений по оформлению или отдельных правил), но на сегодняшний день эти 2 инструмента прекрасно заменяет ESLint(почти всегда, но об этом ниже). Так что его интеграцию я и рассмотрю. Главное отличие ESLint от JSCS и JSHint — модульность и большая кастомизация.
Здесь стоит сделать уточнение по тому, насколько ESLint заменяет эти два инструмента. Парсер JS, который используется в ESLint (Espree — тоже подключаемый по факту), если судить по странице на npmjs, полностью заменяет JSHint, однако более настраиваемый, нежели предшественник, и более медленный, однако для цели ежедневной поддержки кодстайла это имеет крайне низкое значение. Стоит учесть, что скорее всего, замещение по поддержке правил кодстайла еще не полное, сами разработчики данного инструмента говорят о том, что хоть они и объединили усилия с командой JSCS для реализации более универсального инструмента, поддержка еще неполная. Список разночтений можно посмотреть здесь. Поддержка пресетов ограничена. Вернее будет сказать, что реализация этой поддержки теперь изменила вид, если JSCS поддерживал ограниченный набор пресетов из коробки, то теперь эти пресеты стали подключаемыми плагинами, которые нужно установить отдельно и прописать в конфигурационном файле. Я с ходу нашел пресет для Яндекса и пресеты для AirBnB и Google (последние 2 — можно выбрать при инициализации плагина на момент написания статьи).
Стоит выразить разработчикам отдельную благодарность за разнообразные форматы конфигурационных файлов, их автоматическую генерацию и догрузку модулей с пресетами в процессе инициализации конфигов для EsLint.
Но перейдем к практической части, а именно настройке EsLint. Стоит отметить наличие двух частей настройки: общей и редактор-специфичной. Начнем с общей.
Глобальная установка Eslint:
npm i -g eslint
Cтоит учесть, что при использовании nvm в linux может сложиться ситуация, когда eslint глобально не виден, в моем случае достаточно было создать символьную ссылку на eslint внутри /usr/local/bin/ командой:
ln -s /usr/local/nvm/vX.X.X/bin/eslint /usr/local/bin/eslint
где vX.X.X версия текущей Node.js, которую вы в последний раз использовали в
nvm use X.X.X
Она нужна для того, чтобы в корне вашего проекта запустить:
eslint --init
Данная команда приводит к диалогу генерации конфигурационного файла. Гайд очень похож на аналогичную генерацию package.json. По факту у вас 2 выбора: ответить на вопросы об используемых технологиях и кодстайле или выбрать пресет- в списке, как уже упоминалось ранее; можно выбрать Google, AirBnB и Standard — рекомендованный пресет от команды ESLint. Есть и третий вариант с проверкой кодстайла ваших файлов, который по сути действует так же, как и опрос, но также делает попытку определить кодстайл.
По факту не очень полезная фича, если у вас неидеальный кодстайл на момент инициализации конфига — падает при первом же грубом нарушении. Плюс у вас должны стоять глобально все плагины.
Далее идет выбор формата конфигурационного файла — Javascript, JSON, YAML. После выбора одного из форматов генерируется конфиг и доустанавливаются необходимые плагины. EsLint рекомендует использовать локальную установку EsLint и необходимых плагинов, устанавливает их в рабочую директорию и прописывает в раздел devDependencies вашего package.json.
На этом этапе вы можете еще больше кастомизировать ваш конфигурационный файл, следуя гайдам на сайте инструмента, и закомитить изменения. Например, для проекта на реакте я сгенерировал (отвечая на вопросы о кодстайле), а после добавил некоторые правила в такой конфиг:
{
"env": {
"browser": true,
"es6": true,
"commonjs": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
"installedESLint": true,
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
"jsx": true
},
"sourceType": "module"
},
"plugins": [
"react"
],
"rules": {
"indent": [
"warn",
"tab"
],
"linebreak-style": [
"warn",
"unix"
],
"quotes": [
"warn",
"double"
],
"semi": [
"warn",
"always"
],
"react/prop-types": "off",
"no-console": "off",
"no-unused-vars": "off"
}
}
Перейдем к настройке вашего редактора.
Atom.
С плагинами для атома все ясно — ставим в разделе настроек Install плагин linter-eslint, настраиваем его:
Также устанавливаем пакет linter, если он не стоял — это зависимость linter-eslint. Возможно нужно будет обновить Атом, в моем случае при каждом изменении в файле выпадала уйма ошибок и обновление редактора решило проблему. Простота установки плагинов для Атома — одно из его достоинств. Данный плагин также исправляет кодстайл при сохранении, если отметить “Fix errors on save”.
Sublime.
Через PackageControl нужно установить по факту три плагина. Это SublimeLinter, SublimeLinter-contrib-eslint и ESLint-formatter. Первый, как и linter в Atom, является основой линтинга кода в Sublime — этот фреймворк имеет обширную документацию, настройка его очень подробно описана. SublimeLinter-contrib-eslint — мостик для подключения eslint к SublimeLinter и имеет свою документации и воркэраунд в случае проблем. В настройках SublimeLinter в секции linters нужно прописать следующий код:
"linters": {
"eslint": {
"@disable": false,
"args": [],
"excludes": []
}
}
Oстальное — дело вкуса. Настройте внешний вид линтера под ваши нужды. Описание тем и настройки можно найти на сайте документации.
ESLint-formatter — позволяет автоматически отформатировать файл по вашим настройкам ESLint. Его также нужно настроить — прописать пути до Node.js и ESLint, также рекомендую как минимум поставить «format_on_save»: true. Возможно, стоит учесть проектную специфичность и создать .sublime-project и настройки для конкретного проекта прописать там.
WebStorm.
Настройка показана на скриншоте. Достаточно активировать ESLint в Settings > Languages & Frameworks > Javascript > Code Quality Tools > ESLint, прописать путь к локальной установке ESLint, если он не был определен автоматически. Рекомендовано автоматическое определение наличия конфига, но вы также можете указать путь к нему.
Единственный минус — автоматического форматирования по ESLint в WebStorm я не нашел — есть импорт кодстайла из JSCS конфига и Code > Reformat Code, a импорта из ESLint конфига нет. Однако функция линтинга работает:
Для WebStorm стоит учесть данный аспект и не отказываться от JSCS. Это именно тот аспект, из-за которого ESLint “почти всегда” заменяет предшественников — его поддержка в некоторых IDE все еще проигрывает JSCS и JSHint.
В заключение хотелось бы отметить, что жесткая привязка кодстайла к редактору во многих случаях не оправдана и ведет к проблемам настройки среды для разработки. Она оправдана лишь в исключительных случаях, вроде единого кодстайла на всех проектах. Но это крайне редкий кейс. В большинстве случаев модульность, которую предоставляет ESLint, имеет неоспоримые преимущества перед монолитностью. Учитывайте это и настраивайте проект специфично для конкретного случая, а редактор — универсально.
Какой профит от всего этого:
- Никаких “Bracket Wars”. Инструментарий заботится о кодстайле, а не вы. Это дает простор для коструктива вместо холиваров;
- Не нужно больше знать кодстайл — во всяком случае форсировать его изучение и насаждать. Со временем разработчик запомнит специфические правила, а если что-то и пропустит — “fix on save” всех спасет;
- Поддержка кодстайла автоматическая, не требует времени разработчика;
- Проект однороден по стилю, а это дает все плюшки, которые были описаны в начале статьи в “плюсах”.
Спасибо за внимание.
Комментарии (27)
EndUser
29.06.2016 10:45Не могу обнаружить в статье описание, что делать с массивнейшими изменениями в системах контроля версий, ежели каждый разработчик будет переформатировать текст при сохранении.
Как решаете?SSul
29.06.2016 11:46Самое массивное с чем я сталкивался — это изменение окончания строк во всем файле. это выглядит стремно и я обычно такое возвращаю обратно и помогаю перенастроить гит(как правило это он), на сохранение текущих концов строк.
Если форматирование настроено изначально, то массивных изменений быть не должно. Если же делается глобальное форматирование — то как правило есть требование такие вещи делать в отдельных коммитах или отдельными мр, чтобы не блокировать и не усложнять кодревью.
raveclassic
29.06.2016 12:17Вы про всякие line-endings? Это можно автоматически настроить per-project в
.git/config
. Достаточно проапдейтить этот файлик в, например, npm-хукеpostinstall
.
Если дело не в VCS, то на помощь приходит.editorconfig
, благо он поддерживается большинством редакторов/IDE.EndUser
29.06.2016 12:44Я про то, что скобки могут скакать между строчками весьма значительно, появляться и исчезать:
http://astyle.sourceforge.net/astyle.html#_Bracket_Style_Options
http://astyle.sourceforge.net/astyle.html#_break-closing-brackets
http://astyle.sourceforge.net/astyle.html#_add-brackets
Более того, разница в стилях может быть такова, что существенно изменятся практически все строки.
Я думал, что вы ограничитесь форматированием на лету на вкус программиста, но вопрос сохранения в корпоративном формате остался не раскрыт. Раскрыт только вопрос о том, что программист может отображаемый стиль именно что сохранить в файл. Далее, естественно, идёт commit/push, и контроль версий красит красным почти полный файл. Если над модулем работают разные программисты — эти 90% изменения будут почти каждый коммит.
Сравните сами:
мои опции этого бьютифаера --style=bsd --add-brackets --add-one-line-brackets
ваши могут быть, например, --style=google --remove-brackets
И это далеко не line endings, и не только line-endings. Это ещё и "} while", «else if» и другие споры.
На мой взгляд было бы логично, если бы контроль версий включал в себя «корпоративное» форматирование перед пушем в репозитарий. Да то останутся описанные вами проблемы чтения при визуальном сравнении версий именно в репозитарии.
Потому и спрашиваю.raveclassic
29.06.2016 16:26Если я вас правильно понял, то требуется автоматическое приведение к некому стилю при коммите/пуше прозрачно для разработчика? (чтобы он мог работать со своими настройками)
Тогда можно установить гитхуки для автоформатирования опять же на этапе postinstall. Но мне такой вариант не кажется надежным, мало ли что там «наавтоформатируется» перед коммитом.
Другое дело, если IDE/редактор обязывает разработчика соблюдать определенный кодстайл.EndUser
29.06.2016 20:30Поняли правильно.
С гитхуками понятно. У AStyle «опасные ключи», которые удаляют-добавляют не только white-chars, можно не пользовать.
Осталось два мелких вопроса:
1) Как и где программист после пулл-реквеста может сравнивать версии в приятном глазу формате
2) Почему автор темы не упоминает эти вопросы ;-)SSul
30.06.2016 10:31Смотрите какая идея во всем этом — не подстраиваться под «хочу — пишу, хочу — нет» конкретного человека, а форматировать исходный код всегда до коммита, чтобы разработчик всегда видел отклонения, всегда видел принятый кодстайл. Настойка оформления кода и сам кодстайл вынесен за рамки ответственности программиста и присутствует независимо от его предпочтений.В конечном итоге программист будет следовать ему со временем. Плюс ко всему — например я провожу ревью через гитлаб. Если код который я вижу в гитлабе, отформатирован так же как в редакторе или IDE, с которым я работаю — мне нужно лишний раз настраиваться на чтение(об этом я писал в статье).
raveclassic
29.06.2016 11:49Мы в команде при работе с WebStorm/Idea пришли к следующему:
eslint ставится в каждый проект, конфиги для него вынесены в отдельный репозиторий, который также подключается в каждый проект
в шторме включен eslint с конфигом, лежащим в node_modules, и в репозиторий закоммичена директория .idea/jsLinters. Это позволяет автоматически включать линтер в шторме на каждой машине (естественно, нужно сначала выполнить npm i)
в каждом проекте лежит .editorconfig с настройками отступов и прочего
Есть одна тонкость, шторм при указании конфига внутри проекта, почему-то не может автоматически подставить $PROJECT_DIR$ в путь, так что это нужно сделать руками в .idea/jsLinters/eslint.xmlSSul
29.06.2016 12:24Да, это один из вариантов воркэраунда кодстайла для вебшторма, я упомянул тот, с которым приходилось сталкиваться чаще — а именно импорт из .jscsrc. и да, папку .idea после настройки нужно закомитить, чтобы не заниматься настройкой на других машинах.
vintage
29.06.2016 14:28Что с кодстайлом на данный момент? Все плохо: качество кода страдает в угоду скорости разработки
Оформив весь код по единым правилам вы не повысите его качество.
Пример, низкокачественного кода из реального проекта, реализованного по всем "best practice":
function Model() { var data = {}; function getData() { return data; } function setData(val) { data = val; } Object.defineProperty(this, 'data', { get: getData, set: setData }); }
Мозг человека — штука ленивая, он формирует некий инлайн кэш для чтения исходного кода, и как результат переключение между разными по стилю кусками или файлами требует времени для формирования новых паттернов чтения. Как по вашему, будет здорово, если разработчик при переходе с файла на файл будет «подвисать», формируя новую модель чтения? Вряд ли.
Мозг так не работает. Он быстро обучается замечать важное и игнорировать несущественное. Так что научившись один раз игнорировать оформление, вы сможете работать с любыми проектами, а не "подвисать" при переключении.SSul
30.06.2016 10:45Оформив весь код по единым правилам вы не повысите его качество.
Я писал, что единое оформление освобождает ресурсы разработчика и дает возможность замечать плохой код. И при ревью, и при разработке. Если такой код попадает в продакшн — очень жаль того, кто это пропустил/написал. Все же код нужно читать, а не просматривать.
Мозг так не работает. Он быстро обучается замечать важное и игнорировать несущественное.
К сожалению, действительность далека от желаний. На моем опыте ряд проектов, которые требовали погружения и понимания, и отсутствие оформления кода затягивало процесс разработки. Возможно, мы рассуждаем о проектах разной сложности. Если проект не сложный — согласен, игнорировать можноvintage
30.06.2016 16:48Только если вы привыкли читать лишь код лишь в одном стиле. Привыкайте читать код независимо от стиля и у вас не будет проблем. Поработайте одновременно в нескольких проектах с разными стилями и быстро научитесь замечать главное, а не акцентировать внимание на расположении пробелов.
SSul
01.07.2016 08:24Это философия, на мой взгляд. Кто-то любит чистоту и порядок, а кому-то плевать. Но тех кому плевать — обычно мало. Опять же, те, кто любит чистоту, делятся на тех, кто будет ее наводить, и на тех, которые терпеть не могут убирать. Первые берут веник, вторые — покупают робот-пылесос. Если вам плевать, с чем работать — то можете хоть минифицированный код писать;) В конечном итоге любой код должен быть человекочитабельным и однородным. По моему, оправдывать неряшливость отрешенностью — ну не ок. Но если вы познали дзен — ждем статьи о том, как не беспокоится об оформлении кода и как этого достичь)
vintage
01.07.2016 15:25Я ещё раз повторяю: качество кода — оно не в расположении пробелов, и не в наличии лишних точек-с-запятой, а в логике работы. И не утрируйте про код в одну строку.
DartNyan
29.06.2016 22:08Использую standartjs.com — просто и удобно
SSul
30.06.2016 10:52Если я верно понял — Standard — 3й в списке пресетов. Набрать 2 команды и выбрать Standard в списке из 3х пунктов — не сложно. ESLint более универсален, он модульный, настраиваемый и мощный. Если вам нужна поддержка разного кодстайла в разных проектах — он нужен. Если у вас 1 стиль для всего — можно конечно же ограничится 1 библиотекой
RomanYakimchuk
30.06.2016 17:50Выскажу одно мнение в качестве вброса.
У каждого свое чувство вкуса, и мнение как должен выглядеть код. Одно из решений, это инструменты автоматического форматирования кода.
На каждой машине, включая машину с репозиториями, ставится такой инструмент, который конфигурируется каждым разработчиком под себя, а на сервере конфигурируется мейнтейнером конкретного проекта под стайл гайд проекта, или какой-то дефолтный стиль.
В итоге, каждый пишет как ему нравится (визуально), но на сервере всегда лежит корректно отформатированая версия. Форматирование производится на клиентских машинах перед коммитом или пушем одной командой в консоли, а на сервере после мержа изменений в девелоп ветку. Никакой валидации по стайлгайду (пробелы, казюбрики) на клиенте, за исключением архитектурных правил.
Такого инструмента, насколько я знаю, год назад небыло, но вполне вероятно, что на базе ESLint его можно запилить.SSul
01.07.2016 08:46Один мой коллега тоже так говорит. Однако, он еще не создал такого монстра, и вряд ли создаст.
Тут есть определенные проблемы, которые можно перечислять довольно долго. Одна из них заключается в том, что разработчики перестанут понимать друг друга. Смотрю я ревью, например, с хорошо отформатированным кодом, и говорю, строки с 130 — 140 — такие-то проблемы. Разработчик смотрит в свой код, а у него 100 строк в этом файле. Или еще кейс: просит меня разработчик помочь ему разобраться в моем коде — смотрит в мой монитор и говорит: а у тебя все не так, я ему говорю: пойдем посмотрим у тебя, раз не так. смотрю в его файл и тоже ничего не понимаю, потому что у парня помимо всяких своих прихотей по настройке внешнего вида ide, длина строк 55 символов вместо 120, к примеру. Ну это из самого безобидного)
Соответсвенно постоянно надо мапать 1 стиль на другой, разводить зоопарк из стилей. По моему личному убеждению — поддержка единого кодстайла на 1 проекте — вещь необходимая и обсуждению не подлежит. У всех разработчиков свой бекграунд, кто-то пришел из руби, кто-то из пхп, кто-то вообще пишет под настроение… если каждый будет вносить бедлам в проект — в команде будет много холиваров. Это не конструктивно и вредит разработке. Это долгий диспут, я считаю.
В общем, да это есть идея, но она крайне сырая и мы вряд ли можем ожидать ее воплощения в ближайшие годы. Ну и опять же, если не указывать разработчику, что его кодстайл далек от идеала, он не станет интересоваться бестпрактисами, оптимизацией выполнения и прочим.
krimtsev
Сделайте пожалуйста спойлер. Зачем всю статью на главной держать ;)
GreyCat
По-моему, всю статью можно сократить примерно до «Code style = добро; для JavaScript нужно использовать ESLint; у него есть плагины для Sublime Text, Atom, WebStorm» без особенной потери смысла.
Отдельно забавно, что в статье, пропагандирующей правильное количество пробелов в отступах, такой лютый ад с этими самыми отступами в примерах конфигов.
SSul
Поправили, ишью переноса между реальным конфигом и доками/хабром
samizdam
Ну, я считаю, что лишнее напоминание о хороших практиках не бывает лишним. Повторение — мать учения.
Лично наблюдаю товарищей, занимающихся фронтенд-разработкой (у себя на работе): мало у кого есть понимание о вещах упомянутых в статье, хотя многие не первый год работают. То есть конечно да, они слышали об этом, читали, м.б. пробовали, но… руки всё не доходят…
А вода камень точит: после сто первой статьи на хабре, глядишь: и пересилят себя, настроят линты наконец-то.