Katkat

Design a Stateful Outreach Campaign Engine

OscarSoftware Engineer · Technical Screen

Last updated

This problem tests the ability to design a resilient, stateful task scheduler capable of handling asynchronous external triggers (responses) and idempotent state transitions.

Design a Stateful Outreach Campaign Engine

Design a backend service that manages automated outreach campaigns for healthcare clients. A campaign consists of a sequence of messages (Email, SMS) sent at specific intervals (e.g., T+0, T+24h, T+72h). The system must stop the sequence immediately if the user replies or if the client cancels the campaign.

Requirements

Functional Requirements:

  • Campaign Sequences: Support a series of scheduled messages per user contact.
  • Stop Condition: Cancel all future scheduled tasks for a user if they reply or if the client cancels the campaign.
  • Durable State: Track the current status of each campaign (e.g., PENDING, COMPLETED, CANCELLED, STOPPED_BY_RESPONSE).
  • Failure Handling: Retry transient failures (e.g., 5xx errors from email/SMS providers) with exponential backoff.

Non-Functional Requirements:

  • Scale: Handle up to 100,000 active campaigns with varying schedules.
  • Concurrency: Ensure that a race condition between a scheduled task and an incoming user response does not lead to duplicate messages.
  • Observability: Track message delivery status and provide an audit log of all state transitions.

Deliverables

  1. Data Model: Define the database schema for campaigns, individual message tasks, and the user response log.
  2. API Design: Provide the endpoints to start a campaign and register a user response.
  3. Architecture: Describe the high-level design. How do you handle scheduling tasks in the future? How do you ensure idempotent processing of events?
  4. Trade-offs: Discuss how you would handle 'out-of-order' events, such as a reply arriving just as a message is being dispatched.

Hints

Hint 1

Consider using a delayed job queue or a dedicated scheduling service rather than a simple polling database.

Hint 2

Think about using a state machine per campaign to transition between states and prevent race conditions when a reply arrives simultaneously with a send job.

Hint 3

Use idempotent operation keys (e.g., request IDs or task hashes) to ensure that retrying a failed delivery attempt does not result in duplicate messages.

Follow-up questions

  • How would you handle a scenario where a third-party SMS provider experiences a massive outage for several hours?
  • If the business requirements change to allow 'dynamic pauses' in the campaign based on the time of day, how would you update your scheduler?
  • How would you implement a 'dead-letter queue' for messages that consistently fail to reach the user?