When an interviewer asks you to design an email service, they want to see how you translate a familiar product into a set of well‑defined components. The conversation typically starts with a quick clarification of scope, then moves through requirements, data modeling, API design, a high‑level diagram, and finally a deep dive into the hardest parts—delivery reliability, spam handling, and scaling. Below is a practical walkthrough you can follow in a real interview.
1. Clarify the Scope and Requirements
Functional requirements
- Send, receive, and store messages.
- List inbox, sent, drafts, and archive folders.
- Support basic filters (by sender, subject, date).
- Provide read/unread status and flagging.
- Allow attachments up to a moderate size.
Non‑functional requirements
- Durability: No loss of messages, even under hardware failures.
- Latency: Send‑to‑receive latency under a few seconds for typical users.
- Scalability: Support millions of active mailboxes and billions of messages.
- Security: End‑to‑end encryption optional, at least TLS in‑flight.
- Availability: Target 99.9%‑plus uptime.
Ask the interviewer which of these are most critical for the role you’re interviewing for; you can then prioritize design decisions.
2. Identify Core Entities and Data Model
| Entity | Key Attributes | Typical Operations |
|---|---|---|
| User | user_id, email_address, password_hash, quota | register, authenticate, update profile |
| Mailbox | mailbox_id, user_id, type (inbox, sent, …) | create, list, delete |
| Message | message_id, mailbox_id, sender_id, recipient_ids, subject, body, timestamp, flags, attachment_refs | store, retrieve, mark‑read, move, delete |
| Attachment | attachment_id, message_id, size, storage_key | upload, download |
| DeliveryTask | task_id, message_id, recipient_id, status, retry_count | enqueue, update status |
The Message entity is the heart of the system. It is stored once (single write) and referenced from each recipient’s mailbox via a lightweight pointer, which keeps storage costs low and simplifies deletion.
3. Sketch a Minimal API
POST /v1/messages # send a new email
GET /v1/mailboxes/{id}/messages # list messages in a mailbox
GET /v1/messages/{id} # fetch full message body + attachments
PATCH /v1/messages/{id} # update flags (read, starred)
DELETE /v1/messages/{id} # delete (moves to trash)
Each endpoint should accept and return JSON payloads. For large attachments, use a pre‑signed URL flow (upload to object store, then reference the key in the message).
4. High‑Level Architecture
Client (Web/Mobile) → API Gateway → Auth Service
│
▼
Mail Service Layer
│
┌──────────────────────┼───────────────────────┐
│ │ │
Message Store Index Service Delivery Queue
(e.g., distributed (full‑text search, (e.g., Kafka/RabbitMQ)
object store) secondary indexes) │
│ │ ▼
Attachment Store Search Service Delivery Workers → SMTP/IMAP
- API Gateway handles request routing, rate limiting, and TLS termination.
- Auth Service validates tokens and injects
user_id. - Mail Service Layer orchestrates writes to the Message Store, updates indexes, and pushes a
DeliveryTask. - Message Store can be a durable object store (e.g., S3‑compatible) with metadata in a relational DB for ACID guarantees.
- Index Service builds inverted indexes for fast search and filtering.
- Delivery Queue decouples sending from user‑facing APIs, enabling retries and back‑pressure handling.
- Delivery Workers talk to external SMTP servers (or internal mail exchangers) and update task status.
5. Deep Dive: Hard Parts
5.1 Ensuring Durability and Consistency
- Use a write‑ahead log or two‑phase commit when persisting a message and its attachments. The log guarantees that a crash cannot lose a partially written email.
- Replicate the Message Store across at least three zones; a quorum read/write policy (e.g., majority) protects against a single zone outage.
5.2 Scaling the Inbox
- Sharding: Partition mailboxes by
user_idhash. Each shard holds a subset of mailboxes and their messages, allowing horizontal scaling. - Hot mailbox mitigation: For power users with huge inboxes, keep recent messages in a fast cache (e.g., Redis) and older ones in cold storage.
- Compaction: Periodically purge deleted messages after a configurable retention period to reclaim space.
5.3 Spam and Abuse Prevention
- Integrate a machine‑learning classifier as a microservice that scores incoming messages. Scores influence the folder placement (inbox vs spam) and trigger rate‑limiting for suspicious senders.
- Rate‑limit the number of messages a user can send per hour to curb abuse.
5.4 Deliverability and Retry Logic
- Delivery Workers maintain a retry back‑off schedule (e.g., exponential with jitter). After a configurable number of failures, the task moves to a dead‑letter queue for manual review.
- For large recipient lists, batch the delivery tasks to avoid overwhelming downstream SMTP servers.
6. Trade‑offs and Alternatives
| Concern | Choice | Pros | Cons |
|---|---|---|---|
| Storage | Object store + relational metadata | Simple durability, cheap cold storage | Extra join cost for reads |
| Search | Dedicated search cluster (e.g., Elasticsearch) | Powerful full‑text queries, fast filters | Additional operational complexity |
| Delivery | Push model (queue → workers) | Decouples latency from user request | Slightly higher eventual consistency |
| Attachment handling | Direct upload to object store via pre‑signed URL | Offloads large payloads from API | Requires extra client logic |
Discuss why you favor a particular combination based on the interview’s focus—if the role emphasizes low latency, you might lean toward a cache‑first design; if durability is paramount, you stress multi‑zone replication.
7. Typical Follow‑Up Questions
- How would you handle email threading? – Store a
thread_idand aparent_message_idto reconstruct conversation trees; index these fields for quick retrieval. - What if a user wants end‑to‑end encryption? – Generate a per‑user public key pair; encrypt the message body client‑side and store only ciphertext. Delivery workers forward ciphertext without decryption.
- How do you enforce per‑user storage quotas? – Keep a running total of attachment sizes in the user profile; reject uploads that would exceed the quota.
- Can you support real‑time push notifications? – Add a WebSocket or push‑service layer that subscribes to delivery events and forwards a lightweight notification to the client.
- What if the system must support internationalization? – Store message bodies as UTF‑8, use locale‑aware indexing, and ensure the UI can render right‑to‑left scripts.
8. Sample Answer Template (45‑90 seconds)
"Sure, let me walk through a design for a core email service. The main functional pieces are sending, receiving, and storing messages, plus basic folder navigation. Non‑functional goals include durability—no lost mail—and low latency, ideally under a few seconds for a typical user.
At a high level, the client talks to an API gateway that routes requests to an auth service and then to a mail service layer. The mail service writes the message metadata to a relational store and the raw content plus attachments to an object store. It also pushes a delivery task onto a queue. Workers consume the queue, talk to external SMTP servers, and update the task status.
For search, we maintain a secondary index that lets users filter by sender, subject, or date. We shard mailboxes by user hash, replicate data across three zones, and use a cache for recent messages to keep reads fast. Spam detection is handled by a separate classifier microservice that scores incoming mail and routes it to the appropriate folder.
The biggest trade‑offs are between storage simplicity and query speed. Using an object store with relational metadata is cheap and durable, but you need a dedicated search cluster for fast filtering. If the interview focuses on latency, I’d favor adding a cache layer; if durability is the priority, I’d double down on replication and a write‑ahead log.
Does that give you a good overview?"
How to practice this
- Sketch the diagram on paper: Start with the API gateway and add components one by one, labeling data flows.
- Run a mock interview: Pair with a colleague and take turns being the interviewer. Keep the discussion under 15 minutes.
- Use Call Assistant to rehearse your answer aloud and get instant feedback on whether your story stays anchored to the resume points you want to highlight.
FAQ
What is the minimal set of services needed for a functional email prototype? You need an API gateway, authentication, a mail service that writes to a durable store, and a simple delivery worker that sends via SMTP. Search and spam services can be added later.
How do you guarantee that a sent email is not lost if the server crashes mid‑write? Use a write‑ahead log or two‑phase commit so the message is persisted before acknowledging the client. Replicate the log across zones for added safety.
When should you use a cache for inbox listings? Cache recent messages for users with high read frequency or for hot mailboxes. Invalidate the cache on write or delete events to keep consistency.
What are common scalability bottlenecks in an email service? The inbox index, attachment storage I/O, and the delivery queue can become hot spots. Sharding, async processing, and tiered storage help mitigate these issues.
Frequently asked questions
What is the minimal set of services needed for a functional email prototype?
You need an API gateway, authentication, a mail service that writes to a durable store, and a simple delivery worker that sends via SMTP. Search and spam services can be added later.
How do you guarantee that a sent email is not lost if the server crashes mid‑write?
Use a write‑ahead log or two‑phase commit so the message is persisted before acknowledging the client. Replicate the log across zones for added safety.
When should you use a cache for inbox listings?
Cache recent messages for users with high read frequency or for hot mailboxes. Invalidate the cache on write or delete events to keep consistency.
What are common scalability bottlenecks in an email service?
The inbox index, attachment storage I/O, and the delivery queue can become hot spots. Sharding, async processing, and tiered storage help mitigate these issues.
#system design#email service#architecture#scalability#interview#an email service