День 4: Системный дизайн, Clean Architecture и Mock-собеседование
🎯 Цели дня
- Закрепить структуру Clean Architecture в Go.
- Вспомнить инструменты профилирования и тестирования.
- Провести устную репетицию ответов на ключевые вопросы.
- Пройти финальный чек-лист перед собеседованием.
📖 Теоретический минимум (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-подход:
-
"Я использую небуферизированные каналы для естественного backpressure, чтобы при всплеске нагрузки не допустить OOM, а сразу сигнализировать клиенту о перегрузке."
-
"Всегда передаю
contextв вызовы БД и внешних API, чтобы избежать утечек горутин при обрыве соединения клиентом." -
"Для агрегации данных использую
errgroup, так как он автоматически отменяет контекст при первой ошибке, экономя ресурсы кластера." -
"При работе с очередями я всегда настраиваю ручной Ack и идемпотентность обработки, так как гарантия 'at-least-once' может привести к дубликатам при перезапуске пода."
-
"Graceful Shutdown обязателен, иначе при деплое в Kubernetes мы будем получать 502 ошибки от клиентов, чьи запросы оборвутся принудительно через 30 секунд."
🏁 Финальный чек-лист перед собеседованием
Конкурентность
- Могу нарисовать схему Worker Pool на листке бумаги
- Понимаю, как работает
errgroupи чем он лучшеsync.WaitGroup - Знаю разницу буферизированных и небуферизированных каналов
Отказоустойчивость
- Могу объяснить Circuit Breaker и его состояния
- Понимаю, почему retry без jitter опасен
- Реализовал Graceful Shutdown с правильным порядком закрытия ресурсов
Асинхронное взаимодействие
- Знаю, почему ручной Ack в RabbitMQ критически важен
- Понимаю концепцию идемпотентности консьюмера
- Могу объяснить разницу между хореографией и оркестрацией в Saga
Архитектура
- Понимаю структуру Clean Architecture в Go
- Знаю, как передать контекст через границы API
- Могу найти утечку памяти через pprof
🧩 Проверь себя
🎬 Экспресс-тест (30 минут)
Попробуй ответить без IDE:
-
Как ограничить параллелизм при обработке 1000 задач, чтобы одновременно работали только 10? Ответ: Worker Pool с 10 воркерами и небуферизированным каналом задач.
-
Что произойдет, если не реализовать Graceful Shutdown в K8s? Ответ: Активные запросы оборвутся через 30 сек после SIGTERM, клиенты получат 502.
-
Как гарантировать, что сообщение из очереди не потеряется? Ответ: Durable queue + Persistent delivery + Manual Ack.
-
Что делать, если консьюмер может получить одно и то же сообщение дважды? Ответ: Реализовать идемпотентность обработки (хранить
event_idв БД). -
Как защитить сервис от падения, если внешний API начал тормозить? Ответ: Circuit Breaker + таймауты на уровне
http.Client+ retry с jitter.
📝 Итоговый план подготовки
За 4 дня ты:
- Освоил конкурентность (Worker Pool, errgroup)
- Научился строить отказоустойчивые сервисы (Circuit Breaker, Graceful Shutdown)
- Понял асинхронное взаимодействие (RabbitMQ, Saga, идемпотентность)
- Закрепил Clean Architecture и подготовился к устным вопросам
Что делать дальше:
- Повтори тонкости языка (мини-курс параллельно)
- Пройди mock-собеседование с другом или ментором
- Реши 2–3 задачи на LeetCode по Go (если требуют)
- Выспись перед собеседованием!
Удачи! 🚀