> Какие виды уведомлений используются в системах (SMS, push, email и т.д.) (Go)
Уровень: senior · Роль: backend · Категория: Технические вопросы
Компании: Моторика
Стек: Go
> Пример ответа
Короткий ответ
В системах используются четыре основных канала уведомлений: email, push, SMS и in-app. Каждый имеет свои trade-off по доставке, стоимости и пользовательскому опыту. Для backend-инженера важно понимать не только сами каналы, но и архитектуру их интеграции: очередь сообщений, шаблонизацию, дедупликацию, retry-механизмы и observability. Выбор канала зависит от срочности, контекста и требований к гарантии доставки.
Подробное объяснение
Каналы уведомлений делятся по двум осям: срочность и гарантия доставки.
- Email - асинхронный, не срочный, дешёвый. Гарантия доставки низкая (спам-фильтры, неверные адреса). Используется для подтверждений, рассылок, отчётов.
- Push - доставка через FCM/APNs, требует установленного приложения. Средняя срочность, средняя стоимость. Есть ограничения по размеру payload (до 4KB у FCM). Не гарантирует доставку, если устройство офлайн.
- SMS - высокая срочность, высокая стоимость, максимальная совместимость (работает на любом телефоне). Используется для OTP, критичных алертов. Требует интеграции с SMS-провайдером (Twilio, Vonage).
- In-app - уведомления внутри приложения (баннеры, badge, центр уведомлений). Низкая стоимость, высокая кастомизация. Не требует внешних сервисов.
Также существуют Webhook и WebSocket для server-to-server уведомлений, но это скорее интеграционный механизм, чем пользовательский канал.
Архитектурно система уведомлений обычно строится так:
- Producer - сервис, генерирующий событие (например, заказ создан).
- Очередь (RabbitMQ, Kafka) - буферизация и гарантия at-least-once.
- Notification service - определяет канал, шаблон, получателя, приоритет.
- Provider adapters - абстракция над внешними API (email-провайдер, FCM, SMS-шлюз).
- Delivery tracking - статусы (sent, delivered, failed), retry с exponential backoff.
Ключевые паттерны: fan-out (одно событие → несколько каналов), template rendering (Go text/template), deduplication (по ключу события + получателя), rate limiting (чтобы не спамить).
На практике
Для senior-позиции важно уметь проектировать систему с учётом:
- Приоритизации каналов - например, OTP всегда через SMS, но fallback на email.
- Fallback-логики - если push не доставлен за N секунд, отправить SMS.
- Идемпотентности - повторная отправка не должна создавать дубликаты.
- Мультитенантности - разные клиенты с разными провайдерами и шаблонами.
- Мониторинга - метрики по каждому каналу: latency, error rate, conversion rate.
В Go типичная реализация - интерфейс Notifier:
GOtype Notifier interface {Send(ctx context.Context, msg Message) error}
Каждый канал реализует этот интерфейс. Композиция через middleware (retry, logging, rate limit) - стандартный подход.
Пример кода
GOtype Message struct {ID stringUserID stringChannel ChannelType // Email, Push, SMSTemplate stringPayload map[string]anyPriority Priority}type Notifier interface {Send(ctx context.Context, msg Message) error}type EmailNotifier struct {client *smtp.Client // или API провайдера}func (n *EmailNotifier) Send(ctx context.Context, msg Message) error {body, err := renderTemplate(msg.Template, msg.Payload)if err != nil {return fmt.Errorf("render template: %w", err)}// отправка через провайдераreturn n.client.Send(msg.UserID, body)}// Middleware для retryfunc WithRetry(next Notifier, maxRetries int) Notifier {return NotifierFunc(func(ctx context.Context, msg Message) error {var err errorfor i := 0; i < maxRetries; i++ {err = next.Send(ctx, msg)if err == nil {return nil}select {case <-ctx.Done():return ctx.Err()case <-time.After(time.Duration(1<<i) * time.Second):}}return err})}
Как отвечать на собеседовании
Начни с классификации каналов, затем переходи к архитектуре. Покажи, что понимаешь trade-off: SMS дорого, но надёжно; push дёшево, но зависит от платформы. Упомяни, что выбор канала определяется бизнес-требованиями, а не техническими предпочтениями.
Обязательно затронь:
- Как обрабатываешь отказы провайдеров (retry, circuit breaker).
- Как обеспечиваешь идемпотентность (ключ = eventID + userID + channel).
- Как тестируешь интеграции (mock провайдеров, contract tests).
- Как масштабируешь (очередь, worker pool, sharding по userID).
Если спросят про конкретный кейс - например, "уведомление о бронировании" - предложи схему: событие → очередь → notification service → параллельная отправка в email и push, с fallback на SMS при high priority.
Что проверяет интервьюер
- Понимание асинхронной архитектуры и роли очередей.
- Умение проектировать отказоустойчивые системы (retry, dead letter queue).
- Знание внешних интеграций и их ограничений (rate limits, payload size).
- Способность абстрагироваться от конкретных провайдеров.
- Навыки идиоматичного Go (интерфейсы, middleware, context).
Типичные ошибки
- Смешивание каналов и провайдеров: "SMS - это Twilio" - нет, SMS это канал, Twilio это провайдер.
- Игнорирование идемпотентности - повторная отправка при retry создаёт дубликаты.
- Отправка уведомлений синхронно в HTTP-хендлере - блокирует запрос, теряет сообщения при сбое.
- Отсутствие fallback-логики - если email-провайдер лежит, пользователь не получает уведомление.
- Не учитывать rate limits провайдеров - можно получить бан за массовую рассылку.
- Хранение шаблонов в коде - должно быть в конфиге или БД, чтобы менять без деплоя.
> Похожие задачи по backend
Что означает строка load average в Linux
Как пользоваться программой top в Linux
Какие бывают виды HTTP-запросов и их семантика
Что такое конкурентность в программировании
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью