Low Level Design: Email Template Service

Data Model

EmailTemplate (
  id               SERIAL PRIMARY KEY,
  name             VARCHAR NOT NULL,
  locale           VARCHAR NOT NULL DEFAULT 'en',
  subject_template TEXT NOT NULL,
  html_template    TEXT NOT NULL,
  text_template    TEXT NOT NULL,
  variables        JSONB,   -- schema: required fields + types
  version          INT NOT NULL DEFAULT 1,
  active           BOOLEAN DEFAULT TRUE
)

Template Rendering

Render subject line and both HTML and plain-text bodies by passing a context dict through Jinja2 or Handlebars. Always generate a plain-text alternative — many spam filters and corporate mail clients prefer or require it.

MJML

MJML is a responsive email framework: write semantic MJML markup, compile it to table-based HTML that renders consistently across Outlook, Gmail, Apple Mail, and mobile clients. MJML compilation runs server-side at template-save time; the compiled HTML is stored in html_template.

Multi-Language Support

Template lookup uses the composite key (name, locale). If no record exists for the requested locale, fall back to (name, 'en'). This allows partial localization: only translated templates need entries; untranslated templates automatically use the English default.

Variable Validation

The variables JSONB column stores a schema listing required fields and their types. Before rendering, validate that all required variables are present in the provided context. Return a 400 with the list of missing variables rather than rendering a broken email.

Preview API

POST /templates/:name/preview accepts a sample context body and returns fully rendered HTML for browser preview. Useful for template editors and QA. Preview does not send any email.

A/B Variant Testing

TemplateVariant (
  template_id  INT REFERENCES EmailTemplate(id),
  variant      CHAR(1) CHECK (variant IN ('a','b')),
  html_template TEXT NOT NULL,
  weight        INT NOT NULL DEFAULT 50
)

At send time, select variant A or B via weighted random choice based on weight. Record which variant was sent per recipient to measure open and click rates. Winning variant can be promoted to the primary template.

Version History

Each edit creates a new row with an incremented version; the previous row is retained. Only the row with the highest version and active=TRUE is used for rendering. Rollback sets active=FALSE on the current version and active=TRUE on the target version.

Spam Score Check

On preview and before activating a new version, call the SpamAssassin API (or a hosted equivalent such as Mail-Tester) with the rendered HTML and text. Surface the score and flagged rules in the response. Reject activation if the score exceeds a configured threshold (e.g., 5.0) to protect sender reputation.

{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “What is MJML and why use it for email templates?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “MJML is a responsive email framework that compiles semantic markup into table-based HTML. Email clients do not support modern CSS, so hand-writing cross-client HTML is error-prone. MJML handles the compatibility layer, producing HTML that renders consistently across Outlook, Gmail, Apple Mail, and mobile clients.”
}
},
{
“@type”: “Question”,
“name”: “How does multi-language fallback work in an email template service?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Template lookup uses a composite key of template name and locale. If no record exists for the requested locale, the service falls back to the default locale (typically English). This means only translated templates need entries in the database — untranslated templates automatically serve the English version rather than failing.”
}
},
{
“@type”: “Question”,
“name”: “How do A/B variants work in email template testing?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “A TemplateVariant table stores alternative HTML templates linked to a base template, each with a weight. At send time the service selects variant A or B via weighted random choice. The chosen variant is recorded per recipient so open rates and click rates can be compared. The winning variant can then be promoted to the primary template.”
}
},
{
“@type”: “Question”,
“name”: “Why check spam score before activating an email template?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Spam filters score emails on content patterns, link density, image-to-text ratio, and other signals. A high spam score means your email lands in the junk folder or is rejected entirely, damaging sender reputation. Running SpamAssassin or a hosted equivalent on the rendered template before activation catches deliverability issues before they reach real recipients.”
}
}
]
}

See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering

See also: Atlassian Interview Guide

See also: Stripe Interview Guide 2026: Process, Bug Bash Round, and Payment Systems

Scroll to Top