Skip to content

Instantly share code, notes, and snippets.

@lexfrei
Created March 17, 2026 10:00
Show Gist options
  • Select an option

  • Save lexfrei/d3caec76036cf9c11b9d56d2d427d15f to your computer and use it in GitHub Desktop.

Select an option

Save lexfrei/d3caec76036cf9c11b9d56d2d427d15f to your computer and use it in GitHub Desktop.
CozyPortal: план починки линтера (7 шагов)

План починки линтера: 7 шагов

У нас 334 ошибки линтера после обновления golangci-lint до v2.10.1 со строгим конфигом. Работа разбита на 7 PR'ов, от безопасного к рискованному. Каждый шаг — отдельный PR, который можно ревьюить и откатывать независимо.


Шаг 1. Конфиг и автоформатирование

https://github.com/aenix-org/cozyportal/issues/273

Линтеры:

  • gofmt (~3 ошибки)

Фундамент для всех остальных шагов. Применяем строгий .golangci.yml — убираем ~60 path-based exclusions, которые подавляли линтеры в production-коде. Запускаем автоформатирование.

Ценность: без этого шага остальные PR'ы будут показывать ложные ошибки. Конфиг — single source of truth для всей команды.

Риски: минимальные. gofmt — чисто косметические изменения, семантика кода не меняется. Но удаление path-exclusions "откроет" все 334 ошибки — до этого они были подавлены.

Важно: несмотря на смену конфига линтера, предлагается до последнего шага не требовать от команды соблюдения линтинга за пределами затронутого ими кода. Пока lint fixes в процессе, CI lint job будет красным — это ожидаемо и не должно блокировать работу.


Шаг 2. Порядок функций, пробелы, точки в комментариях

https://github.com/aenix-org/cozyportal/issues/274

Линтеры (~78 ошибок):

  • funcorder (50) — порядок функций: public перед private
  • wsl_v5 (21) — пустые строки между логическими блоками
  • nlreturn (5) — пустая строка перед return
  • godot (2) — точка в конце комментариев

Механические правки без изменения логики: перестановка функций, добавление/удаление пустых строк, точки в конце комментариев.

Ценность:

  • funcorder — единообразная структура файлов, проще навигация
  • wsl/nlreturn — визуальное разделение блоков логики, читаемость
  • godot — корректный godoc

Риски: нулевые по логике, но большой diff (перестановка функций). git blame будет показывать этот коммит — это цена, которую платим один раз.


Шаг 3. Имена переменных, godoc, nolint директивы

https://github.com/aenix-org/cozyportal/issues/275

Линтеры (~56 ошибок):

  • varnamelen (34) — слишком короткие имена переменных
  • godoclint (16) — отсутствие godoc на экспортируемых типах
  • nolintlint (6) — некорректные nolint директивы

Переименование коротких переменных (sserver, rreq), добавление godoc на экспортируемые типы, исправление некорректных nolint директив.

Ценность:

  • varnamelen — однобуквенные переменные непонятны вне контекста, особенно в длинных функциях
  • godoclint — публичный API должен быть задокументирован
  • nolintlint — каждое подавление линтера должно объяснять ПОЧЕМУ (иначе это техдолг, который никто не вспомнит)

Риски: низкие. Переименование переменных может конфликтовать с открытыми PR'ами. Некоторые короткие имена оправданы конвенцией (ctx, err, ok) — их добавляем в ignore-names, а не переименовываем.


Шаг 4. Revive и константы

https://github.com/aenix-org/cozyportal/issues/276

Линтеры (~55 ошибок):

  • revive (50) — мета-линтер: stuttering names, unused params, indent-error-flow
  • goconst (5) — повторяющиеся строковые литералы

Revive — это мета-линтер с ~40 правилами: stuttering names (console.ConsoleImageconsole.Image), unused parameters, missing docs, indent-error-flow. Goconst — повторяющиеся строковые литералы выносятся в константы.

Ценность:

  • revive — покрывает Go code review guidelines, то что ревьюер должен ловить руками. Stuttering names — прямое нарушение Effective Go
  • goconst — повторяющиеся литералы это time bomb: меняешь одну, забываешь вторую

