» » » Линтеры в Go. Как их готовить. Денис Исаев

 

Линтеры в Go. Как их готовить. Денис Исаев

Автор: admin от 27-06-2019, 22:45, посмотрело: 127

Предлагаю ознакомиться с расшифровкой доклада Дениса Исаева "Линтеры в Go. Как их готовить."



В go 50+ линтеров: в чем их профит и как эффективно встроить их в процесс разработки? Доклад будет полезен как тем, кто еще не использует линтеры, так и тем, кто уже применяет их: я раскрою малоизвестные трюки и практики работы с линтерами.





Кому интересно, прошу под кат.



Привет. Меня зовут Деннис Исаев. Мы поговорим о том как готовить линтеры в Go. Доклад будет интересен как новичкам, кто еще не использовал линтеры, так и профессионалам. Я расскажу про некоторые малоизвестные трюки.



Линтеры в Go. Как их готовить. Денис Исаев

Немного об о мне. Я автор opensource проекта Golangci-lint. Работал в mail.ru. Сейчас работаю TeamLead в backend в Яндекс.Такси. Мой доклад основан на опыте общения с сотнями пользователей Golangci-lint. О том как они использовали линтер, какие у них были трудности и опыте внедрения Go линтеров в компаниях mail.ru и Яндекс.



Линтеры в Go. Как их готовить. Денис Исаев

Мы осветим 5 основных вопросов в докладе.



Линтеры в Go. Как их готовить. Денис Исаев

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



Линтеры в Go. Как их готовить. Денис Исаев

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



Линтеры в Go. Как их готовить. Денис Исаев

Кроме того и часа свой что линтер находит только мелочовку, что-то стилистическое, какие-то не критичные баги и на самом деле оно того не стоит. Проще ли тратить время.



Линтеры в Go. Как их готовить. Денис Исаев

Сразу контрпримеры. Баг в Docker найден с помощью go vet. Забытый вызов Cancel функции. Из-за этого фоновая горутина может не завершиться.



Линтеры в Go. Как их готовить. Денис Исаев

Баг в проекте Etcd. Классный линтер go-critic нашел что аргумент strings.HasPrefix перепутаны местами. Значит не будет работать проверка на небезопасный протокол HTTP.



Линтеры в Go. Как их готовить. Денис Исаев

В самом Go баг что i элемент сравнивается i-ым, хотя должен сравниваться с j-ым. Тоже найден линтерами.



Линтеры в Go. Как их готовить. Денис Исаев

Три примера в крупных opensource проектах найденные линтерами. Некоторые спросят: Ок и что? Он находит какие-то критичные баги. На один критичный баг находит 100 некритичных или ложных срабатываний. Могу привести свою эмпирическую статистику: у меня обычно где-то 80 процентов всех проблем которые репортят линтеры это: какие-то стилистические issues, например, переменные не используются и так далее, 15 процентов это реальные баги и где-то 5 процентов ложные срабатывания.



Линтеры в Go. Как их готовить. Денис Исаев

Теперь поговорим о том зачем нужны линтеры. Самый главный бонус что линтеры экономят время. А время это деньги. Чем раньше вы найдете баги, тем дешевле они стоит для вашей компании. На слайде график примерной стоимости исправления бага зависимости от стадии где он найден. Соответственно с каждой стадии начиная разработки заканчивая production стоимость увеличивается в три раза. Находите баги рано, в IDE идеально и экономьте деньги компании.



Линтеры в Go. Как их готовить. Денис Исаев

Часто бывает такое что на CodeReview разработчики репортят какие-то проблемы, которые могли бы найти линтеры. Зачем они это делают непонятно. Во-первых, автору кода нужно дождаться пока он пройдет CodeReview. Во-вторых, самому ревьюеру нужно потратить время чтобы найти какие-то механические проблемы. Он мог доверить это ли линтеру. Я когда такое замечаю всегда форсирую и мы договариваемся в команде что все что можно найти линтер, мы глазами не ищем на review. Мы доверяем все это ли линтерам. Более того, если мы находим какие-то проблемы которые в review часто бывают и линтеров на них нет мы стараемся найти линтеры, которые могли бы их в теории ловить. Чтобы мы не тратили время на review.



Линтеры в Go. Как их готовить. Денис Исаев

Линтеры позволяют нам как-то гарантировать и иметь предсказуемое качество кода в проекте. Например, в данном случае это неиспользуемые аргументы функции.



