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> &mdash; 500k+ personalized emails through AWS SES</li>
+ <li class="fragment"><strong>Email Marketing</strong> &mdash; 500k+ personalized emails</li>
<li class="fragment"><strong>Domain Registry</strong> &mdash; TCP connections to Verisign</li>
<li class="fragment"><strong>Internal Admin</strong> &mdash; Multi-site LiveView dashboard</li>
</ol>
@@ @@ -47,44 +47,44 @@
============================================= -->
<section class="section-divider" data-transition="zoom">
- <h2>3.1 &mdash; 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 &mdash; 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&ndash;4 API requests &mdash; 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 &mdash; 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 &rarr; personalize &rarr; rate-check &rarr; 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 &mdash; 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 &mdash; Internal Admin</h2>
+ <h2>Internal Admin</h2>
<p>Multi-site LiveView dashboard</p>
<aside class="notes">