EasyStarter

Contact Form Service

DatabasesAPI DesignRate Limiting

Problem Statement

FormDrop is building a backend-as-a-service for contact forms. Website owners embed a snippet on their site, and form submissions are:

- Received via API - a POST endpoint accepts form data (name, email, message, custom fields).Validated - check for required fields, email format, and spam detection (honeypot field, reCAPTCHA score).Stored - all submissions saved to a database with timestamps and metadata.Emailed - the website owner receives an email notification for each submission (with customizable email templates).Dashboard - owners can view, search, and export all submissions from a web dashboard.Spam filtering - block obvious bots and flag suspicious submissions.

Targeting 5,000 websites generating 200,000 form submissions per day total.

What You'll Learn

Design a form submission backend that receives contact form data, validates it, and sends email notifications. Build this architecture under realistic production constraints, then validate tradeoffs in the design lab simulation.

DatabasesAPI DesignRate Limiting

Constraints

Active websites~5,000
Submissions/day~200,000
Email notification delay< 60 seconds
Spam block rate> 95%
API response time< 300 ms
Availability target99.5%
ApproachClick to expand

Interview-Ready Approach

1) Clarify Scope and SLOs

  • Problem statement: Design a form submission backend that receives contact form data, validates it, and sends email notifications.
  • Design for a peak load target around 100 RPS (including burst headroom).
  • Active websites: ~5,000
  • Submissions/day: ~200,000
  • Email notification delay: < 60 seconds
  • Spam block rate: > 95%
  • API response time: < 300 ms

2) Capacity Planning Method

  • Convert traffic and growth constraints into request rate, storage growth, and concurrency budgets.
  • Keep at least 2-3x safety margin per tier (ingress, compute, storage, async workers).
  • Reserve explicit latency budgets per hop so p95 can be defended in review.

3) Architecture Decisions

  • Databases: Define a clear system-of-record and design read/write paths separately before adding optimizations.
  • API Design: Standardize API boundaries, idempotency keys, pagination, and error contracts first.
  • Rate Limiting: Enforce token/sliding-window limits at ingress and for sensitive internal APIs.

4) Reliability and Failure Strategy

  • Use strong write constraints (transactions or conditional writes) and explicit backup/restore strategy.
  • Apply strict input validation and backward-compatible versioning.
  • Return deterministic 429 behavior with clear retry headers.

5) Validation Plan

  • Run one peak-load test, one dependency-degradation test, and one failover test.
  • Verify idempotency for all retried writes and async consumers.
  • Track user-facing SLOs first: p95 latency, error rate, and successful throughput.

6) Trade-offs to Call Out in Interviews

  • Databases: SQL gives stronger transactional guarantees; NoSQL often gives better write scaling and flexibility.
  • API Design: Rich APIs improve developer speed but can create long-term compatibility burden.
  • Rate Limiting: Aggressive limits protect the system but can hurt legitimate burst traffic.

Practical Notes

  • Rate limit by IP address and by form ID to prevent spam floods.
  • Use a queue for email delivery - don't send emails synchronously in the API request.
  • A honeypot field (hidden input) catches most bots without annoying real users.

Learn the Concept

Practice Next

Reference SolutionClick to reveal

Why This Solution Works

Request path: The solution keeps ingress, service logic, and stateful dependencies separated so each layer can scale independently.

Reference flow: Web Clients -> API Gateway -> Rate Limiter -> API Service -> Primary SQL DB

Design strengths

  • Security controls are enforced at ingress to protect downstream capacity.

Interview defense

  • This design makes bottlenecks explicit (ingress, core compute, persistence, async workers).
  • It supports progressive scaling without re-architecting the core request path.
  • It keeps correctness-sensitive state changes in durable systems while offloading background work asynchronously.