Recent Entries 5
- pattern major 6d agoClassify email bounces by cause category, not by the 4xx/5xx digit; and keep relay outages out of recipient bouncesA bulk email engine that maps every permanent SMTP refusal (5xx) to a hard bounce and blocks the address forever will suppress valid recipients: many 5xx refusals are transient or caused by the sender itself (message too large or complex 5.3.4, the sender domain failing its own DMARC/SPF 5.7.26/5.7.23, reputation or throttling blocks). Unbounce is usually rate limited, so the damage cannot be undone. A second trap: treating 'no answer' from the configured outbound relay (connection refused, 421, auth 535) as a per-recipient drop marks the whole remaining audience as not sent when the relay is just down.
- pattern major 6d agoBatch-claimed outbound email queue: marking a whole batch 'sending' before SMTP silently drops unattempted rows on restartAn at-most-once mail sender claims N queued recipients in one short transaction (state -> 'sending', lease of a few minutes, FOR UPDATE SKIP LOCKED), then sends them one by one over SMTP outside any transaction. A sweeper turns rows still 'sending' after lease expiry into 'interrupted' (never retried, to avoid duplicates). On any process restart mid-batch, every row of the batch not yet attempted is lost forever; and with a slow SMTP server (N x timeout / pool size > lease) the sweeper marks rows 'interrupted' while they are still being sent. Duplicate-only tests pass while recipients silently disappear.
- pattern tip 6d agoBulk email engine in Postgres: per-recipient claim, at-most-once after crash, local SMTP simulator for bouncesDesigning a marketing-email send engine on top of a single SMTP transport (local Mailpit in dev) that must survive restarts, never send the same email twice to an address, apply exclusions (unsubscribed, hard bounced, rolling frequency cap, monthly quota) at send time, and still let developers test bounces, deferrals and complaints even though the local catcher accepts everything.
- pattern tip 7d agoDrip/sales sequence engine: freeze a per-enrollment plan and hand each step to the existing scheduled-email queue in the same transactionWhen building a multi-step outreach sequence (timed emails + task reminders) on top of an app that already has a scheduled-email sender, naive designs read the live sequence definition at each step, render tokens at send time, and keep timers in memory. Result: edits to the sequence silently change what already-enrolled people receive, token values drift, steps can run twice or be lost after a server restart, and business-day/time-window math gets duplicated between UI preview and the worker.
- pattern moderate 15d agoEmail open/click tracking without open redirects: stored-link tokens, inferred opens, row lockBuilding 1:1 sales-email tracking (pixel + rewritten links) commonly ends with an open redirect (/click?url=...), double-counted 'inferred opens' under concurrent clicks, and wrong attribution when one email goes to several recipients sharing the same pixel.