Refine slides with naming, code simplification, and further trimming
Torey Heinz
committed Feb 23, 2026
commit 73a555636aae6c5fb782fcb33e19dad509819c01
Showing 3
changed files with
16 additions
and 56 deletions
scratch.md
+3
-0
| @@ | @@ -194,3 +194,6 @@ Review each of these files for typos and consistency. Are we building a case for |
| - slides/01-intro.html | |
| - 02-the-language.livemd | |
| - slides/03-real-world.html | |
| + | |
| + | |
| + | Let's add this screen shout after "The Core Function" it shows our EPP health page for OrbitFour |
slides/01-intro.html
+1
-2
| @@ | @@ -138,12 +138,11 @@ |
| <p class="fragment small muted">The BEAM VM was designed from scratch for this:</p> | |
| <ul> | |
| <li class="fragment"><strong>Lightweight processes</strong> — ~2KB each, millions at once</li> | |
| - | <li class="fragment"><strong>Preemptive scheduling</strong> — no request can starve others</li> |
| <li class="fragment"><strong>Per-process GC</strong> — no stop-the-world pauses</li> | |
| <li class="fragment"><strong>Crash isolation</strong> — one process can't take down the app</li> | |
| </ul> | |
| <p class="fragment muted" style="margin-top: 0.8em; font-style: italic;"> | |
| - | Battle-tested for 40 years. Nine nines of uptime.<br/> |
| + | Battle-tested for 40 years. 99.999999% uptime.<br/> |
| It just wasn't fun to work with... | |
| </p> | |
slides/03-real-world.html
+12
-54
| @@ | @@ -30,11 +30,11 @@ |
| <section> | |
| <h2>Three Production Systems</h2> | |
| - | <p class="muted">Going beyond basic CRUD apps</p> |
| + | <p class="muted">Going beyond basic Web apps</p> |
| <ol> | |
| - | <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> |
| + | <li class="fragment"><strong>Vianet Marketing</strong> — Email marketing platform</li> |
| + | <li class="fragment"><strong>OrbitFour</strong> — full featured domain registration platform</li> |
| + | <li class="fragment"><strong>Vianet Admin</strong> — Multi-site LiveView dashboard</li> |
| </ol> | |
| <aside class="notes"> | |
| @@ | @@ -43,22 +43,22 @@ |
| </section> | |
| <!-- ============================================= | |
| - | SECTION 3.1: Email Marketing |
| + | SECTION 3.1: Vianet Marketing |
| ============================================= --> | |
| <section class="section-divider" data-transition="zoom"> | |
| - | <h2>Email Marketing</h2> |
| - | <p>500,000+ personalized emails through AWS SES</p> |
| + | <h2>Vianet Marketing</h2> |
| + | <p>Sending emails through AWS Simple Email Service (SES)</p> |
| <aside class="notes"> | |
| - | First system: our email marketing platform. This sends half a million personalized emails through AWS SES. The challenge isn't just volume — it's doing it reliably without exceeding rate limits. |
| + | First system: our Vianet marketing platform. This sends half a million personalized emails through AWS SES. The challenge isn't just volume — it's doing it reliably without exceeding rate limits. |
| </aside> | |
| </section> | |
| <section data-transition="fade"> | |
| <img src="images/vianet-marketing.jpg" alt="Vianet Marketing email campaign dashboard" style="max-height: 85vh; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.3);"> | |
| <aside class="notes"> | |
| - | This is our email marketing platform. On the left you can see the campaign details — audience, member count, schedule. On the right, the actual email preview. Over half a million members, and each one gets a personalized email. |
| + | This is our Vianet marketing platform. On the left you can see the campaign details — audience, member count, schedule. On the right, the actual email preview. Over half a million members, and each one gets a personalized email. |
| </aside> | |
| </section> | |
| @@ | @@ -101,11 +101,11 @@ |
| <pre><code class="language-elixir" data-trim> | |
| def perform(%Oban.Job{args: args}) do | |
| - | campaign = Campaigns.get_campaign!(args["campaign_id"]) |
| + | member_ids = Campaigns.member_ids!(args["campaign_id"]) |
| # Stream from DB → chunk → bulk insert jobs | |
| Repo.transaction(fn -> | |
| - | Repo.stream(member_ids_query(campaign), max_rows: 2_000) |
| + | Repo.stream(member_ids(campaign), max_rows: 2_000) |
| |> Stream.chunk_every(2_000) | |
| |> Enum.each(fn member_ids -> | |
| member_ids | |
| @@ | @@ -134,13 +134,11 @@ def perform(%Oban.Job{args: args} = job) do |
| {:ok, member} <- get_member_with_site(member_id), | |
| {:ok, _} <- validate_email(member.email), | |
| {:ok, campaign} <- get_campaign_with_site(campaign_id), | |
| - | {:ok, campaign_email} <- reserve_campaign_email(campaign, member), |
| {:ok, _} <- EmailRateLimiter.ready?() | |
| ) do | |
| send_campaign_email(campaign, member) | |
| else | |
| {:not_ready, :exceeded_rate} -> {:snooze, 1} | |
| - | {:not_ready, :exceeded_daily} -> {:snooze, min(backoff(job), 3_600)} |
| {:already_sent} -> {:discard, :already_sent} | |
| {:invalid_email} -> {:discard, :invalid_email} | |
| {:error, result} -> {:error, result} | |
| @@ | @@ -167,8 +165,6 @@ defmodule Marketing.EmailRateLimiter do |
| def ready?, do: GenServer.call(__MODULE__, :acquire) | |
| def handle_call(:acquire, _from, state) do | |
| - | state = refill(state) |
| - | |
| if state.tokens >= 1.0 do | |
| {:reply, {:ok, :token}, %{state | tokens: state.tokens - 1.0}} | |
| else | |
| @@ | @@ -330,44 +326,6 @@ end |
| </aside> | |
| </section> | |
| - | <section> |
| - | <h2>LiveView: Assign Async</h2> |
| - | <p class="muted small">Load from three databases concurrently — loading states are automatic</p> |
| - | |
| - | <pre class="tiny-code"><code class="language-elixir" data-trim data-line-numbers="1-13|15-23"> |
| - | # In mount — load data asynchronously from three databases |
| - | socket |
| - | |> assign_async( |
| - | [:puppies_admins, :roommates_admins, :rr_admins], |
| - | fn -> |
| - | {:ok, %{ |
| - | puppies_admins: Puppies.Admins.get_active_admins(), |
| - | roommates_admins: Roommates.Admins.get_admins(), |
| - | rr_admins: ReputableRooms.Admins.get_admins() |
| - | }} |
| - | end |
| - | ) |
| - | |
| - | # In template — spinner while loading, then render |
| - | <.async_result :let={admins} assign={@puppies_admins}> |
| - | <:loading><.spinner /></:loading> |
| - | <.input |
| - | type="select" |
| - | label="Puppies admin" |
| - | options={Enum.map(admins, &{&1.name, &1.id})} |
| - | /> |
| - | </.async_result> |
| - | </code></pre> |
| - | |
| - | <p class="fragment" style="font-size: 0.7em; color: #555;"> |
| - | <strong>No loading state management. No useEffect. No fetch.</strong> |
| - | </p> |
| - | |
| - | <aside class="notes"> |
| - | Here's assign_async in action. The mount function loads admin lists from three different databases concurrently. Loading states are automatic — the template gets spinners for free. When data arrives, the async_result component renders the content. Compare this to React: you'd need useState, useEffect, a loading boolean, error handling — all wired up manually. Here LiveView handles it all. |
| - | </aside> |
| - | </section> |
| - | |
| <!-- ============================================= | |
| SECTION: Wrap-up | |
| @@ | @@ -386,7 +344,7 @@ socket |
| <ol> | |
| <li class="fragment"><strong>Why Elixir?</strong> — The BEAM, 40 years of reliability</li> | |
| <li class="fragment"><strong>The Language</strong> — Pattern matching, processes, supervision, LiveView</li> | |
| - | <li class="fragment"><strong>Real-World Code</strong> — 500k emails, a domain registrar, multi-site admin</li> |
| + | <li class="fragment"><strong>Real-World Code</strong> — marketing emails, a domain registrar, multi-site admin</li> |
| </ol> | |
| <ul style="margin-top: 1em; list-style: none; padding: 0;"> | |
| <li class="fragment">Concurrency is the <strong>default</strong>, not an afterthought</li> | |