Линтеры в Go. Как их готовить. Денис Исаев

Линтеры позволяют находить критичные баги, максимально рано, экономить время CodeReview. При этом гарантировать какое-то качество кода по проекту.



Линтеры в Go. Как их готовить. Денис Исаев

В Go более 50 линтеров, но самые такие популярные 4. Это те, которые на слайде. Их использует просто потому что они классные. С остальными обычно не хочется разбираться. Я сейчас хочу на примерах показать какие вообще есть линтеры. Хочу продемонстрировать 25 линтеров на примерах. Сейчас будет наверное самое важное в докладе.



Линтеры в Go. Как их готовить. Денис Исаев

Начнем с линтеров проверяющих форматирование. Gofmt это по сути не линтер. Но мы его можем рассматривать как линтер. Он умеет говорить нам не хватает перевода строк, где-то лишние пробелы. В общем это стандарт для проверки и поддержания форматирования кода.



Линтеры в Go. Как их готовить. Денис Исаев

Также у gofmt есть малоизвестная опция -s, которая позволяет упрощать выражения.



Линтеры в Go. Как их готовить. Денис Исаев

Goimports содержит себе все то же самое что содержит gofmt, но дополнительно он еще умеет переупорядочить импорт, удалять и добавлять нужные импорты.



Линтеры в Go. Как их готовить. Денис Исаев

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



Линтеры в Go. Как их готовить. Денис Исаев

Рассмотрим линтеры проверяющие сложность кода. Самый классный из них это gocyclo. Он же самый занудный. Его многие ненавидят. Он проверяет цикломатическую сложность кода и ругается когда это сложность функции превысит какой-то порог. Порог настраивается. Если упрощенно, цикломатическая сложность — это количество if в коде. Здесь он слишком большой и линтер ругается.



Линтеры в Go. Как их готовить. Денис Исаев

Nakedret это такой линтер, который умеет говорить что вы используете return без значений и при этом вы его используете в слишком длинной функции. По официальным guide такие return не рекомендуются.



Линтеры в Go. Как их готовить. Денис Исаев

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



Линтеры в Go. Как их готовить. Денис Исаев

Golint ругается на эту же переменную apiUrl. Говорит что url следует использовать большими буквами. Так как эта аббревиатура.



Линтеры в Go. Как их готовить. Денис Исаев

Gochecknoinits убеждается что вы не используете init функции. Init функции по определенным соображениям не следует использовать.



Линтеры в Go. Как их готовить. Денис Исаев

Gosimple классный линтер. Часть staticheck или megacheck. Внутри себя содержит огромное количество паттернов по упрощению кода. В данном случае можете заметить что strings.HasPrefix не нужен, так как strings.TrimPrefix уже содержит внутри себя нужные проверки и можно убрать if.



Линтеры в Go. Как их готовить. Денис Исаев

Goconst проверяет что у вас в коде нет повторяющихся строковых литералов, которые можно было бы вынести в константы. Количество этих повторов настраиваются. В данном случае два.



Линтеры в Go. Как их готовить. Денис Исаев

Misspell линтер, который проверяет что у вас в коде в комментариях нет опечаток. В данном случае на слайде опечатка слове else в тексте комментария. Можно настраивать диалект английского: американский, британский.



Линтеры в Go. Как их готовить. Денис Исаев

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



Линтеры в Go. Как их готовить. Денис Исаев

Теперь посмотрим линтеры, которые проверяют неиспользуемый код. Во-первых это varcheck. Он проверяет неиспользуемые переменные.



Линтеры в Go. Как их готовить. Денис Исаев

Unused умеет ругаться на неиспользуемые поля структур.



Линтеры в Go. Как их готовить. Денис Исаев

Deadcode говорит нам, если не используется тип.



Линтеры в Go. Как их готовить. Денис Исаев

Или не используется функция.



Линтеры в Go. Как их готовить. Денис Исаев

Unparam умеет сообщать когда аргументы функции не используются в самом теле функции.



Линтеры в Go. Как их готовить. Денис Исаев

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



Линтеры в Go. Как их готовить. Денис Исаев

Есть группа линтеров проверяющих производительность. Например, maligned говорит нам что данную структуру testStruck можно сжать в размере с помощью переупорядочивания полей. Более того, если запускать его как часть golangci-lint, у него есть опция позволяющая распечатать вам сразу нужный порядок полей, чтобы самому не подбирать их.



Линтеры в Go. Как их готовить. Денис Исаев

Есть такой классный линтер gocritic. Внутри у него множество проверок. Одна из них hugeParam. Она умеет репортить нам о копировании тяжелых структур данных. В данном случае heavyStruct копируется по значению и нам нужно просто передавать ее как указатель.



Линтеры в Go. Как их готовить. Денис Исаев

Prealloc умеет находить нам места в коде, в которых мы можем заранее преаллоцировать slice. Он это находит так, что ищет где мы делаем константные часовые итерации по slice. И в них дела append. В данном случае, можно сделать на длину slice ss преаллоцирование переменной ret и сэкономить память и ЦПУ.



Линтеры в Go. Как их готовить. Денис Исаев

И наконец линтеры, которые находят баги. Scopelint находит наверное самую часто ошибку новичков в go это захват переменной range цикла по ссылке. В данном случае переменная цикла arg захватывается по ссылке. На следующей итерации там будет уже другое значение.



Линтеры в Go. Как их готовить. Денис Исаев

Staticcheck. Раньше именовался megacheck. Теперь он переименовался. Из-за этого есть такая небольшая путаница в комьюнити. Staticcheck умеет находить тонны различных багов. Это совершенно классная штука. Как и go vet. Один из них на слайде — это гонка. Нам нужно конечно же инкрементить sync.WaitGroup до захода в горутину.



Линтеры в Go. Как их готовить. Денис Исаев

Go vet находит в основном баги. В данном случае переменная i сравнивается так что результат всегда будет true. Поэтому здесь очевидно баг. Вообще всегда стоит использовать go vet.



Линтеры в Go. Как их готовить. Денис Исаев

Gosec расшифровывается как go security. Находит потенциальные проблемы с безопасностью в Go. В данном случае у нас в arg могут поступать пользовательские данные. Поэтому она может проникать в shell команду rm. И тут может быть там shell in action например. Замечу что go security довольно часто выдает false positive. Поэтому я его иногда отключаю.



Линтеры в Go. Как их готовить. Денис Исаев

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



Линтеры в Go. Как их готовить. Денис Исаев

Отдельно должен отметить два линтера это staticcheck и go-critic. Потому что внутри себя каждый из них содержит еще по десятки, если не сотни, проверок. Поэтому обязательно посмотрите их попробуйте.



Линтеры в Go. Как их готовить. Денис Исаев

Сейчас мы рассмотрели 25 линтеров на примерах. И я еще говорил что у нас более 50 линтеров в Go. Какие использовать? Я советую обычно использовать все по максимуму. Включите просто все линтеры какие вы можете. А дальше потратьте час и начинайте их выключать по одному. Выключайте те которые вам кажутся несущественными. Например, он находит вам какие-то оптимизации производительности, которые вам вообще не интересны. Вы потратите час и сформируйте для себя свой список линтеров и с ним можете уже дальше жить.



Линтеры в Go. Как их готовить. Денис Исаев

Полный каталог всех линтеров есть по ссылке на слайде.



Линтеры в Go. Как их готовить. Денис Исаев

Поговорим про то как запускать линтеры. Иногда линтеры запускают с помощью таких вот makefile. Проблема в том что оно работает медленно. Оно все выполняется последовательно.



Линтеры в Go. Как их готовить. Денис Исаев

Мы можем сделать выполнение параллельно через xargs -P. Здесь тоже остается проблема. Во-первых, это всего лишь 4 линтера. А уже 10 строчек кода. Что будет если мы 20 линтеров включим. Во-вторых, это распараллеливание ну мягко говоря не самое оптимальное.



Линтеры в Go. Как их готовить. Денис Исаев

На помощь приходит gometalinter. Gometalinter это такой агрегатор линтеров, которые он может запускать буквально в пару команд. На слайде аналогичная предыдущему слайду команда запуска этих же линтеров. Но их не надо самостоятельно устанавливать и не нужно шаманить с параллельным запуском. Gometalinter уже под капотом все распараллеливает. Но у него есть одна фундаментальная проблема. Он запускает каждый линтер как отдельный процесс. Форкает его. Если добавить к этому знание о том что каждый линтер внутри себя 80 процентов времени тратит на парсинг кода и лишь 20 процентов на сам анализ, то получается что 80 процентов работы мы тратим впустую. И никак не переиспользуем данные. Мы могли бы 1 раз распарсить программу и дальше скормите 50 линтерам.



Линтеры в Go. Как их готовить. Денис Исаев

К счастью, есть golangci-lint, который делает ровно это. Он один раз парсит. Один раз достает типы. Дальше прогоняет на них уже анализаторы. За счет этого оно работает значительно быстрее. Аналогичная команда запуска на слайде.



Линтеры в Go. Как их готовить. Денис Исаев

Можно посмотреть график на одном из моих проектов 30 тысяч строк кода. Небольшой проект и всего 4 линтера. Можно заметить там колоссальную разницу по скорости работы в разы, как между последовательным запуском, так и между gometalinter, так и golangci-lint. Если этих линтеров будет не 4, а 20, то разница будет намного больше.



Линтеры в Go. Как их готовить. Денис Исаев

Важное уточнение про gometalinter. C 7 апреля автор проекта gometalinter объявив его deprecated. Репозиторий заархивировал и всем советуют переходить на golangci-lint, так как он более быстрый, там у него больше плюшек. Например, поддержка go модулей и так далее.



Линтеры в Go. Как их готовить. Денис Исаев

И кроме производительности и Go модулей в golangci-lint есть такие плюшки как YAML конфигурация, возможность как-то пропускать предупреждения, исключать их так далее.



Линтеры в Go. Как их готовить. Денис Исаев

Golangci-lint конфигурируется с помощью файла golangci-lint.yaml. Пример этого файла с описанием всех опций есть по ссылке на слайде. Рассмотрим, например, секцию linters-settings. В этой секции рассмотрим конфигурацию goimports. У него есть такая редкая опция local-prefixes. В ней можно указать путь к текущему проекту. В данном случае для примера github.com/local/repo.



Линтеры в Go. Как их готовить. Денис Исаев

Когда goimports будет видеть локальные импорты и в github.com/local/repo, он будет обращать внимание на то чтобы они были в отдельной секции.



Линтеры в Go. Как их готовить. Денис Исаев

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



Линтеры в Go. Как их готовить. Денис Исаев

А если вы еще и используйте опцию golangci-lint run --fix, то golangci-lint еще и зафиксит за вас автоматом и пересортируют импорты.



Линтеры в Go. Как их готовить. Денис Исаев

Поговорим про то какие есть линтеры есть в терминологии golangci-lint. Линтеры делятся на быстрые и медленные. Быстрые называются fast, помечены флажком fast в help. Отличаются они тем что быстрые линтеры требуют довольно ограниченное представление программы, например AST дерево и какую-то информацию о типах. В то время как медные линтеры еще дополнительно требует SSA представления программой и меньше переиспользуют кэш. Медленных линтеров всего шесть. Они помечены на слайде. Есть определенные кейсы, когда имеет смысл запускать только быстрые линтеры.



Линтеры в Go. Как их готовить. Денис Исаев

Можно заметить разницу в скорости. Она колоссальная в три раза между быстрым запуском и медленным. Собственно golangci-lint run --fast запускается только быстрые линтеры.



Линтеры в Go. Как их готовить. Денис Исаев

Про build cache. Есть такая штука как build cache. Это кэш, который строится бинарником Go при компилировании программы при загрузке типов, чтобы следующий раз эта компиляция была быстрее. Этот же кеш переиспользуют линтеры для парсинга программы для построений информации о типах. Можно заметить что если кеш очистить, то первый свежий запуск будет довольно долгим. А последующий будет уже в 3 раза быстрее. Обратите внимание на ваш первый запуск линтеров на вашем проекте. Он всегда будет значительно медленнее.



Линтеры в Go. Как их готовить. Денис Исаев

Здесь же можно сделать вывод что есть смысл в CI между запусками CI переиспользовать ваш кэш. Вы ускорите не только линтеры, вы ускорите еще и запуск тестов, просто компиляцию и возможно ещё что-то. Всем советую.



Линтеры в Go. Как их готовить. Денис Исаев

Не могу не поговорить про go analysis. Это новый фреймворк, который появился начиная с Go 1.12. Он унифицирует интерфейсы таким образом что линтеры становится легко писать, линтеры легко использовать, запускать. Go vet начинается 1.12 вообще перешел целиком на go analysis. Очевидно что за этим будущее. Что оно сильно изменит всю экосистему Go. Но пока вообще довольно рано говорить об этом. Чтобы что будет дальше? Потому что я видел всего несколько линтеров на go analysis и практически ни один из существующих на него еще не перешел.



Линтеры в Go. Как их готовить. Денис Исаев

Если сделать краткий вывод по секции как запускать линтеры, то советую всем использовать golangci-lint. Вы будете быстро удобно запускать линтеры. Вам не надо шаманить с другими инструкциями, командами.



Линтеры в Go. Как их готовить. Денис Исаев

Поговорим про то как внедрять линтеры в проект. Я думаю там все кто пытались внедрить линтеры сталкивались с такой проблемой. Вот у вас проект на миллион строк кода с историей. Вы убедили TeamLead внедрить линтеры. Запускаете и видите миллион сообщений. Понимаете что вам сидеть безвылазно недели чтобы все это исправить. Что делать? Можно просто сдаться и бросить все это. Или можно что-нибудь придумать.



Линтеры в Go. Как их готовить. Денис Исаев

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



Линтеры в Go. Как их готовить. Денис Исаев

Можно исключить путям. Например, у вас есть директория third-party и там лежит не ваш код. Вам его не нужно проверять. Можно исключить по именам файлов.



Линтеры в Go. Как их готовить. Денис Исаев

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



Линтеры в Go. Как их готовить. Денис Исаев

Когда вообще использовать nolint? Например, я использую nolint:deepguard, который умеет заблеклистить импорты, т.е. импорты нельзя использовать. Я заблеклистил импорт библиотеки logrus для того чтобы случайно не использовать его вместо моего нужного логера. Но в самом моем логере я использую logrus. Поэтому мне нужно только в одном месте в проекте только в одном файле сделать от импорт. Я помечаю его с помощью nolint.



Линтеры в Go. Как их готовить. Денис Исаев

Допустим вы все это сделали, exclude добавили, nolint проставили. Видим что все равно осталось тысячи сообщений. Исправлять это несколько дней. Есть классный хак. Рассмотрим на примере. Есть файлик main.go, в котором 5 строчка добавлена давно, а 6 строчка добавлена только сегодня. Что можем сделать?



Линтеры в Go. Как их готовить. Денис Исаев

Мы можем использовать revgrep. Revgrep позволяет нам указать git ревизию, после которой нужно искать баги. То есть оставлять сообщение линтеров только после заданной ревизии. Если 6 строчка у нас изменена после origin master, то он за зарепорит только ее. А все предыдущие сообщения, 5 строчку он не зарепортит. Это очень классный трюк. Именно с помощью него можно внедрить линтер любой проект за час. Как мы это делаем. Мы берем запускаем там golangci-lint на проекте в миллион строк кода. Он выдает тысячу предупреждений. Мы немножко понастроили, подшаманили. Дальше мы договариваемся с командой что прямо сейчас мы делаем git ревизию или используем hash commit. После которого мы не допускаем ошибки линтеров. Но до которого мы все ошибки линтеров оставляем и пока их не правим или правим медленно. Мы указываем это этот hash commit или tag в revgrep и начинаем запускать CI. Отныне линтер будут репортить нам только ошибки в новом коде. При этом старом коде они никак не будут реагировать на ошибки. и таким образом можно вот реально за час внедрить линтеры в любой проект. Именно так я сделал в mail.ru, когда внедрял линтеры в огромные проекты.



Линтеры в Go. Как их готовить. Денис Исаев

Более того revgrep уже внедрен в golangci-lint. Достаточно просто указать опцию --new-from-rev или --new. Всем обязательно советую.



Линтеры в Go. Как их готовить. Денис Исаев

Здесь есть еще одна тонкость. Допустим мы постепенно со временем все ошибки зафиксили в старом коде и убрали вообще опцию --new. У нас сейчас есть 20 линтеров, мы их запускаем. Новых ошибок нет. В один момент добавляется новый линтер. Мы хотим этот линтер тоже запускать. Но он выдает очень много ошибок. Что делать? Если мы добавим --new-from на все линтеры, то будет не круто. Мы хотим все прошлые линтеры запускать на всем проекте.



Линтеры в Go. Как их готовить. Денис Исаев

Решение простое. Можем запускать golangci-lint дважды. Один раз запускать его на новом коде с новым линтером. Второй раз запускать его целиком со всеми старыми линтерами на всем проекте. Такой трюк сильно помогает внедрять новые линтеры, когда они выходят.



