| name | email-infrastructure |
| description | Email delivery infrastructure - DNS authentication (SPF/DKIM/DMARC), subdomain isolation, provider abstraction, template systems, bounce handling, warmup strategy, and deliverability monitoring. |
Email Infrastructure
Production email delivery requires DNS authentication, domain isolation, and provider-agnostic architecture. A single misconfiguration can land your entire domain in spam.
GOOD vs BAD: Domain Strategy
BAD: Send bulk marketing from example.com
→ Spam complaints tank your main domain reputation
→ Transactional emails (password reset, receipts) start landing in spam
→ Recovery takes weeks of warmup
GOOD: Subdomain isolation with separate reputations
→ mail.example.com for transactional (password reset, receipts, 2FA)
→ notify.example.com for product notifications (comments, mentions)
→ marketing.example.com for bulk campaigns (newsletters, promotions)
→ Each subdomain has independent reputation — one bad campaign does not poison the rest
DNS Authentication: SPF + DKIM + DMARC
# SPF — declare which servers can send from your domain
mail.example.com TXT "v=spf1 include:_spf.provider.com ~all"
# DKIM — cryptographic signature on every email
selector._domainkey.mail.example.com TXT "v=DKIM1; k=rsa; p=MIGf..."
# DMARC — policy for failed authentication (progressive rollout)
# Week 1-2: monitor only
_dmarc.mail.example.com TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"
# Week 3-4: quarantine suspicious emails
_dmarc.mail.example.com TXT "v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc@example.com"
# Week 5+: reject after confidence builds
_dmarc.mail.example.com TXT "v=DMARC1; p=reject; rua=mailto:dmarc@example.com"
Never jump straight to p=reject. The progressive rollout catches misconfigurations before they block legitimate mail.
Email Provider Abstraction
interface EmailProvider {
send(message: EmailMessage): Promise<EmailResult>
sendBatch(messages: EmailMessage[]): Promise<EmailResult[]>
}
interface EmailMessage {
from: string
to: string | string[]
subject: string
html: string
text?: string
replyTo?: string
headers?: Record<string, string>
tags?: Record<string, string>
}
interface EmailResult {
id: string
status: 'sent' | 'queued' | 'failed'
error?: string
}
function createEmailProvider(config: { provider: string }): EmailProvider {
switch (config.provider) {
case 'resend': return new ResendProvider()
case 'ses': return new SESProvider()
case 'postmark': return new PostmarkProvider()
default: throw new Error(`Unknown email provider: ${config.provider}`)
}
}
Transactional vs Marketing Separation
interface EmailService {
sendTransactional(message: EmailMessage): Promise<EmailResult>
sendMarketing(message: EmailMessage): Promise<EmailResult>
}
class ProductionEmailService implements EmailService {
constructor(
private transactional: EmailProvider,
private marketing: EmailProvider
) {}
async sendTransactional(message: EmailMessage): Promise<EmailResult> {
return this.transactional.send({
...message,
from: `noreply@mail.example.com`,
headers: { 'X-Priority': '1' }
})
}
async sendMarketing(message: EmailMessage): Promise<> {
..({
...message,
: ,
: { : }
})
}
}
Template System (MJML)
import mjml2html from 'mjml'
const mjmlTemplate = `
<mjml>
<mj-head>
<mj-attributes>
<mj-all font-family="system-ui, -apple-system, sans-serif" />
<mj-text font-size="16px" line-height="1.5" color="#1a1a1a" />
</mj-attributes>
<mj-style>
@media (prefers-color-scheme: dark) {
.dark-bg { background-color: #1a1a1a !important; }
.dark-text { color: #e5e5e5 !important; }
}
</mj-style>
</mj-head>
<mj-body>
<mj-section css-class="dark-bg">
<mj-column>
<mj-text css-class="dark-text">Hello {{name}},</mj-text>
<mj-text css-class="dark-text">{{body}}</mj-text>
<mj-button href="{{actionUrl}}" background-color="#2563eb">
{{actionLabel}}
</mj-button>
</mj-column>
</mj-section>
</mj-body>
</mjml>
`
function escapeHtml(str: string): string {
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')
}
function compileTemplate(mjml: string, vars: Record<string, string>): string {
let compiled = mjml
for (const [key, value] of .(vars)) {
compiled = compiled.(, (value))
}
{ html, errors } = (compiled)
(errors. > ) {
()
}
html
}
Bounce and Complaint Handling
interface BounceEvent {
type: 'bounce' | 'complaint' | 'delivery'
email: string
reason?: string
timestamp: string
}
async function handleEmailWebhook(event: BounceEvent): Promise<void> {
switch (event.type) {
case 'bounce':
await db.emailSuppression.upsert({
where: { email: event.email },
create: { email: event.email, reason: 'hard_bounce', suppressedAt: new Date() },
update: { reason: 'hard_bounce', suppressedAt: new Date() }
})
break
case 'complaint':
await db..({
: { : event. },
: { : event., : , : () },
: { : , : () }
})
()
:
db..({
: { : event. },
: { : () }
})
}
}
(): <> {
entry = db..({ : { email } })
entry !==
}
Domain Warmup Strategy
New domain/IP starts with zero reputation. Send too fast and ISPs block you.
Week 1: 50 emails/day → Send to your most engaged users only
Week 2: 200 emails/day → Expand to users who opened in last 30 days
Week 3: 500 emails/day → Include 90-day active users
Week 4: 1,000 emails/day → General audience, monitor bounce rate
Week 5: 5,000 emails/day → Scale up if bounce < 2% and complaints < 0.1%
Week 6: 10,000 emails/day → Full volume if metrics stay clean
Week 8+: Full send → Maintain list hygiene going forward
CRITICAL THRESHOLDS:
Bounce rate > 5% → STOP sending, clean your list
Complaint rate > 0.1% → STOP sending, review content and targeting
Open rate < 10% → Re-evaluate subject lines and audience
Deliverability Monitoring
interface DeliverabilityMetrics {
sent: number
delivered: number
bounced: number
complained: number
opened: number
clicked: number
}
async function getDailyMetrics(date: string): Promise<DeliverabilityMetrics> {
const metrics = await db.emailLog.aggregate({
where: { sentAt: { gte: new Date(date), lt: new Date(date + 'T23:59:59Z') } },
_count: { id: true },
})
return metrics
}
async function checkHealthThresholds(metrics: DeliverabilityMetrics): Promise<void> {
if (metrics.sent === 0) return
const bounceRate = metrics. / metrics.
complaintRate = metrics. / metrics.
deliveryRate = metrics. / metrics.
(bounceRate > ) {
(, )
}
(complaintRate > ) {
(, )
}
(deliveryRate < ) {
(, )
}
}
Pre-Send Checklist
Before every send, verify:
1. Recipient is not on suppression list (bounces + complaints)
2. SPF/DKIM/DMARC records are valid for the sending subdomain
3. Unsubscribe link is present (CAN-SPAM, GDPR requirement)
4. Plain text version exists alongside HTML
5. From address matches the authenticated subdomain
6. List-Unsubscribe header is set for bulk sends
7. Subject line is not empty and under 78 characters
Key principle: Treat email infrastructure like a reputation system. Subdomain isolation protects your core domain. Progressive DMARC rollout catches issues before they block mail. Always check the suppression list before sending. Monitor bounce and complaint rates daily — by the time you notice spam folder placement, the damage is already done.