Code review в Go: что проверять и как давать обратную связь

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

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

Начать курс

Code review в Go: что проверять и как давать обратную связь

Code review (ревью кода) — это не просто формальность, а один из самых эффективных инструментов для повышения качества кода, обмена знаниями в команде и предотвращения багов. В сообществе Go (Golang) code review имеет особое значение, так как язык поощряет простоту, читаемость и идиоматичность. В этой статье мы разберем, на что обращать внимание при ревью Go-кода и как давать обратную связь, чтобы она была полезной, а не обидной.

1. Общие принципы code review в Go

Прежде чем переходить к техническим деталям, важно помнить о культуре ревью. Ваша цель — улучшить код и помочь коллеге, а не доказать свою правоту. Вот несколько золотых правил:

  • Будьте конструктивны. Вместо «это плохо» напишите «здесь может быть утечка горутины, давай добавим контекст».
  • Спрашивайте, а не утверждайте. «Почему ты выбрал sync.Mutex, а не канал?» — хороший вопрос. «Ты дурак, надо было канал» — плохой.
  • Не задерживайте ревью. Стремитесь дать ответ в течение 24 часов (или по договоренности в команде).
  • Фокусируйтесь на логике и архитектуре. Стилистические правки (пробелы, отступы) лучше доверить gofmt.

2. Что проверять в первую очередь: чек-лист для Go

Вот структурированный список того, на что стоит обращать внимание при ревью Go-кода. Разделим его на логические блоки.

2.1. Обработка ошибок

В Go нет исключений, ошибки — это значения. Это одна из ключевых идиом языка.

  • Проверяйте все возвращаемые ошибки. Игнорирование ошибки с помощью _ допустимо только в очень редких случаях (например, при записи в io.Discard).
  • Не используйте panic для обычных ошибок. panic — это для невосстановимых состояний (например, ошибка инициализации сервера).
  • Оборачивайте ошибки. Используйте fmt.Errorf("что-то пошло не так: %w", err) для добавления контекста. Это позволяет использовать errors.Is() и errors.As().
// Плохо: потеря контекста
if err != nil {
    return err
}

// Хорошо: оборачиваем ошибку
if err != nil {
    return fmt.Errorf("не удалось открыть файл %s: %w", filename, err)
}

2.2. Горутины и конкурентность

Go славится своей легковесной конкурентностью, но она же является источником многих багов.

  • Утечки горутин. Убедитесь, что каждая запущенная горутина когда-нибудь завершится. Используйте контекст (context.Context) для отмены.
  • Правильное использование каналов. Кто создает канал, тот и закрывает его. Закрытие канала — это ответственность отправителя.
  • Гонка данных (data race). Избегайте одновременной записи в одну переменную без синхронизации. Используйте мьютексы (sync.Mutex) или атомики (sync/atomic).
// Пример с контекстом для отмены горутины
func process(ctx context.Context, dataCh chan int) {
    for {
        select {
        case <-ctx.Done():
            log.Println("Завершаем работу")
            return
        case val, ok := <-dataCh:
            if !ok {
                return // канал закрыт
            }
            fmt.Println(val)
        }
    }
}

2.3. Идиомы и стиль Go

Go — это язык, где читаемость кода ценится превыше всего.

  • Имена переменных. Короткие имена для коротких областей видимости. i для цикла, r для reader. Для глобальных переменных — более длинные имена.
  • Избегайте «магических чисел». Используйте константы.
  • Zero value. Используйте нулевое значение структуры (var s MyStruct) вместо явной инициализации пустыми полями, если это логично.
  • Методы и функции. Если функция возвращает булево значение, называйте её IsValid(), HasAccess().

2.4. Производительность и память

На ревью не обязательно гнаться за каждой наносекундой, но явные "красные флаги" стоит подсветить.

  • Избегайте лишних аллокаций. Например, при конкатенации строк в цикле используйте strings.Builder.
  • Утечки памяти. Например, если вы храните срез в глобальной переменной, а потом добавляете в него элементы, старый массив не будет собран сборщиком мусора.
  • Правильный размер буфера. При чтении из io.Reader используйте буфер разумного размера (например, 32KB).

3. Как давать обратную связь: от мягкой до критической

Не все комментарии одинаково важны. Чтобы не перегружать автора, используйте градацию важности.

  • Must fix (блокирующие). Ошибки, которые приведут к падению прода или некорректной работе. Например: гонка данных, игнорирование ошибки, утечка горутины.
  • Should fix (важные). Нарушение идиом Go, плохая читаемость, потенциальные проблемы с производительностью. Например: неиспользование context.Context там, где он нужен.
  • Nice to have (предложения). Улучшения стиля, альтернативные подходы. Например: «А что если вынести эту логику в отдельную функцию?».

Пример хорошего комментария: «Здесь стоит добавить defer resp.Body.Close(), иначе произойдет уте

Блоги

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