Code review на Go: что проверять, чтобы код не бомбил

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

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

Начать курс

Code review на Go: что проверять, чтобы код не бомбил

Code review — это не просто галочка в Jira или повод поругаться с коллегой в комментариях к пул-реквесту. Это, пожалуй, самый честный способ не выстрелить себе в ногу на проде. А когда речь заходит о Go — языке, который обожают за простоту и ненавидят за «а где здесь дженерики?» (спойлер: они уже есть), — ревью становится искусством. В этой статье мы разберем, на что именно смотреть при code review на Go, чтобы ваш код не только работал, но и радовал глаз. Поехали!

1. Обработка ошибок: не игнорируй, не бойся

В Go нет исключений. Тут всё честно: если функция может упасть, она возвращает error. Самая частая проблема новичков — игнорирование ошибок через подчеркивание. Помните: «Never ignore an error» — это не просто мантра, это закон.

Что проверять в коде:

  • Проверка err != nil — она должна быть везде, где функция возвращает ошибку. Даже если вы уверены, что «здесь ошибки не будет».
  • Логирование или проброс — ошибку нужно либо залогировать с контекстом, либо вернуть наверх. Глушить её — моветон.
  • Сравнение с sentinel-ошибками — используйте errors.Is() и errors.As(), а не прямое сравнение ==.
// Плохо: игнорируем ошибку
file, _ := os.Open("config.yaml")

// Хорошо: проверяем и логируем
file, err := os.Open("config.yaml")
if err != nil {
    log.Printf("не удалось открыть конфиг: %v", err)
    return err
}
defer file.Close()

2. Горутины и конкурентность: порядок в хаосе

Горутины — это суперсила Go. Но с великой силой приходит великая ответственность. На ревью мы часто видим утечки горутин, гонки данных и блокировки. Code review в Go без проверки конкурентности — это как ехать на спорткаре без ремня безопасности.

Чек-лист для горутин:

  • Запуск горутин — всегда контролируйте время жизни. Используйте sync.WaitGroup или контексты.
  • Гонки данных — проверяйте, что доступ к общим данным защищён мьютексом или каналом. Запустите go test -race.
  • Паника в горутине — если горутина паникует, упадёт вся программа. В production-коде обязательно используйте recover.
// Плохо: горутина без контроля
go func() {
    // может упасть и убить весь процесс
    doWork()
}()

// Хорошо: с WaitGroup и recover
var wg sync.WaitGroup
wg.Add(1)
go func() {
    defer wg.Done()
    defer func() {
        if r := recover(); r != nil {
            log.Printf("горутина упала: %v", r)
        }
    }()
    doWork()
}()
wg.Wait()

3. Интерфейсы: маленькие и по делу

В Go интерфейсы — это контракты. Но, в отличие от Java, они не обязаны быть огромными. В сообществе Go есть принцип: «Чем меньше интерфейс, тем лучше». Один метод — идеально. Два — нормально. Три — уже подозрительно.

На что смотреть:

  • Размер интерфейса — если в нём больше 3-4 методов, задумайтесь о разделении.
  • Принимайте интерфейсы, возвращайте структуры — классическая мудрость Go. Она делает код гибким и тестируемым.
  • Не определяйте интерфейсы заранее — пусть их создаёт потребитель, а не производитель.
// Плохо: огромный интерфейс
type Storage interface {
    Save(data []byte) error
    Load(id string) ([]byte, error)
    Delete(id string) error
    List() ([]string, error)
    Connect() error
    Close() error
}

// Хорошо: маленькие интерфейсы
type Saver interface {
    Save(data []byte) error
}
type Loader interface {
    Load(id string) ([]byte, error)
}

4. Именование и стиль: код должен говорить

Go — язык с жёсткими правилами форматирования (gofmt — ваш друг). Но code review Go не про пробелы, а про семантику. Имена переменных, функций и пакетов должны быть понятны без комментариев.

Правила именования:

  • Короткие имена для локальных переменных — i, j, err — ок. Для глобальных — осмысленные.
  • Акронимов — пишите их одинаково: HTTP, URL, ID. Не Http или Url.
  • Пакеты — одно слово, нижний регистр, без подчеркиваний. user, db, api.
// Плохо: непонятные имена
func f(x int) int {
    return x * 2
}

// Хорошо: говорящие имена
func DoubleValue(value int) int {
    return value * 2
}

5. Тесты: не количество, а качество

Go поставляется со встроенным тестовым фреймворком — и это прекрасно. Но на ревью мы часто видим тесты, которые тестируют только happy path. Грамотный code review в Go требует проверки тестов на полноту.

Что должно быть в тестах:

  • Table-driven тесты — стандарт в Go. Позволяет легко добавлять кейсы.
  • Edge cases — пустые строки, nil, нулевые значения, граничные условия.
  • Тесты на ошибки — проверьте, что функция возвращает ошибку, когда должна.
func TestDivide(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
        wantErr  bool
    }{
        {"positive", 10, 2, 5, false},
        {"by zero", 10, 0, 0, true},
        {"negative", -6, 3, -2, false},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result, err := Divide(tt.a, tt.b)
            if tt.wantErr && err == nil {
                t.Error("expected error")
            }
            if !tt.wantErr && result != tt.expected {
                t.Errorf("got %d, want %d", result, tt.expected                

Блоги

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