Линтеры в Go. Как их готовить. Денис Исаев

Мы поговорили про внедрение линтеров в любой проект. Теперь поговорим про удобство работы. Во-первых, нужно добиться воспроизводимости в CI. Как только вы добавляете линтер в CI, вам нужно чтобы она была стабильна. Никогда не делайте go get. Потому что оно не версионировано. Линтер в любой момент изменился, обновился и у вас все CI build начали фейлиться. Это я видел десятки раз. Всегда используйте конкретные версии. Лучше с помощью wget ее поставьте. Она еще быстрее будет. Кроме того, не рекомендую использовать опцию ---enable-all для линтеров, потому что в один день вы обновляете golangci-lint, например, у вас добавляется 5 новых линтеров и у вас все build начинает фейлиться. Потому что вы эти линтеры случайно включили. Лучше явно прописываете какие линтеры включаете.



Линтеры в Go. Как их готовить. Денис Исаев

Классная штука pre-commit hook. Кто использует pre-commit hook поднимите руки? Довольно мало. Pre-commit hook это файл в git, который позволяет вам исполнять произвольный код после того как вы захотели закоммитить. Но до того как этот commit завершится успешно. Если pre-commit hook возвращает вам ошибку, to commit не пройдет. Туда обычно удобно встраивать быстрые тесты, статический анализ и так далее. Всем советую встраивать golangci-lint. Можно делать это вручную через shell скрипт. Можно через утилиту pre-commit. Пример того как настроить на слайде. Устанавливается pre-commit с помощью pip — утилиты для установки пакетов Python. pip install pre-commit устанавливает конфиг. Golangci-lint уже поддерживает интеграцию с pre-commit.



Линтеры в Go. Как их готовить. Денис Исаев

Опция --fast. Мы к ней вернулись. Всем советую использовать в IDE именно ее. Вообще в IDE конечно же стоит использовать интеграцию с линтерами. Для того чтобы ваша IDE не подвисала обязательно используйте опцию --fast.



Линтеры в Go. Как их готовить. Денис Исаев

Думаю это довольно очевидно. В CI линтеры надо встраивать. Если вы их не встроите, то будет классическая картина: "давай сейчас забьем, сейчас у нас релиз, не до этого". Постепенно у вас будет все больше и больше замечаний. Вы просто перестанете смотреть на линтеры как класс. Поэтому строго в CI. Более того, можно просто установить линтеры в CI, при build fail мы залезаем в build log, ищем почему он там свалился. Где какое замечание, на какой строчке? Это не очень удобно.



Линтеры в Go. Как их готовить. Денис Исаев

Есть способ круче. Можно сделать так чтобы линтеры выступали как человек, как reviewer. Они могут комментировать вам в github.com, gitlab.com строчки кода с ошибкой на вашем Pull request. Линтер могут писать что нашёл проблему. Это невероятно круто. Это позволяет экономить время авторов кода. Потому что не нужно лезть в build log. Плюс человек может прокомментировать, если он не согласен с этим замечанием линтера. В примере на слайде это делается с помощью утилиты reviewdog. Утилита opensource. Она бесплатная. Можно у себя установить.



Линтеры в Go. Как их готовить. Денис Исаев

Кроме reviewdog есть еще такие проекты как GolangCI, Code Climate, Hound. Они позволяют подключить буквально в один клик к своим opensouce или приватным репозиториям вот эти вот линтеры и комментировать inline в Pull Request. Есть еще классная штука SonarQube.



Линтеры в Go. Как их готовить. Денис Исаев

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



Линтеры в Go. Как их готовить. Денис Исаев

Я хотел бы чтобы вы прямо в понедельник пришли на работу и смогли что-то применить из того что я сказал. Вот кратко что можно применить. Во-первых установите golangci-lint. Включаете там все линтеры. Дальше тратите 1 час. За этот час выключаете все линтеры, которые вам кажутся бредовыми. После этого встраиваете golangci-lint в CI, в IDE и настраивайте pre-commit hook. Cразу после этого можно настроить --new-from-rev и указать что с текущего коммита мы ищем баги. А все предыдущие баги будем исправлять потом отдельно. После этого опционально настраиваете reviewdog, чтобы он еще комментировал вам в ваш github или в gitlab. Вы повысите этим качество проекта просто колоссально. Обрадуете всю команду.



Линтеры в Go. Как их готовить. Денис Исаев

Всем спасибо за внимание. Мои контакты на слайде.



Вопрос: Подскажите у вас есть уже готовые конфигурационные файлы, которые вы выложили в открытый доступ и которые можно просто скачать и использовать чтобы не разбираться в тонне настроек golangci-lint? То что вы рекомендуете.



Ответ: Хорошая идея. В самом golangci-lint уже есть свой golangci-lint.yaml, который он использует. Можно его использовать как стартовую точку.



Вопрос: На слайде про build cache ссылаешься на кэш для модулей. В кэше указываешь полностью кэш модулей. Можно указать .cache/downloads тогда будет достаточно большое различие: 400 мегабайт против 10. Этого достаточно для того чтобы модули просто экстрактились. Но это только если модуль используется.



Вопрос: Будете также поддерживать и go модули или dep или уходить во что-то в одно?



Ответ: Не нужно одно поддерживает. Сейчас есть библиотечка go packages. Она занимается подгрузкой исходного кода. Она поддерживает и модули и не модули. Она не собирается пока но убирать поддержку не модулей.



Вопрос: Планируете ли вы делать различные плагины для интеграции с не только с Travis, но и с другими серверами автоматизации?



Ответ: golangci-lint не делает никакой интеграции. Чтобы в CI запустить достаточно просто вызвать golangci-lint --run.



Вопрос: Чтобы парсить какие-нибудь репорты, например в дженкинсе мы сохраняем html-файл.



Ответ: Есть формат вывода junit, csv, json xml. Это все уже можно парсить.



Вопрос: мы использовали раньше gometalinter и он был медленный. Потом мы переключились на линтер кто называется revive. Вы его вообще никак не упоминали. А я с моей стороны я вообще не эксперт в теме. Я не знал про ваш линтер, который вы рассказывали. Вы не могли бы в конце писать допустим плюсы вашего линтера или плюсы revive.



Ответ: revive это переписанный golint. Это всего лишь один из линтеров. Там есть настройки. Он еще добавил несколько линтеров, несколько проверок. Golangci-lint это внутри себя 30-50 линтеров. Это в revive полтора линтера. Revive классен тем что он взял golint и сделал параллельным. Revive работой быстрее чем golint. Но это лишь один линтер. Revive можно сделать частью golangci-lint.



Вопрос: У тебя был слайд в обзоре линтеров про gocritic: hugeParam, который рекомендуют передавать жирные структуры по указателю. Но это же приведет к тому что все эти структуры будут аллоцироваться исключительно в HEAP. И не приведет ли это к большим проблемам, чем преимуществам? Если таких структур, например, передается много.



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



Вопрос: Я из Яндекса. Мы пользуемся вашим линтером на большом репозитории. Заметили что он большом репозитории уже начинает не очень быстро работать. Буквально за пару дней написали простую утилиту, которая через go package находит пакеты, которые изменились с момента введения ветки от мастера и пакеты, которые от них зависят. И запускаем линтер только на них. И проверка линтером в разы ускорилась.



Ответ: Может быть вы idssue создадите, приложите скриптик и вполне возможно я все это встрою в golangci-lint.



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



Ответ: Много человек просило и сразу скажу сложность том, что вот эти уровни серьезность их поддерживать всего там 3 или 4 линтера из с 30. Что делать со стальными? Не понятно. Нужно получаться в ручную парсить их замечания, как-то размечать их. Обрабатывать ложные срабатывания. Это вообще большой объем работ. Я не уверен что это когда ли будет сделано. Есть другие способы как достичь той же самой цели.



Вопрос: На хабре есть статьи. А точнее серия статей про C++ линтер. Компания развивает это дело. Они зарабатывают на этом деньги. На самом деле их работа, а точнее эта серия публикаций направлена больше не на разработчиков, а больше на тех кто разработчиками управляет. Эта по сути чистота кода, стилизация. Это наша задача, но и задача руководителей, тимлидов. Планируется ли вот популяризация вот этого направления, линтеров в СМИ, в крупных таких ресурсах, чтобы люди это читали и потом это внедряли своих командах? А не мы снизу стучались.



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



Источник: Хабр / Интересные публикации

Категория: Google

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

Добавление комментария

Имя:*
E-Mail:
Комментарий:
Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
Введите два слова, показанных на изображении: *