Чтение Go-кода и рефакторинг без изменения поведения
Зачем это на интервью
В задаче на чтение кода проверяют, видите ли вы реальное поведение, а не только «неудачный стиль». Рефакторинг ценен, если сохраняет контракт: результаты, ошибки, порядок побочных эффектов, отмену и совместимость.
Минимум для E4
Начните с экспортируемой функции и ответьте: какие входы допустимы, какой результат обещан, какие зависимости вызываются, какие ошибки возвращаются и кто освобождает ресурс. Затем пройдите все ветви, особенно ранние return, defer, пустые и nil значения. Зафиксируйте поведение тестом до структурного изменения.
Пример: извлечение повторяющейся логики не меняет порядок проверки и сохраняет исходную ошибку.
package user
import (
"context"
"errors"
"fmt"
)
var ErrNotFound = errors.New("user not found")
type Repository interface {
Find(context.Context, string) (string, error)
}
func LoadName(ctx context.Context, repo Repository, id string) (string, error) {
if id == "" {
return "", fmt.Errorf("load user: empty id")
}
name, err := repo.Find(ctx, id)
if err != nil {
return "", fmt.Errorf("load user %q: %w", id, err)
}
return name, nil
}Тест должен проверять errors.Is(err, ErrNotFound), а не точную строку. При извлечении helper нельзя заменить %w на %v: иначе классификация перестанет работать.
Углубление для E5/Senior
Опишите инварианты и границы изменения явно: например, «зависимость не вызывается при пустом id», «контекст передаётся без замены», «ошибка ErrNotFound распознаётся вызывающим кодом». Для рискованного рефакторинга используйте characterization tests, feature flag или параллельное сравнение результатов. Разделяйте рефакторинг и изменение поведения на разные commits/PR: это упрощает ревью, rollback и поиск регрессии.
Проверяйте скрытые контракты: порядок SQL-запросов, идемпотентность retry, порядок закрытия ресурсов, timeouts, метрики и serialized JSON. Производительность меняйте только после профиля и benchmark.
Ключевые понятия
- Контракт поведения — наблюдаемые входы, выходы, ошибки и побочные эффекты.
- Инвариант — свойство, сохраняемое во всех допустимых путях выполнения.
- Characterization test — тест существующего поведения, включая иногда неудобное, до рефакторинга.
- Малый шаг — компилируемое изменение с понятной причиной и отдельной проверкой.
- Semantic refactor — не меняет контракт; переименование публичного символа без периода совместимости уже меняет API.
Типовые вопросы
- С чего начать чтение незнакомой функции?
- С её контракта и вызовов: входы, результат, ошибки, побочные эффекты и ownership ресурсов; затем пройти все ветви.
- Как доказать, что рефакторинг не поменял поведение?
- Добавить tests на контракт до изменения, делать маленькие шаги, запускать unit/integration tests и нужные статические проверки.
- Почему нельзя сразу «улучшить» ошибку?
- Её тип,
errors.Is, текст, HTTP mapping или метрика могут быть внешним контрактом; сначала нужно выяснить потребителей.
- Её тип,
- Что проверять вокруг
defer?- Что
deferзарегистрирован после успешного получения ресурса, порядок LIFO корректен, а ошибкаCloseне потеряна там, где она значима.
- Что
- Когда переписывание оправдано?
- Когда есть измеримая проблема или изменение требований и есть план миграции; не потому, что новый стиль субъективно нравится.
Практика
- Возьмите функцию с тремя ветками ошибок и напишите characterization tests для каждой.
- Извлеките validation/helper без изменения exported API и покажите
git diffтолько с рефакторингом. - Добавьте тест, что repository не вызывается для пустого идентификатора.
Критерии готовности: тесты фиксируют успех, все ошибки и отсутствие лишнего вызова; после изменений проходят gofmt, go vet ./..., go test ./... и при наличии параллелизма go test -race ./....
Частые ошибки и ловушки
- Менять код до понимания вызывающих сторон и контрактных тестов.
- Сливать форматирование, переименование и изменение логики в один diff.
- Терять
context.Context, оборачивать его вcontext.Background()или игнорироватьctx.Err(). - Удалять seemingly redundant проверку, не доказав, что она не защищает старого клиента или corrupt data.
Связанные темы
Go-код на интервью · Ошибки и observability · Тестирование
Источники
- Refactoring: Improving the Design of Existing Code, Martin Fowler — проверено 2026-10-02.
- Go testing package — проверено 2026-10-02.
- Go Code Review Comments — проверено 2026-10-02.