Go-джентльмены: Code Style и конвенции для командной работы

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему даже гениальный код может провалить проект?

Представьте: вы пишете на Go. Код работает быстро, тесты проходят, а продакшен радует стабильностью. Но в понедельник приходит новый разработчик, открывает ваш модуль и… впадает в ступор. Переменные называны x1, data2, tmp. Функции — func1, process. Импорты разбросаны хаотично. Код скомпилируется, но понять его — задача со звёздочкой.

Знакомая ситуация? Если вы работаете в команде, code style и конвенции для Go — это не прихоть линтера, а ваш общий язык. Без него даже самый талантливый разработчик превращается в «соло-игрока», который тормозит всю команду. В этой статье мы разберём, как писать на Go так, чтобы ваш код читали, понимали и любили коллеги. А заодно — как не попасть в ловушку «быстро, но грязно».

Go — это про простоту. Но простоту нужно договаривать

Язык Go был создан с мыслью о командной работе. Не зря Роб Пайк и Кен Томпсон закладывали в него идею «читаемости превыше всего». Однако даже в Go можно написать код, который будет работать, но вызывать желание переписать его с нуля.

Давайте разберём ключевые блоки, которые превращают хаос в стройную систему.

1. gofmt — ваш первый друг и соратник

В Go есть встроенный инструмент форматирования — gofmt. Это не просто «красивости». Это стандарт. Если вы не используете gofmt — вы нарушаете конвенцию. Точка.

Пример: представьте, что один разработчик ставит отступы табами, другой — пробелами. В Go эта проблема решена раз и навсегда. gofmt приводит код к единому виду. Запускайте его перед каждым коммитом. Лучше — настройте pre-commit hook.

// Плохо (без gofmt)
func add(a int,b int) int {
return a+b
}

// Хорошо (после gofmt)
func add(a, b int) int {
    return a + b
}

Совет: В современных IDE (VS Code, GoLand) gofmt запускается автоматически при сохранении. Проверьте настройки — это сэкономит часы code review.

2. Нейминг: говорите на языке предметной области

Go поощряет короткие имена. Но короткие — не значит бессмысленные. Правило простое: чем больше область видимости, тем длиннее имя.

  • Локальные переменные (внутри функции) — 1-2 символа: i, j, s, err.
  • Параметры функции — осмысленные, но краткие: ctx, req, db.
  • Экспортируемые функции и типы — полные имена: CreateUser, UserService.

Плохой пример:

func p(u string, pwd string) bool {
    return u == "admin" && pwd == "12345"
}

Хороший пример:

func IsValidCredentials(username, password string) bool {
    return username == "admin" && password == "12345"
}

Обратите внимание: IsValidCredentials — это не просто имя, это документация. Другой разработчик сразу поймёт, что делает функция, не заглядывая в тело.

3. Импорты: порядок — залог спокойствия

В Go импорты делятся на три группы:

  1. Стандартная библиотека (fmt, net/http, os).
  2. Внешние пакеты (github.com/xxx).
  3. Внутренние пакеты (ваш проект).

Между группами — пустая строка. Это негласное, но строгое правило. gofmt его не проверяет, но code review — обязательно.

import (
    "fmt"
    "os"

    "github.com/gorilla/mux"
    "github.com/lib/pq"

    "yourproject/internal/config"
    "yourproject/internal/handler"
)

Почему это важно? Когда импорты разбросаны, вы тратите время на поиск пакета. Единый порядок ускоряет чтение кода.

4. Комментарии: не пишите очевидное

Хороший код говорит сам за себя. Комментарии нужны для объяснения «почему», а не «что». Если вы пишете // прибавляем 1 к i — вы зря тратите время. Лучше назовите переменную counter.

В Go есть золотое правило: каждый экспортируемый элемент (функция, тип, константа) должен иметь комментарий. И он должен начинаться с имени этого элемента.

// UserService управляет операциями с пользователями.
type UserService struct {
    repo UserRepository
}

// CreateUser создаёт нового пользователя и возвращает его ID.
func (s *UserService) CreateUser(ctx context.Context, name, email string) (int, error) {
    // ...
}

Такой комментарий — это документация, которую видит godoc. Коллеги скажут вам спасибо.

Конвенции, которые спасают от багов

5. Обработка ошибок: не игнорируйте, а оборачивайте

В Go нет исключений. Ошибки — это значения. И их нужно обрабатывать явно. Плохая практика — игнорировать ошибку с помощью _.

Правильный подход: если функция может вернуть ошибку, проверяйте её. Если ошибку нужно передать выше — оборачивайте её с контекстом.

// Плохо
data, _ := ioutil.ReadFile("config.json")

// Хорошо
data, err := ioutil.ReadFile("config.json")
if err != nil {
    return fmt.Errorf("read config: %w", err)
}

Оборачивание через %w позволяет использовать errors.Is и errors.As — это мощный механизм для проверки типов ошибок на верхних уровнях.

6. Паника — это не ошибка

Паника в Go предназначена для truly exceptional ситуаций: разыменование нулевого указателя, выход за границы массива. Не используйте панику для обработки бизнес-логики. Если что-то пошло не так — возвращайте ошибку.

// Плохо
func Divide(a, b int) int {
    if b == 0 {
        panic("division by zero")
    }
    return a / b
}

// Хорошо
func Divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

Помните: паника в горутине может обрушить всю программу. Ошибки — безопаснее и предсказу

Блоги

Рекомендаций по книгам