Polish email marketing challenge and solution slides
Torey Heinz
committed Feb 22, 2026
commit 07942ae64ec4d9d3bd7aa7a063ba1d83119f950b
Showing 1
changed file with
19 additions
and 19 deletions
slides/03-real-world.html
+19
-19
| @@ | @@ -30,9 +30,9 @@ |
| <section> | |
| <h2>Three Production Systems</h2> | |
| - | <p class="muted">Same patterns, different domains</p> |
| + | <p class="muted">Going beyond basic CRUD apps</p> |
| <ol> | |
| - | <li class="fragment"><strong>Email Marketing</strong> — 500k+ personalized emails through AWS SES</li> |
| + | <li class="fragment"><strong>Email Marketing</strong> — 500k+ personalized emails</li> |
| <li class="fragment"><strong>Domain Registry</strong> — TCP connections to Verisign</li> | |
| <li class="fragment"><strong>Internal Admin</strong> — Multi-site LiveView dashboard</li> | |
| </ol> | |
| @@ | @@ -47,44 +47,44 @@ |
| ============================================= --> | |
| <section class="section-divider" data-transition="zoom"> | |
| - | <h2>3.1 — Email Marketing</h2> |
| + | <h2>Email Marketing</h2> |
| <p>500,000+ personalized emails through AWS SES</p> | |
| <aside class="notes"> | |
| - | First system: our email marketing platform. This sends half a million personalized emails through AWS SES. |
| + | First system: our email marketing platform. This sends half a million personalized emails through |
| </aside> | |
| </section> | |
| <section> | |
| <h2>The Challenge</h2> | |
| - | <p>Send <strong>500,000+ personalized marketing emails</strong> through AWS SES.</p> |
| <div class="callout fragment"> | |
| - | <p><strong>The catch:</strong> If you exceed your rate limit, AWS doesn't tell you — they <strong>silently drop your emails</strong>.</p> |
| + | <p>Send 500,000+ personalized marketing emails</p> |
| </div> | |
| <ul> | |
| - | <li class="fragment">Rate limiter that <strong>never</strong> exceeds the AWS SES sending rate</li> |
| - | <li class="fragment">Schedule and personalize hundreds of thousands of emails</li> |
| + | <li class="fragment">Emails need to be sent individually so each one is trackable.</li> |
| + | <li class="fragment">Ensure we send <strong>one and only one</strong> email per recipient!</li> |
| <li class="fragment">Handle bounces, complaints, and delivery notifications</li> | |
| - | <li class="fragment">Run concurrently without blocking the web app</li> |
| + | <li class="fragment">Each email generates 3–4 API requests — needs to run <strong>concurrently</strong> without blocking the app</li> |
| + | <li class="fragment">With AWS SES, if you exceed your rate limit, they...<br> <strong class="fragment">silently drop your emails</strong>.</li> |
| </ul> | |
| <aside class="notes"> | |
| - | The key constraint: AWS SES silently drops emails if you exceed your rate limit. No error, nothing. So we need a rate limiter that's rock-solid, plus the ability to schedule and personalize half a million emails without blocking the web app. |
| + | The key constraint: AWS SES silently drops emails if you exceed your rate limit. No error, nothing. So we need a rate limiter that's rock-solid. We also need to handle bounces, complaints, and delivery notifications. Plus the ability to schedule and personalize half a million emails without blocking the web app. |
| </aside> | |
| </section> | |
| <section> | |
| - | <h2>The Architecture</h2> |
| + | <h2>Our Solution</h2> |
| - | <ol style="font-size: 0.65em; margin-top: 15px;"> |
| - | <li class="fragment"><strong>Scheduler Worker</strong> queries DB, creates email jobs — in batches of 2,000</li> |
| + | <ul> |
| + | <li class="fragment"><strong>Scheduler Worker</strong> queries recipients, creates Oban jobs</li> |
| <li class="fragment"><strong>Oban</strong> picks up each job and triggers a <strong>Mailer Worker</strong></li> | |
| - | <li class="fragment"><strong>Mailer Worker</strong> handles one email: validate, personalize, rate-check, send</li> |
| - | <li class="fragment"><strong>Rate Limiter</strong> (GenServer) ensures we never exceed the AWS SES limit</li> |
| - | </ol> |
| + | <li class="fragment"><strong>Mailer Worker</strong> validate → personalize → rate-check → send</li> |
| + | <li class="fragment"><strong>Rate Limiter</strong> ensures we never exceed the AWS SES limit</li> |
| + | </ul> |
| <aside class="notes"> | |
| - | Here's the architecture. A scheduler worker pulls member IDs from the database in chunks of 2,000 and creates individual email jobs. Each mailer worker handles one email — validate, personalize, check the rate limiter, send. The rate limiter is a GenServer that enforces the AWS SES sending rate. All supervised. |
| + | Here's our solution. A scheduler worker pulls member IDs from the database in chunks of 2,000 and creates individual email jobs. Each mailer worker handles one email — validate, personalize, check the rate limiter, send. The rate limiter is a GenServer that enforces the AWS SES sending rate. All supervised. |
| </aside> | |
| </section> | |
| @@ | @@ -270,7 +270,7 @@ end |
| ============================================= --> | |
| <section class="section-divider" data-transition="zoom"> | |
| - | <h2>3.2 — Domain Registry</h2> |
| + | <h2>Domain Registry</h2> |
| <p>TCP connections to Verisign</p> | |
| <aside class="notes"> | |
| @@ | @@ -448,7 +448,7 @@ defp connection_healthy?(_conn), do: false |
| ============================================= --> | |
| <section class="section-divider" data-transition="zoom"> | |
| - | <h2>3.3 — Internal Admin</h2> |
| + | <h2>Internal Admin</h2> |
| <p>Multi-site LiveView dashboard</p> | |
| <aside class="notes"> | |