Stories · Season 2 — Frontrow · Episode 03

The Reminder That Arrived a Day Late

In which Frontrow promises to remind you about the show, and keeps that promise at the worst possible moment.

The story

Frontrow had a lovely little feature: 24 hours before your event, you got a friendly reminder with your tickets attached. Customers adored it. It was the kind of small touch that made people feel looked after.

It was held together with string. Specifically, a background loop that woke up, scanned every upcoming event, worked out which reminders were “due,” and sent them. It worked on Mara’s laptop. It worked in the demo. It worked right up until the night a deploy restarted the service mid-scan.

When the service came back, it had no memory of how far it had gotten. So it sent the whole batch again. Thousands of customers got their reminder twice. A few hundred, due to a timezone bug in the “is it due yet?” math, got theirs at 3 a.m. And one cohort — for an event that fell exactly on the deploy window — didn’t get reminded at all, then got a furious “WHERE WERE MY TICKETS” email an hour before doors.

Priya in support summarized the week in one sentence: “We reminded everyone, several times, at the wrong time, except the people who needed it.”

Why this is hard the traditional way

“Do this thing later” sounds trivial and is quietly brutal. A scan-and-send loop has to answer hard questions correctly every time: Which items are due? Did I already send this one? What if I crash halfway? What if two instances scan at once? What about daylight saving time? Get any one wrong and you get duplicates, gaps, or 3 a.m. emails.

The state of “what’s scheduled and what’s been done” lives only in memory, or smeared across a last_run_at column that nobody fully trusts. A restart erases it. A second instance double-sends. The feature that was supposed to feel thoughtful becomes the feature most likely to wake a customer up.

How Relay changes the ending

Relay has a durable scheduler. You don’t run a loop that hunts for due work — you tell Relay “deliver this command at this time,” and it persists that intention and delivers it once, even across restarts and across multiple instances.

// When the ticket is booked, schedule the reminder — and forget about it.
await scheduler.ScheduleCommandAsync(
    new SendEventReminder(ticketId),
    deliverAt: eventStartsAt.AddHours(-24));
// Survives restarts. Fires once. No scan loop, no last_run_at column, no 3 a.m.

Because the schedule is durable, a deploy in the middle of the night changes nothing — the reminder is still on the books and still fires once, at the right moment. Claiming due work is concurrency-safe, so running six instances doesn’t mean sending six reminders. The thoughtful feature gets to stay thoughtful.

What it costs you to ignore this

  • Duplicates erode trust. “Helpful reminder” twice is annoying; five times is a reason to unsubscribe.
  • Gaps break promises. The reminder that didn’t send is the one the customer was relying on.
  • Restarts become incidents. Every deploy is a roll of the dice for anything time-based.
  • Timezone math is a tarpit. DST and “due yet?” logic, hand-rolled, will be wrong twice a year, forever.