Риски: средние. Переименование типов — breaking change для внешних потребителей API. Нужно проверять, что переименования не ломают генерированный клиентский код и OpenAPI спеки.


Шаг 5. Статический анализ и оптимизации

https://github.com/aenix-org/cozyportal/issues/277

Линтеры (~42 ошибки):

  • gocritic (30) — hugeParam, unnecessaryDefer, typeSwitchVar
  • perfsprint (6) — замена fmt.Sprintf на strconv
  • prealloc (3) — аллокация слайсов с известной ёмкостью
  • staticcheck (3) — deprecated API, мёртвый код

Ценность:

  • gocritic — ловит реальные баги (hugeParam для больших struct'ов — копирование вместо ссылки)
  • staticcheck — deprecated API сломается при обновлении зависимостей
  • perfsprint/prealloc — микрооптимизации, но в hot paths имеют значение

Риски: средние. Замена value на pointer receiver может изменить семантику (мутабельность). hugeParam fixes нужно проверять через тесты.


Шаг 6. Дедупликация и декомпозиция

https://github.com/aenix-org/cozyportal/issues/278

Линтеры (~50 ошибок):

  • dupl (27) — дублированные блоки кода
  • funlen (12) — функции длиннее 60 строк
  • gocyclo (6) — цикломатическая сложность > 15
  • gocognit (4) — когнитивная сложность > 15
  • cyclop (1) — комплексная сложность пакета

Самый трудоёмкий шаг. Дублированный код выносится в общие хелперы. Длинные функции разбиваются на более мелкие. Снижается цикломатическая и когнитивная сложность.

Ценность:

  • dupl — дублирование это главный источник рассинхронизации: правишь баг в одном месте, забываешь второе
  • funlen/gocyclo/gocognit — функции >60 строк или с complexity >15 невозможно ревьюить и тестировать. Это прямо коррелирует с количеством багов

Риски: высокие. Рефакторинг изменяет control flow. Каждый вынос хелпера — потенциальный regression. Обязательно покрыть unit-тестами ДО рефакторинга, потом проверить что тесты остались зелёными.


Шаг 7. Обработка ошибок, глобалы, остаток

https://github.com/aenix-org/cozyportal/issues/280

Линтеры (~68 ошибок):

  • gochecknoglobals (13) — глобальные переменные
  • wrapcheck (11) — ошибки без контекста (unwrapped)
  • mnd (11) — magic numbers
  • godox (9) — TODO/FIXME в коде
  • err113 (7) — динамические ошибки вместо sentinel
  • tagliatelle (4) — несоответствие стиля struct tags
  • errcheck (3) — игнорируемые ошибки
  • noinlineerr (3) — inline error handling
  • inamedparam (3) — неименованные параметры в интерфейсах
  • nestif (2) — вложенные if'ы
  • errchkjson (1) — непроверенный JSON marshal
  • forcetypeassert (1) — type assertion без проверки

Сборная солянка: оборачивание ошибок контекстом, sentinel errors вместо динамических, устранение глобальных переменных, замена magic numbers на константы, удаление TODO/FIXME.

Ценность:

  • wrapcheck/err113 — без контекста в ошибках невозможно дебажить production. "connection refused" — где? какой сервис?
  • gochecknoglobals — глобальное состояние ломает тестируемость и параллелизм
  • mndif timeout > 30 — 30 чего? секунд? минут?
  • godox — TODO в коде = забытая задача; должна быть в трекере

Риски: высокие. wrapcheck меняет error messages (может сломать тесты, которые проверяют конкретные строки). err113 требует создания sentinel errors — меняет публичный API пакетов. gochecknoglobals может потребовать dependency injection рефакторинг.


Общие принципы

  • Каждый шаг — отдельный PR, ревьюится и мержится независимо
  • Порядок от безопасного к рискованному: если шаг 6 зависнет на ревью, шаги 1–5 уже приносят пользу
  • Группировка по типу изменений: легче ревьюить "50 перестановок функций" чем "3 перестановки + 2 переименования + 1 рефакторинг" в одном PR
  • Все тесты должны проходить после каждого шага
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment