Как писать чистый код на Go: 7 правил для профи

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

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

Начать курс

Введение: почему чистый код на Go — это не роскошь, а необходимость

Go (или Golang) — язык, который с первого взгляда кажется простым и даже скучным. В нём нет дженериков (до версии 1.18), нет классов, нет виртуальной машины. Но именно эта минималистичность делает его идеальным полем для битвы за чистый код. Если вы пишете на Go и ваш проект начинает напоминать спагетти из goroutine и нечитаемых switch — вы теряете главное преимущество языка: скорость разработки и простоту поддержки.

В этой статье мы разберём 7 практических правил, которые помогут вам писать идиоматический Go-код, который будет понятен не только компилятору, но и вашим коллегам (и вам самому через месяц). Поехали!

1. Правильные имена переменных: коротко и ясно

В Go принято использовать короткие, но осмысленные имена. В отличие от Java, где ListOfUserOrders — это норма, в Go вы увидите orders или даже os. Почему? Потому что Go — язык для чтения кода, а не для его написания.

Основные правила нейминга в Go

  • Локальные переменные: 1-2 буквы для очевидных вещей (i — счётчик, s — строка).
  • Пакеты: одно слово, строчные буквы, без подчёркиваний (http, fmt).
  • Экспортируемые идентификаторы: начинаются с заглавной буквы (GetUser).
  • Акроним: пишутся в одном регистре (HTTP, URL, а не Http или Url).
// Плохо
func GetUserDataFromDatabaseByID(userID string) (UserData, error) {
    var userDataFromDb UserData
    // ...
    return userDataFromDb, nil
}

// Хорошо
func GetUser(id string) (User, error) {
    var u User
    // ...
    return u, nil
}

Совет: если имя переменной занимает больше 2 слов — скорее всего, вы что-то усложняете.

2. Форматирование: доверьтесь gofmt

В Go нет «стилевых войн». Команда gofmt решает всё за вас. Это не рекомендация — это стандарт. Если вы не используете gofmt — вы пишете не на Go, а на «диалекте Go».

Что делает gofmt?

  • Расставляет отступы (табуляция, а не пробелы).
  • Убирает лишние переносы строк.
  • Выравнивает комментарии.
  • Удаляет неиспользуемые импорты (с флагом -s).
// До gofmt
func  add (a int,b int) int{
return a+b
}

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

В любом современном редакторе (VS Code, GoLand) gofmt запускается автоматически при сохранении. Если нет — настройте. Это сэкономит часы код-ревью.

3. Обработка ошибок: не игнорируйте, не паникуйте

В Go нет исключений (try-catch). Ошибки — это просто значения, которые нужно проверять. Это одна из самых сильных сторон языка, если вы используете её правильно.

Правила чистой работы с ошибками

  • Всегда проверяйте error. Никаких _ для пропуска.
  • Не используйте panic в библиотечном коде. Только если программа не может продолжить работу (например, не открылся конфиг).
  • Оборачивайте ошибки с помощью fmt.Errorf("context: %w", err) (Go 1.13+).
// Плохо
func ReadConfig(path string) *Config {
    data, _ := ioutil.ReadFile(path) // игнорируем ошибку
    var cfg Config
    json.Unmarshal(data, &cfg)
    return &cfg
}

// Хорошо
func ReadConfig(path string) (*Config, error) {
    data, err := ioutil.ReadFile(path)
    if err != nil {
        return nil, fmt.Errorf("read config: %w", err)
    }
    var cfg Config
    if err := json.Unmarshal(data, &cfg); err != nil {
        return nil, fmt.Errorf("parse config: %w", err)
    }
    return &cfg, nil
}

Важно: никогда не пишите if err != nil { return nil, err } без контекста. Добавляйте хотя бы одно слово, чтобы понять, где произошла ошибка.

4. Интерфейсы: маленькие и только там, где нужны

В Go принято правило: «принимайте интерфейсы, возвращайте структуры». Это не догма, но хорошая практика. Интерфейсы в Go — это контракты, и они должны быть минимальными.

Как не перегрузить интерфейсы

  • Один метод — один интерфейс (вспомните io.Reader, io.Writer).
  • Не создавайте интерфейсы «на вырост». Только когда есть 2+ реализации.
  • Не называйте интерфейсы с суффиксом I (как в C#). В Go принято имя + er: Reader, Writer, Sender.
// Плохо: огромный интерфейс
type IUserService interface {
    GetUser(id string) User
    CreateUser(u User) error
    UpdateUser(u User) error
    DeleteUser(id string) error
    SendEmail(email string) error
}

// Хорошо: разделяем на маленькие
type UserGetter interface {
    GetUser(id string) (User, error)
}

type EmailSender interface {
    Send(email string) error
}

Такой подход делает код гибким: вы можете легко подменить реализацию для тестов, не таща за собой лишние методы.

5. Структура проекта: стандартное расположение

Go — не Java, и структура папок здесь гораздо проще. Но даже в простоте можно запутаться. Придерживайтесь конвенции, описанной в официальном Go project layout.

Базовая структура для микросервиса

myapp/
├── cmd/
│   └── myapp/
│       └── main.go          # точка входа
├── internal/
│   ├── handler/             # HTTP-хендлеры
│   ├── service/             # бизнес-логика
│   └── repository/          # работа с БД
├── pkg/                     # публичные библиотеки
├── config/                  # конфиги (YAML, env)
├── go.mod
└── Makefile

Поч

Блоги

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