Светофор качества Go-кода
Код, который неудобно читать, ломается не сразу: он сначала становится медленнее в разработке, а потом — дороже в поддержке. Проверять это на глаз неудобно, а линтер вроде golangci-lint требует установки и настройки. Здесь — быстрый разбор по эвристикам, которые замечает любой опытный ревьюер: размер функции, число аргументов, глубина вложенности, дубли и опасные вызовы.
Что считает «хорошим» код в Go
В Go есть официальный документ Effective Go, и в нём сказано почти всё, что нужно знать о простоте: пишите короткие функции, называйте вещи прямо, возвращайте ошибки вместо паник, не прячьте управляющий поток в сложных условиях. Но документ читают один раз, а код пишут каждый день — нужен способ посмотреть на свой код со стороны.
Эвристики ниже — это те же правила, переведённые в проверки. Размер функции сравнивается с порогом в 40 и 80 строк. Количество аргументов — с четырьмя и шестью. Глубина вложенности — с тремя и четырьмя уровнями. Сверх этого ищутся длинные строки, повторяющиеся блоки, опасные вызовы (panic, log.Fatal, os.Exit, time.Sleep) и ошибки, которые присваиваются, но не проверяются.
Важно понимать границы: это не линтер и не замена go vet. Анализатор не знает вашего проекта, не понимает бизнес-логику и может ругаться на код, который в вашем случае уместен. Его задача — подсветить места, где стоит посмотреть глазами, и объяснить, почему это обычно плохо.
Вставьте Go-код
Анализ идёт прямо в браузере: код никуда не отправляется. Достаточно функции или фрагмента — например, обработчика запроса.
Какие эвристики используются
- Размер функции. Больше 40 строк — жёлтый, больше 80 — красный. Функция, которая делает несколько дел, трудно тестировать и переиспользовать.
- Количество аргументов. Больше четырёх позиционных параметров — повод собрать их в структуру: иначе ошибку в порядке аргументов не поймает компилятор.
- Глубина вложенности. Глубже трёх уровней — сигнал к раннему return или к выносу ветки в отдельную функцию.
- Длинные строки и дублирование. Строки длиннее 140 символов и повторяющиеся блоки кода — источник неудобного чтения и расхождения копий.
- Опасные вызовы. panic, log.Fatal, os.Exit, time.Sleep и присваивание в пустую переменную — типичные источники проблем в сервисном коде.
Это эвристики, а не линтер: они не знают контекста проекта и не знают, что делает код. Зато они работают мгновенно, без установки чего-либо, и подсказывают направление, в котором стоит копать. Для строгой проверки есть go vet и golangci-lint.
Частые вопросы о качестве кода в Go
Чем этот анализатор отличается от go vet и golangci-lint?
go vet проверяет корректность: неверные глаголы в Printf, потерянные копии мьютексов, подозрительные конструкции. golangci-lint собирает десятки линтеров и требует установки. Здесь работают эвристики читаемости: размер функции, число аргументов, вложенность, дублирование, длинные строки, опасные вызовы. Это другой класс замечаний — не «код неправильный», а «код будет трудно поддерживать».
Почему длинная функция — это проблема?
Функция длиннее 40-80 строк обычно делает несколько дел: читает, проверяет, преобразует, пишет. Такую функцию трудно покрыть тестами (нужно подготовить много входных данных ради одного результата), и при изменении требований легко сломать соседнюю ветку. Практическое правило: если функцию нельзя описать одним предложением без «и», стоит разбить.
Что делать с замечаниями про panic и log.Fatal?
В прикладном коде бизнес-ошибки возвращают через error — тогда вызывающий код может их обработать. panic оставляют для нарушенных инвариантов, которые восстановить нельзя, обычно при старте. log.Fatal и os.Exit в библиотечном коде особенно неприятны: процесс умирает без выполнения defer, и это ломает graceful shutdown у сервиса, который вызывающий код рассчитывал остановить сам.
Анализатор отправляет мой код на сервер?
Нет. Разбор идёт в браузере, код никуда не уходит и нигде не сохраняется. Страница статическая, внешних запросов на ней нет.
Что изучить дальше
- Правила и идиомы GoEffective Go на практике: именование, обработка ошибок, простота кода.
- Тестирование в GoTable-driven тесты, t.Run и покрытие — чем проверяют качество кода.
- Светофор качестваЭвристики простоты: длина функции, аргументы, вложенность, дубли.
- Гайд по pprofКак измерить производительность, чтобы оптимизировать по фактам.
Что внутри
Что именно проверяет анализатор
Пять групп сигналов: длина функции и число аргументов (функция с шестью аргументами почти всегда просит объект), глубина вложенности (условные ветки внутри условных веток), длинные строки и функции, дублирующиеся блоки, а также дорогие конструкции: panic и log.Fatal в серверном коде, потерянная ошибка, sleep вместо ожидания сигнала.
Каждое срабатывание объясняется: не «плохой код», а конкретное правило, которое в Go чаще нарушают, и как это выглядит в коде.
Сложность — не самоцель
Длинная функция не всегда плохая: линейный разбор пятисот строк читается лучше, чем десять функций по пятьдесят строк с общим контекстом. Поэтому анализатор отмечает не размер, а признаки, которые коррелируют с реальными проблемами: много аргументов, глубокая вложенность, смешение уровней абстракции в одной функции.
Отдельно он смотрит на ошибки в обработке: вызов, который возвращает err, но результат используется без проверки, — это не стилистическая придирка, а способ потерять ошибку по дороге. Тот же смысл у panic и log.Fatal в серверном коде: они завершают процесс, и в воркере с очередью это означает потерю всех задач разом.
Что анализатор не ловит
Логические ошибки, неверную архитектуру, отсутствие тестов и проблемы в предметной области. Инструмент работает с формой кода, а не с его смыслом: он скажет, что функция делает слишком много, но не скажет, что она делает не то. Проверку «правильно ли это» делают тесты и ревью — автоматика берёт на себя только то, что можно посчитать.
Как пользоваться
- Вставьте одну функцию, а не файл. Анализатор показывает, что именно в этой функции подозрительно, и не комментирует соседей.
- Разбирайте срабатывания по порядку. Потерянная ошибка важнее длинной строки: сначала то, что ломает поведение, потом то, что мешает читать.
- Проверяйте себя на знакомом коде. Возьмите свой рабочий обработчик: если анализатор находит в нём десять проблем, а он у вас три года в проде, значит, правила слишком строгие — не чините код, смягчайте правила.
Когда это пригождается
Рефакторинг: с чего начать
Начните с функции с наибольшим числом аргументов и с самой глубокой вложенностью: это обычно граница, где код хочет стать объектом или разделиться на шаги.
Ревью чужого pull request
Список срабатываний — повод задать автору вопрос про конкретное место, а не спор о вкусе.
Тест не ловит баг
Если в функции шесть аргументов и три уровня вложенности, тест наверняка строит не те данные. Разбиение функции обычно чинит и тест.
Анализатор эвристический: он смотрит на форму кода, а не на смысл. Срабатывание — повод посмотреть, а не приговор.