Slide 1

Slide 1 text

КАК ГОТОВИТЬ ЛИНТЕРЫ В GO Денис Исаев Яндекс.Такси @jirfag

Slide 2

Slide 2 text

1. Golangci-lint 2. Mail.Ru Group 3. Yandex О докладчике

Slide 3

Slide 3 text

1. Зачем нужны линтеры 2. 50+ линтеров: какие использовать 3. Запуск линтеров 4. Внедрение в проект 5. Удобство работы Содержание

Slide 4

Slide 4 text

Что такое линтер 1. Зачем нужны линтеры

Slide 5

Slide 5 text

Что такое линтер 1. Зачем нужны линтеры

Slide 6

Slide 6 text

Почему линтеры не используют? 1. Зачем нужны линтеры

Slide 7

Slide 7 text

Почему линтеры не используют 1. Зачем нужны линтеры “Линтеры только замедляют разработку, и их долго настраивать”

Slide 8

Slide 8 text

Почему линтеры не используют 1. Зачем нужны линтеры “Они находят только всякую мелочь, ничего серьезного”

Slide 9

Slide 9 text

Баг в docker, найденный go vet 1. Зачем нужны линтеры

Slide 10

Slide 10 text

Баг в etcd, найденный go-critic 1. Зачем нужны линтеры

Slide 11

Slide 11 text

Баг в Go, найденный линтерами 1. Зачем нужны линтеры

Slide 12

Slide 12 text

И что? 1. Зачем нужны линтеры

Slide 13

Slide 13 text

1. Находить баги рано 1. Зачем нужны линтеры

Slide 14

Slide 14 text

2. Код-ревью быстрее 1. Зачем нужны линтеры

Slide 15

Slide 15 text

3. Качество кода 1. Зачем нужны линтеры

Slide 16

Slide 16 text

{ Выводы Линтеры 1. Находить критичные баги 2. Находить баги рано 3. Ускорять код-ревью 4. Гарантировать качество кода 1. Зачем нужны линтеры

Slide 17

Slide 17 text

В Go 50+ линтеров 2. Какие линтеры использовать 1. go vet 2. staticcheck 3. errcheck 4. golint

Slide 18

Slide 18 text

Проверка форматирования 2. Какие линтеры использовать gofmt: нет перевода строки

Slide 19

Slide 19 text

Проверка форматирования 2. Какие линтеры использовать gofmt: лишние пробелы

Slide 20

Slide 20 text

Проверка форматирования 2. Какие линтеры использовать gofmt -s: упростить до x[1:]

Slide 21

Slide 21 text

Проверка форматирования 2. Какие линтеры использовать goimports: поменять местами импорты

Slide 22

Slide 22 text

Проверка форматирования 2. Какие линтеры использовать unindent: заменить на if a && b

Slide 23

Slide 23 text

Проверка сложности кода 2. Какие линтеры использовать gocyclo: цикломатическая сложность функции высокая

Slide 24

Slide 24 text

Проверка сложности кода 2. Какие линтеры использовать nakedret: используется return без значений

Slide 25

Slide 25 text

Проверка стиля 2. Какие линтеры использовать gochecknoglobals: глобальная переменная

Slide 26

Slide 26 text

Проверка стиля 2. Какие линтеры использовать golint: лучше apiURL

Slide 27

Slide 27 text

Проверка стиля 2. Какие линтеры использовать gochecknoinits: функции init запрещены

Slide 28

Slide 28 text

Проверка стиля 2. Какие линтеры использовать gosimple: можно убрать if

Slide 29

Slide 29 text

Проверка стиля 2. Какие линтеры использовать goconst: дважды повторяется “http://”

Slide 30

Slide 30 text

Проверка стиля 2. Какие линтеры использовать misspell: опечатка: esle -> else

Slide 31

Slide 31 text

Проверка стиля 2. Какие линтеры использовать unconvert: лишняя конверсия: string(apiUrl) -> apiUrl

Slide 32

Slide 32 text

Поиск неиспользуемого 2. Какие линтеры использовать varcheck: переменная не используется

Slide 33

Slide 33 text

Поиск неиспользуемого 2. Какие линтеры использовать unused: поле unused не используется

Slide 34

Slide 34 text

Поиск неиспользуемого 2. Какие линтеры использовать deadcode: тип не используется

Slide 35

Slide 35 text

Поиск неиспользуемого 2. Какие линтеры использовать deadcode: функция не используется

Slide 36

Slide 36 text

Поиск неиспользуемого 2. Какие линтеры использовать unparam: аргумент unused не используется

Slide 37

Slide 37 text

Поиск неиспользуемого 2. Какие линтеры использовать ineffassign: результат изменения переменной не используется

Slide 38

Slide 38 text

Проверка производительности 2. Какие линтеры использовать maligned: переупорядочить поля структуры

Slide 39

Slide 39 text

Проверка производительности 2. Какие линтеры использовать gocritic: hugeParam: переменная типа heavyStruct копируется

Slide 40

Slide 40 text

Проверка производительности 2. Какие линтеры использовать prealloc: можно предаллоцировать

Slide 41

Slide 41 text

Поиск багов 2. Какие линтеры использовать scopelint: захват в range по ссылке

Slide 42

Slide 42 text

Поиск багов 2. Какие линтеры использовать staticcheck: гонка

Slide 43

Slide 43 text

Поиск багов 2. Какие линтеры использовать go vet: всегда true

Slide 44

Slide 44 text

Поиск багов 2. Какие линтеры использовать gosec: пользов-ский ввод передается в shell

Slide 45

Slide 45 text

Поиск багов 2. Какие линтеры использовать errcheck: нет проверки ошибки

Slide 46

Slide 46 text

Богатые проверками линтеры 2. Какие линтеры использовать 1. staticcheck (ex-megacheck) 2. go-critic

Slide 47

Slide 47 text

Так какие линтеры использовать? 2. Какие линтеры использовать

Slide 48

Slide 48 text

Каталог линтеров 2. Какие линтеры использовать https://github.com/golangci/ awesome-go-linters

Slide 49

Slide 49 text

Запуск нескольких линтеров 3. Как запускать линтеры pkgs = $(shell go list ./... | fgrep -v /vendor) lint: go get golang.org/x/lint/golint go get honnef.co/go/tools/cmd/staticcheck go get github.com/kisielk/errcheck golint $(pkgs) go vet $(pkgs) staticcheck $(pkgs) errcheck $(pkgs) Makefile для запуска 4-х линтеров

Slide 50

Slide 50 text

Параллельный запуск 3. Как запускать линтеры pkgs = $(shell go list ./... | fgrep -v /vendor) lint: go get golang.org/x/lint/golint go get honnef.co/go/tools/cmd/staticcheck go get github.com/kisielk/errcheck echo $(pkgs) | xargs -P4 golint echo $(pkgs) | xargs -P4 go vet echo $(pkgs) | xargs -P4 staticcheck echo $(pkgs) | xargs -P4 errcheck

Slide 51

Slide 51 text

Go Meta Linter 3. Как запускать линтеры $ gometalinter \ --disable-all \ --enable golint --enable vet \ --enable staticcheck \ --enable errcheck \ --deadline=1m \ --vendor \ ./...

Slide 52

Slide 52 text

golangci-lint 3. Как запускать линтеры $ golangci-lint run \ --disable-all \ -E golint,govet,staticcheck,errcheck

Slide 53

Slide 53 text

Сравнение производительности 3. Как запускать линтеры

Slide 54

Slide 54 text

Gometalinter 3. Как запускать линтеры https://github.com/alecthomas/gometalinter/ issues/590

Slide 55

Slide 55 text

Плюшки golangci-lint 3. Как запускать линтеры 1. Производительность 2. Удобная yaml конфигурация 3. Фильтрация предупреждений: --skip-dirs, --exclude 4. Исключения предупреждений в коде: //nolint

Slide 56

Slide 56 text

Конфигурация golangci-lint 3. Как запускать линтеры # https://github.com/golangci/golangci-lint/blob/ master/.golangci.example.yml linters-settings: goimports: local-prefixes: github.com/local/repo

Slide 57

Slide 57 text

Конфигурация на примере goimports 3. Как запускать линтеры import ( "github.com/local/repo/pkg" "github.com/pkg/errors" )

Slide 58

Slide 58 text

Конфигурация на примере goimports 3. Как запускать линтеры import ( "github.com/pkg/errors" "github.com/local/repo/pkg" )

Slide 59

Slide 59 text

Авто-исправление 3. Как запускать линтеры golangci-lint run --fix

Slide 60

Slide 60 text

Больше скорости. --fast 3. Как запускать линтеры $ golangci-lint help linters | fgrep "fast: false" 1. staticcheck 2. unused 3. gosimple 4. stylecheck 5. interfacer 6. unparam

Slide 61

Slide 61 text

Больше скорости. --fast 3. Как запускать линтеры $ time golangci-lint run real 0m17.577s $ time golangci-lint run --fast real 0m6.757s

Slide 62

Slide 62 text

Больше скорости. Build cache 3. Как запускать линтеры $ go clean -cache $ time golangci-lint run --fast real 0m17.533s $ time golangci-lint run --fast real 0m6.585s

Slide 63

Slide 63 text

Больше скорости. Build cache в CI 3. Как запускать линтеры # .travis.yml cache: directories: - $HOME/.cache/go-build - $HOME/gopath/pkg/mod

Slide 64

Slide 64 text

go/analysis 3. Как запускать линтеры https://godoc.org/golang.org/x/tools/ go/analysis

Slide 65

Slide 65 text

{ Выводы golangci-lint 3. Как запускать линтеры

Slide 66

Slide 66 text

Незначимые сообщения от линтеров 4. Внедрение в проект cache.go:7:6: exported type Cache should have comment or be unexported redis.go:13:6: exported type Redis should have comment or be unexported redis.go:17:1: exported function NewRedis should have comment or be unexported redis.go:33:1: exported method Redis.Get should have comment or be unexported redis.go:55:1: exported method Redis.Set should have comment or be unexported

Slide 67

Slide 67 text

Исключаем неважные для нас сообщения 4. Внедрение в проект # .golangci.yml issues: exclude: - should have comment or be unexported - another pattern Опция `exclude` исключает сообщения по их тексту по регулярке golangci-lint автоматически находит .golangci.yml

Slide 68

Slide 68 text

Исключаем по путям 4. Внедрение в проект # .golangci.yml run: skip-dirs: - pkg/examples skip-files: - ".*\\.generated\\.go$" golangci-lint автоматически исключает сгенерированны е файлы

Slide 69

Slide 69 text

Исключаем конкретные случаи 4. Внедрение в проект var bad_name int //nolint:golint var b_a_d int //nolint:golint,gofmt var v int //nolint //nolint:prealloc func f() { // ... } Можно исключать текущую строку или блок //nolint

Slide 70

Slide 70 text

Незначимые сообщения от линтеров 4. Внедрение в проект package logutils import ( "fmt" "os" "github.com/sirupsen/logrus" //nolint:depguard ) #https://github.com/golangci/golangci-lint/blob/master /pkg/logutils/stderr_log.go#L7

Slide 71

Slide 71 text

Нет времени исправлять тонну проблем 4. Внедрение в проект $ go vet main.go:5: missing argument for Sprintf("%s") main.go:6: missing argument for Sprintf("%s")

Slide 72

Slide 72 text

Нет времени исправлять тонну проблем 4. Внедрение в проект $ go vet main.go:5: missing argument for Sprintf("%s") main.go:6: missing argument for Sprintf("%s") $ go vet |& revgrep origin/master main.go:6: missing argument for Sprintf("%s")

Slide 73

Slide 73 text

Нет времени исправлять тонну проблем 4. Внедрение в проект $ golangci-lint run \ --new-from-rev=origin/master $ golangci-lint run --new

Slide 74

Slide 74 text

Добавление нового линтера 4. Внедрение в проект $ golangci-lint run $

Slide 75

Slide 75 text

Добавление нового линтера 4. Внедрение в проект $ golangci-lint run $ golangci-lint run \ --disable-all \ --enable newlinter --new-from-rev=origin/master

Slide 76

Slide 76 text

Воспроизводимость в CI 5. Удобство работы go get github.com/some/linter wget -O - -q \ https://github.com/some/linter/install.sh | sh -s v1.2.3 golangci-lint run --enable-all golangci-lint run --disable-all -Eerrcheck -E...

Slide 77

Slide 77 text

Pre-commit hook 5. Удобство работы # .pre-commit-config.yaml repos: - repo: https://github.com/golangci/golangci-lint rev: v1.16.0 hooks: - id: golangci-lint # pip install pre-commit # pre-commit install

Slide 78

Slide 78 text

--fast 5. Удобство работы. IDE

Slide 79

Slide 79 text

5. Удобство работы. CI

Slide 80

Slide 80 text

5. Удобство работы. VCS

Slide 81

Slide 81 text

1. GolangCI 2. Code Climate 3. Hound 5. Удобство работы. VCS 1. Reviewdog 2. SonarQube

Slide 82

Slide 82 text

goreportcard.com

Slide 83

Slide 83 text

1. Зачем нужны линтеры 2. 50+ линтеров: какие использовать 3. Запуск линтеров 4. Внедрение в проект 5. Удобство работы Что мы обсудили

Slide 84

Slide 84 text

Кратко как применить доклад 1. Попробуйте включить все линтеры в golangci-lint 2. Встройте golangci-lint в CI + IDE + pre-commit 3. Примените --new-from-rev для быстрой интеграции 4. Сделайте линтеры ревьюерами через Reviewdog/CodeClimate.com/GolangCI.com/...

Slide 85

Slide 85 text

Темы для дальнейшего обсуждения 1. Настройка .golangci.yml 2. Go/analysis и дальнейшее развитие линтеров 3. Разработка своего линтера 4. Страдания с gocyclo

Slide 86

Slide 86 text

Контакты t.me/d_isaev github.com/jirfag [email protected] linkedin.com/in/denis-isaev