Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to cook linters

How to cook linters

Iskander (Alex) Sharipov

April 13, 2019
Tweet

More Decks by Iskander (Alex) Sharipov

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  49. Запуск нескольких линтеров
    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-х линтеров

    View Slide

  50. Параллельный запуск
    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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  66. Незначимые сообщения от линтеров
    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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  70. Незначимые сообщения от линтеров
    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

    View Slide

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

    View Slide

  72. Нет времени исправлять тонну проблем
    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")

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  76. Воспроизводимость в 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...

    View Slide

  77. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  82. goreportcard.com

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide