Анализ6 мин чтения2026-03-06

Golang linter: как настроить статический анализ кода правильно

Если ты работаешь на Go, рано или поздно встанет вопрос: как уловить баги и проблемы в коде до того, как они попадут в production? Ручная проверка на code revie

Если ты работаешь на Go, рано или поздно встанет вопрос: как уловить баги и проблемы в коде до того, как они попадут в production? Ручная проверка на code review — это медленно. Тесты ловят логические ошибки, но не стиль и не потенциальные уязвимости. Нужен инструмент, который будет смотреть код за тебя.

Это и делает golang linter.

Что вообще такое linter и зачем он нужен

Короче, linter — это автоматический инспектор кода. Он сканирует исходник и ищет проблемы без запуска программы. Это не компилятор и не тестовый фреймворк. Компилятор проверяет синтаксис. Линтер смотрит глубже.

Есть два подхода в анализе кода: статический и динамический. Их часто путают.

Статический анализ — это когда инструмент читает исходный код и ищет проблемы, не запуская программу. Именно это делает golang linter. Вот что он может найти:

Динамический анализ — это когда программу запускают и смотрят, что происходит во время выполнения. Используют профайлеры, детекторы утечек памяти, race detector в Go. Но это не линтер.

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

golangci-lint: де-факто стандарт

На рынке есть несколько инструментов (gofmt, go vet, golint), но в production используют один: golangci-lint.

Это не один линтер, а оркестр из 60+ линтеров. Приходит одна команда — а она запускает:

И ещё 50+ других. Каждый специализируется на своём.

Установка простая:

curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin latest

Или через homebrew на макосе:

brew install golangci-lint

Запустить:

golangci-lint run ./...

Вот что ты увидишь в выводе:

main.go:5:2: `unused` is a unused variable (varcheck)
main.go:10:1: exported function `ProcessData` should have comment or be unexported (golint)
main.go:15:5: possible nil pointer dereference (vet)

Каждая строка — это потенциальная проблема. Линтер говорит: файл, строка, столбец, описание, какой именно линтер это нашёл.

Конфигурация: как не запустить 60 линтеров подряд

Если запустить golangci-lint с дефолтными настройками на реальном проекте, упадёшь в вывод на 500 строк. Половина из них — ложноположительные. Нужно настроить, какие линтеры включать.

Создай в корне проекта файл .golangci.yml:

run:
  timeout: 5m
  tests: true
  skip-dirs:
    - vendor
    - mocks
    - generated

linters:
  enable:
    - gofmt
    - goimports
    - govet
    - errcheck
    - staticcheck
    - gosec
    - ineffassign
    - unconvert
    - unparam
    - unused
  disable:
    - golint  # deprecated, use revive instead

linters-settings:
  errcheck:
    check-type-assertions: true
    check-blank: false

  goimports:
    local-prefixes: github.com/mycompany

  staticcheck:
    go: "1.20"

  gosec:
    severity: medium
    confidence: medium

issues:
  exclude-rules:
    - path: _test\.go
      linters:
        - errcheck
    - path: cmd/
      text: "should have comment or be unexported"

Вот что здесь происходит:

run — общие настройки. Timeout в 5 минут, чтобы линтер не висел. tests: true — анализировать и тестовые файлы. skip-dirs — пропускать папки (вендор, генерированный код).

linters — какие включить. Я выбрал основной набор, который ловит 95% проблем и не даёт много false positives. Есть специфичные линтеры вроде dupl (поиск дублирующегося кода) или gocritic (сложные проверки), но они требуют настройки.

linters-settings — тонкая настройка каждого линтера. Например, errcheck проверяет, что ты не забыл обработать ошибку. check-type-assertions включает проверку type assertions.

issues — исключения. Например, в тестовых файлах (_test.go) не требуем обработку ошибок везде, потому что это ненужно.

Честно? Лучше начать с минимального набора линтеров, а потом добавлять. На одном проекте мы в спешке включили 50 линтеров и потом неделю разбирались, почему CI падает.

Как встроить в CI/CD pipeline

Линтер локально — это хорошо, но нужно чтобы он запускался автоматически в CI. Иначе разработчик просто не запустит его перед пушем.

Вот пример для GitHub Actions:

name: Lint

on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - uses: actions/setup-go@v4
        with:
          go-version: '1.20'
      
      - uses: golangci/golangci-lint-action@v3
        with:
          version: latest
          args: --timeout=5m

Для GitLab CI:

lint:
  stage: test
  image: golangci/golangci-lint:latest
  script:
    - golangci-lint run ./...
  allow_failure: false

Для Gitea (у них свой CI):

test-lint:
  image: golangci/golangci-lint:latest
  commands:
    - golangci-lint run ./...

Теперь каждый MR/PR будет проверяться автоматически. Если линтер найдёт проблемы — CI упадёт, и разработчик должен будет их исправить перед мержем.

Когда линтер ошибается: как игнорировать false positives

Бывает, что линтер ошибается. Например, говорит, что функция не используется, хотя она вызывается через reflection. Или требует обработать ошибку, которая по логике не может произойти.

Есть несколько способов игнорировать:

Inline комментарий — прямо в коде:

func processData(data []byte) {
    // nolint:errcheck
    ioutil.WriteFile("output.txt", data, 0644)
}

Правило в конфиге — для целого класса проблем:

issues:
  exclude-rules:
    - text: "G104"  # Errors unhandled
      paths:
        - cmd/main.go

Отключение линтера для строк:

//nolint:all
var unusedVar = 42

Но честно — если часто игнорируешь линтер, значит либо конфиг неправильный, либо в коде действительно проблемы.

Сравнение подходов: локально vs CI vs IDE

Локально (перед коммитом):

golangci-lint run ./...

Плюс: ловишь ошибки сразу, не тратишь время на CI. Минус: зависит от ответственности разработчика.

В CI/CD (автоматически):

Плюс: гарантирует, что ничто не пройдёт в main. Минус: feedback медленнее, разработчик уже закончил работу.

В IDE (в реальном времени):

Для VS Code есть плагин Go от Google, который интегрирует golangci-lint прямо в редактор. Ошибки подсвечиваются красным прямо при печати.

Правильный подход — все три. Локально быстро поймёшь ошибку. IDE подсвечивает при написании. CI гарантирует.

Сравнение с другими инструментами

На рынке есть альтернативы:

go vet — встроенный инструмент Go. Быстрый, но ловит только серьёзные ошибки. Недостаточно.

gofmt — форматирование кода. Это не анализ, это просто приведение в порядок. Используй goimports вместо него.

revive — более лёгкая альтернатива golangci-lint. Быстрее, но меньше проверок. Подходит для микросервисов.

Платные инструменты вроде SonarQube или Codacy имеют красивые UI и историю изменений, но для стартапа это дорого.

По моему опыту, golangci-lint — это sweet spot. Один раз настроишь, и забудешь.

Практический совет: как внедрить в существующий проект

Если прибиваешь линтер к старому проекту, сразу включишь его на 1000 ошибок. Разработчики будут в шоке.

Правильный путь:

  1. Запусти линтер и собери все ошибки:
golangci-lint run ./... > lint-issues.txt
  1. Включи в конфиг опцию max-issues-per-linter: 10000 временно.

  2. Добавь в CI как allow_failure: true — CI не падает, но видишь результаты.

  3. За неделю-две разработчики постепенно исправляют ошибки.

  4. Как уменьшилось количество — убираешь allow_failure, линтер становится обязательным.

Если сразу включить на полную, проект взорвётся в конфликтах.

Что дальше?

Линтер — это первая линия защиты. После него нужны:

Но без линтера не обойтись.

Если хочешь ещё больше автоматизации — обрати внимание на инструменты вроде Distiq. Это AI-бот, который делает code review автоматически, анализирует не только стиль, но и архитектуру, логику, потенциальные баги. Работает с Go, Python, JavaScript и другими языками. Интегрируется в GitHub, GitLab и GitVerse за две минуты. Особенно полезно в больших командах, где code review становится узким местом.

Попробуйте Distiq для автоматического code review

AI-бот анализирует каждый MR/PR и оставляет комментарии с замечаниями. Интеграция за 2 минуты.

Попробовать бесплатно

Похожие статьи