Форматирование в Go: пошаговый разбор gofmt и его друзей
Go — один из немногих языков, где стиль кода не обсуждается: gofmt решает за всех. Разбираем, как именно работает встроенное форматирование, чем gofmt отличается от goimports, как встроить проверку в CI и что делать, когда результат не нравится.

Зачем Go форматирует код за вас
В большинстве языков стиль — предмет спора: табы или пробелы, скобки на той же строке или на следующей, пустая строка перед return или нет. В Go этого спора нет с 2009 года. Инструмент gofmt поставляется вместе с компилятором и задаёт единственный каноничный стиль. Код, не прошедший через gofmt, на код-ревью выглядит чужеродно, а многие линтеры и CI-пайплайны просто отклоняют его.
Ниже — разбор того, как gofmt устроен, чем дополняется, где его поведение может удивить и как автоматизировать форматирование так, чтобы о нём больше не думать.
gofmt: что он делает и чего не делает
gofmt — это не линтер и не статический анализатор. Он работает на уровне синтаксического дерева (AST): парсит файл, перестраивает дерево в каноничное представление и записывает обратно. Именно поэтому gofmt детерминирован — один и тот же исходник всегда даёт один и тот же результат, независимо от платформы и версии Go[1].
Что gofmt контролирует
- Отступы — всегда табуляция, не пробелы.
- Выравнивание — столбцы в структурах, присваиваниях и комментариях выравниваются табуляцией через алгоритм tabwriter.
- Скобки — открывающая фигурная скобка на той же строке, что и оператор.
- Пустые строки — лишние схлопываются, но одна пустая строка между логическими блоками сохраняется.
- Пробелы вокруг операторов и после запятых.
Чего gofmt не трогает
- Порядок импортов — этим занимается
goimports. - Именование переменных и функций.
- Длину строки — жёсткого лимита нет, gofmt не переносит длинные выражения автоматически.
- Логику кода — gofmt не удаляет мёртвый код, не упрощает условия.
gofmt, go fmt и goimports — кто есть кто
Новичков часто путает, что существуют три похожие команды. Разница принципиальна.
| Инструмент | Что делает | Когда использовать |
|---|---|---|
gofmt | Форматирует один или несколько файлов. Поддерживает флаг -s для упрощения кода. | Ручной запуск или хуки, где нужен контроль над флагами. |
go fmt | Обёртка: вызывает gofmt -l -w для всех .go-файлов в пакете. | Быстрый запуск по пакету: go fmt ./... |
goimports | Всё, что делает gofmt, плюс добавляет недостающие импорты, удаляет лишние и группирует их. | Рекомендуется как замена gofmt в редакторе и CI. |
goimports не входит в стандартную поставку Go. Установка (Go 1.21+):
go install golang.org/x/tools/cmd/goimports@latest
После этого бинарник появится в $GOPATH/bin (или $HOME/go/bin, если GOPATH не задан). Убедитесь, что этот каталог в вашем PATH.
Флаг -s: упрощение, о котором забывают
Флаг -s у gofmt выполняет набор безопасных упрощений синтаксиса. Он не меняет семантику, но убирает избыточные конструкции. Вот конкретные примеры:
// До: явный тип элемента в литерале слайса
points := []Point{Point{1, 2}, Point{3, 4}}
// После gofmt -s: тип элемента убран, компилятор выводит его сам
points := []Point{{1, 2}, {3, 4}}
// До: диапазон с неиспользуемым индексом
for _ = range items {
process()
}
// После gofmt -s: underscore убран
for range items {
process()
}
Команда go fmt флаг -s не передаёт по умолчанию. Если хотите упрощение — вызывайте gofmt напрямую:
gofmt -s -w .
gofmt не предмет обсуждения. Стиль gofmt — это не чей-то любимый стиль, а ничей. Почти все находят его «не совсем таким, как привыкли», зато ни один проект не тратит время на споры о форматировании.
— из официального FAQ проекта Go
Интеграция в редактор
Форматирование при сохранении файла — единственный надёжный способ не забывать о нём. Настройка зависит от редактора.
VS Code
Расширение Go (от Go Team at Google) форматирует по умолчанию через gopls, который внутри использует goimports. Убедитесь, что в settings.json есть:
{
"editor.formatOnSave": true,
"go.formatTool": "goimports"
}
Проверка: откройте любой .go-файл, добавьте лишний пробел, сохраните. Если пробел исчез — работает.
GoLand / IntelliJ
В GoLand gofmt вызывается через File Watchers или встроенную функцию Reformat Code (Ctrl+Alt+L). Чтобы использовать именно goimports, добавьте File Watcher: Settings → Tools → File Watchers → + → go fmt, в поле Program укажите путь к goimports.
Vim / Neovim
Через gopls и встроенный LSP-клиент, либо через плагин vim-go, где форматирование при сохранении включено по умолчанию.
Проверка в CI: не форматировать, а ловить
В CI нет смысла форматировать код — там нужно поймать неформатированный и завалить билд. Стандартный приём:
# CI-скрипт (bash)
# Проверяем, есть ли файлы, которые gofmt изменил бы
unformatted=$(gofmt -l .)
if [ -n "$unformatted" ]; then
echo "Следующие файлы не отформатированы:"
echo "$unformatted"
exit 1
fi
Флаг -l выводит только имена файлов, которые отличаются от канонического формата. Флаг -d покажет diff — удобно для отладки, но в CI лучше ограничиться списком, чтобы лог не раздувался на больших проектах.
Для проверки импортов добавьте аналогичную проверку через goimports -l ..
Когда gofmt раздражает
Два случая, которые вызывают больше всего вопросов:
Длинные строки
gofmt не переносит строки. Вызов функции с десятью аргументами останется в одну строку, если вы его так написали. Чтобы gofmt разнёс аргументы по строкам, поставьте перенос строки после открывающей скобки — дальше форматировщик подхватит структуру и выровняет каждый аргумент.
// Одна строка — gofmt не тронет
result := doSomething(ctx, userID, orderID, amount, currency, timestamp, callback)
// Стоит перенести первый аргумент — gofmt выровняет остальные
result := doSomething(
ctx,
userID,
orderID,
amount,
currency,
timestamp,
callback,
)
Обратите внимание на запятую после последнего аргумента — без неё компилятор Go потребует закрывающую скобку на той же строке.
Выравнивание комментариев в структурах
gofmt выравнивает комментарии табами, и при большой разнице в длине полей результат может выглядеть рвано. Решения нет: это цена детерминированности. Совет — не писать комментарии на каждой строке структуры, а вынести описание полей в godoc над типом.
gofumpt: строже, чем gofmt
Для команд, которым стандартного стиля мало, существует gofumpt — надстройка, совместимая с gofmt, но добавляющая дополнительные правила: удаление лишних пустых строк внутри функций, единообразие группировки импортов, запрет пустых строк в начале и конце блока. Установка:
go install mvdan.cc/gofumpt@latest
В VS Code достаточно поменять настройку:
"gopls": {
"formatting.gofumpt": true
}
Важно: gofumpt — сторонний инструмент (автор — Daniel Martí). Он стабилен и широко используется, но не является частью официального Go. Убедитесь, что вся команда согласна на его правила, прежде чем включать в CI.
Что делать дальше
Форматирование — первый слой автоматической гигиены кода. Следующие шаги:
- Добавьте
staticcheckилиgolangci-lintв CI — они ловят логические ошибки, а не только стиль. - Настройте pre-commit хук, который вызывает
goimports -lдо коммита — так неформатированный код не попадёт даже в локальный репозиторий. - Зафиксируйте версию gofumpt (если используете) в Makefile или Taskfile проекта, чтобы у всех разработчиков был одинаковый результат.
Мерить здесь нечего — форматирование либо единообразно, либо нет. Единственная метрика: ноль файлов в выводе gofmt -l . на каждом коммите.
[1] AST (Abstract Syntax Tree) — абстрактное синтаксическое дерево, промежуточное представление исходного кода в виде древовидной структуры, где каждый узел соответствует конструкции языка (объявление, выражение, оператор). gofmt парсит файл в AST, а затем печатает дерево обратно в текст по фиксированным правилам — именно поэтому результат всегда одинаков. ↩
Логотип и фирменный стиль: Три концепции разного характера, палитра с обоснованием, проверка на читаемость в малом размере и в один цвет.
Спросить за 60 ₽Источники: Команда gofmt — официальная документация Go, goimports — golang.org/x/tools
Частые вопросы
Чем go fmt отличается от gofmt?
Обязательно ли использовать goimports вместо gofmt?
Можно ли настроить стиль gofmt под свой проект — например, использовать пробелы вместо табов?
У меня другая версия Go — gofmt будет форматировать иначе?
CI упал на проверке gofmt, но у меня локально всё отформатировано. Что делать?
gofmt не переносит длинные строки. Есть ли линтер, который заставит это делать?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.