День 4: Системный дизайн, Clean Architecture и Mock-собеседование

🎯 Цели дня

  1. Закрепить структуру Clean Architecture в Go.
  2. Вспомнить инструменты профилирования и тестирования.
  3. Провести устную репетицию ответов на ключевые вопросы.
  4. Пройти финальный чек-лист перед собеседованием.

📖 Теоретический минимум (1 час)

Clean Architecture в Go

project/
├── cmd/                    # Точка входа
│   └── app/
│       └── main.go         # DI, Graceful Shutdown
├── internal/               # Закрытая логика
│   ├── handler/            # HTTP/gRPC handlers
│   ├── service/            # Бизнес-логика
│   └── repository/         # Работа с БД/очередями
├── pkg/                    # Переиспользуемые утилиты
│   ├── worker/             # Worker Pool
│   └── logger/             # Логгер
└── api/                    # OpenAPI/gRPC specs

Правила:

  • internal/ не импортируется извне.
  • Зависимости направлены внутрь (handler → service → repository).
  • Контекст передаётся только как первый аргумент функции.

Профилирование

bash
# Запусти с pprof
go run -race main.go &
go tool pprof http://localhost:8080/debug/pprof/heap

# Посмотри утечки
(pprof) top 10
(pprof) list <функция>

# CPU профиль
go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30

Тестирование

bash
# С проверкой гонок и покрытием
go test -race -cover ./...

# Бенчмарки
go test -bench=. -benchmem ./...

💻 Практика: Аудит кода AgentForge (2 часа)

Чек-лист проверки:

Открой свой текущий код проекта и проверь:

  • Везде ли передаётся context.Context в вызовы БД и внешних API?
  • Есть ли таймауты у всех http.Client?
  • Реализован ли Graceful Shutdown в main.go?
  • Используются ли именованные возвращаемые ошибки или fmt.Errorf("...: %w", err) для оборачивания?
  • Есть ли Worker Pool для фоновых задач?
  • Настроена ли идемпотентность консьюмеров очередей?
  • Есть ли Circuit Breaker для внешних вызовов?

Если чего-то нет — добавь заглушки или комментарии // TODO: добавить таймаут, чтобы показать интервьюеру, что ты знаешь, как это должно быть.

🗣️ Mock-собеседование: Топ-5 фраз, которые повысят твою ценность

Проговори эти фразы вслух. Они показывают senior-подход:

  1. "Я использую небуферизированные каналы для естественного backpressure, чтобы при всплеске нагрузки не допустить OOM, а сразу сигнализировать клиенту о перегрузке."

  2. "Всегда передаю context в вызовы БД и внешних API, чтобы избежать утечек горутин при обрыве соединения клиентом."

  3. "Для агрегации данных использую errgroup, так как он автоматически отменяет контекст при первой ошибке, экономя ресурсы кластера."

  4. "При работе с очередями я всегда настраиваю ручной Ack и идемпотентность обработки, так как гарантия 'at-least-once' может привести к дубликатам при перезапуске пода."

  5. "Graceful Shutdown обязателен, иначе при деплое в Kubernetes мы будем получать 502 ошибки от клиентов, чьи запросы оборвутся принудительно через 30 секунд."

🏁 Финальный чек-лист перед собеседованием

Конкурентность

  • Могу нарисовать схему Worker Pool на листке бумаги
  • Понимаю, как работает errgroup и чем он лучше sync.WaitGroup
  • Знаю разницу буферизированных и небуферизированных каналов

Отказоустойчивость

  • Могу объяснить Circuit Breaker и его состояния
  • Понимаю, почему retry без jitter опасен
  • Реализовал Graceful Shutdown с правильным порядком закрытия ресурсов

Асинхронное взаимодействие

  • Знаю, почему ручной Ack в RabbitMQ критически важен
  • Понимаю концепцию идемпотентности консьюмера
  • Могу объяснить разницу между хореографией и оркестрацией в Saga

Архитектура

  • Понимаю структуру Clean Architecture в Go
  • Знаю, как передать контекст через границы API
  • Могу найти утечку памяти через pprof

🧩 Проверь себя

1. Куда направлены зависимости в Clean Architecture?
2. Как гарантировать, что сообщение из очереди не потеряется? — выберите все подходящие
3. Зачем в аудите кода проверять передачу context.Context в вызовы БД и внешних API?
4. Как защитить сервис, если внешний API начал тормозить? — выберите все подходящие
5. Как ограничить параллизм обработки 1000 задач до 10 одновременных?
6. Что запрещает директория internal/?
7. Что первым делом должен сделать сервис при Graceful Shutdown?

🎬 Экспресс-тест (30 минут)

Попробуй ответить без IDE:

  1. Как ограничить параллелизм при обработке 1000 задач, чтобы одновременно работали только 10? Ответ: Worker Pool с 10 воркерами и небуферизированным каналом задач.

  2. Что произойдет, если не реализовать Graceful Shutdown в K8s? Ответ: Активные запросы оборвутся через 30 сек после SIGTERM, клиенты получат 502.

  3. Как гарантировать, что сообщение из очереди не потеряется? Ответ: Durable queue + Persistent delivery + Manual Ack.

  4. Что делать, если консьюмер может получить одно и то же сообщение дважды? Ответ: Реализовать идемпотентность обработки (хранить event_id в БД).

  5. Как защитить сервис от падения, если внешний API начал тормозить? Ответ: Circuit Breaker + таймауты на уровне http.Client + retry с jitter.

📝 Итоговый план подготовки

За 4 дня ты:

  1. Освоил конкурентность (Worker Pool, errgroup)
  2. Научился строить отказоустойчивые сервисы (Circuit Breaker, Graceful Shutdown)
  3. Понял асинхронное взаимодействие (RabbitMQ, Saga, идемпотентность)
  4. Закрепил Clean Architecture и подготовился к устным вопросам

Что делать дальше:

  • Повтори тонкости языка (мини-курс параллельно)
  • Пройди mock-собеседование с другом или ментором
  • Реши 2–3 задачи на LeetCode по Go (если требуют)
  • Выспись перед собеседованием!

Удачи! 🚀