Дизайн webhook на собеседовании системного аналитика

Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

Карьерник — Duolingo для аналитиков: 10 минут в день тренируй SQL, Python, A/B, статистику, метрики и ещё 3 темы собеса. 1500+ вопросов в Telegram-боте. Бесплатно.

Зачем разбирать на собесе

Webhooks — стандарт async integration. На собесе SA: «как secure», «retry».

Reliable delivery

Producer. Publish event → enqueue webhook delivery job.

Worker. Calls receiver. Failure → retry с exponential backoff.

Standard retry policy. 1m, 5m, 30m, 2h, 24h. После N failures — dead letter.

Attempt 1: 0s
Attempt 2: 60s
Attempt 3: 300s
...
Stop после 5 attempts → notify owner.

At-least-once. Receiver должен handle duplicates.

Signing

Receiver должен verify, что webhook actually от us.

HMAC. Stripe-style.

Producer:
  payload = JSON.stringify(body)
  timestamp = now()
  signature = HMAC-SHA256(timestamp + payload, shared_secret)
  headers["X-Signature"] = signature
  headers["X-Timestamp"] = timestamp

Receiver:
  computed = HMAC-SHA256(headers.timestamp + body, shared_secret)
  if computed != signature: reject
  if abs(now - timestamp) > 5min: reject (replay)

Without signing — anyone может call webhook URL, fake events.

Idempotency на receiver

Worker может retry → receiver gets event multiple times.

Solution. Each event имеет unique ID. Receiver tracks processed IDs.

ON RECEIVE event_id:
  if event_id seen previously:
    return 200 OK (already processed)
  else:
    process
    save event_id в processed_events
    return 200 OK
Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

Replay attacks

Атакующий перехватил signed webhook → replay позже.

Защита. Timestamp в signature + reject старых (5 min window).

if now - timestamp > 5min: reject

Без timestamp check — signed webhook valid forever.

Failure handling

Receiver returns:

  • 2xx — success.
  • 4xx — invalid request, don't retry.
  • 5xx — server error, retry.
  • Timeout — retry.

Receiver обязан:

  • Respond быстро (< 5s обычно).
  • Не делать tяжёлую работу sync — enqueue async work, return 200.
@app.post("/webhook")
async def webhook(request):
    verify_signature(request)
    queue.enqueue(process_event, request.body)
    return Response(status=200)  # быстро

Связанные темы

FAQ

Это официальная информация?

Нет. Статья основана на стандартных практиках (Stripe, GitHub webhook design).


Тренируйте системный анализ — откройте тренажёр с 1500+ вопросами для собесов.