> Что интересного вы делали и чему научились на ошибках (Go)

Уровень: middle · Роль: backend · Категория: Технические вопросы

Компании: Автомакон

Стек: Go

> Пример ответа

Короткий ответ

Расскажу про проект, где я переписывал legacy-сервис на Go. Основная ошибка была в том, что я сразу начал оптимизировать горячий путь, не измерив профиль. В итоге потратил время на микрооптимизации, а реальная проблема оказалась в N+1 запросах к БД. Научился всегда начинать с profiling и pprof, а не с предположений.

Подробное объяснение

В одном из сервисов, отвечающем за агрегацию данных, был высокий latency на чтение. Я решил, что проблема в аллокациях и сложности алгоритма, и начал переписывать хот-пат через sync.Pool и ручное управление буферами. Через неделю работы стало понятно, что выигрыш минимален - 3-5%. Тогда я запустил pprof и увидел, что 70% времени уходит на блокировки при обращении к PostgreSQL из-за отсутствия индексов и лишних JOIN.

Из этой ошибки я вынес несколько принципов:

  • сначала профилируй, потом оптимизируй;
  • не доверяй интуиции о "медленном коде";
  • проверяй интеграции с БД и внешними системами до внутренних оптимизаций.

Второй случай - это работа с graceful shutdown. Я не обрабатывал сигналы корректно, и при деплое терялись in-flight запросы. Научился использовать context.WithTimeout и errgroup для координации завершения воркеров.

На практике

Сейчас в новых задачах я всегда начинаю с написания бенчмарков и профилирования, если есть подозрение на производительность. Для БД - включаю логирование медленных запросов и смотрю explain. Для graceful shutdown - пишу тесты, которые эмулируют SIGTERM и проверяют, что все запросы завершились.

Также я стал больше внимания уделять code review: если вижу, что коллега оптимизирует без измерений, предлагаю сначала собрать метрики.

Пример кода

Пример того, как я теперь делаю graceful shutdown с errgroup:

GO
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
g, gCtx := errgroup.WithContext(ctx)
g.Go(func() error {
return runHTTPServer(gCtx)
})
g.Go(func() error {
return runWorker(gCtx)
})
if err := g.Wait(); err != nil {
log.Printf("shutdown: %v", err)
}
}

Как отвечать на собеседовании

Структурируйте ответ по схеме: ситуация - действие - результат - вывод. Назовите конкретную ошибку, объясните, как вы её нашли (профилирование, логи, метрики), что изменили и какой эффект получили. Если ошибка была в архитектуре, расскажите, как перепроектировали. Важно показать, что вы умеете анализировать свои действия и не повторяете их.

Что проверяет интервьюер

Интервьюер оценивает:

  • способность к рефлексии и признанию ошибок;
  • инженерный подход: измерение, гипотезы, проверка;
  • знание инструментов: pprof, tracing, метрики;
  • умение рассказывать о trade-off и последствиях решений.

Типичные ошибки

  • Рассказывать про ошибку без конкретики: "было плохо, стало хорошо".
  • Сваливать вину на других или на внешние факторы.
  • Называть ошибкой то, что на самом деле было осознанным решением.
  • Не показывать, что вы сделали выводы и изменили процесс работы.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью