A customer’s contact emailed support asking why their site had never gotten a single renewal reminder, not once since 2019, despite renewing several times in the years since. That’s the entire complaint. No error on their end, nothing broken they could point to. Just years of silence from a system that was supposed to warn them before their site lapsed.

The flag that never reset

The renewal-notice job runs on a schedule, checking every account against a handful of category windows: a month out, a week out, the day after expiry, and a long-lapsed follow-up. Each category has its own exclusion rule, meant to stop a customer getting the same warning email twice in the same cycle. The rule checked one thing: does this account’s “last notice sent” flag already equal this category’s name. If it does, skip it.

That’s correct for exactly one cycle. It was never correct across cycles, because nothing in the system ever reset the flag once the account renewed and a new cycle began. Once an account was flagged for, say, the “expiring in a month” category, it was excluded from that category forever, not just for the one renewal it was actually warned about. Renew five times since, and the account still reads as “already warned” from the very first time, years earlier.

The account in the complaint had exactly that shape: flagged years earlier, renewed several times since, permanently excluded the entire time. Every other check that could have blocked the notice for some legitimate reason, a disabled trial, a test account, an opted-out league, came back clean. This wasn’t a gate working correctly. It was one flag with no expiry, silently doing the opposite of what its own name implied.

Nearly seven years old, and the evidence for why was already deleted

Tracing the origin took longer than fixing it. The real design intent was there once: an early version of this exact code checked whether the flag’s timestamp was recent before treating it as a suppression, which is the correct behavior, don’t repeat a warning within the same cycle, but do allow it again next cycle. That recency check got commented out during a round of testing, with a commit message along the lines of “switched to a different approach, still testing.” It was never restored. Later, in an unrelated cleanup pass, the commented-out block was deleted outright, along with the field it referenced, erasing the last trace that a real fix had ever existed for this. From that point forward there was no recency check left to restore, only the bare, permanent equality check that had quietly been running the whole time.

Nearly seven years of accounts sliding into permanent exclusion, and nothing about it ever looked like a bug from the outside. No failed job. No error line. No customer support pattern anyone could name, because “never got a reminder” and “never needed one” look identical from a dashboard.

The number that made it real, and the review that made it complete

Before touching anything, the scope got measured against live data with bounded, read-only queries rather than trusted from a code read alone. On the day the fix shipped: 27 accounts in the “month out” window and 1 in the “week out” window were newly eligible for a notice they’d been permanently blocked from, against roughly 520 accounts that were correctly still within a real single-cycle suppression and needed no change. Not a spike. A steady daily leak, the kind that never trips a threshold because there’s no threshold for an email that quietly didn’t go out.

Before shipping, the diagnosis and the planned fix went to an independent second-opinion review, specifically to ask two questions: is the numeric scope right, and is there anything the first pass missed. It found the core diagnosis was right, but the planned fix was narrower than it needed to be. Two adjacent defects were sitting right next to the one being fixed, one where a failed or disabled email send still got recorded as sent, quietly creating the exact same kind of permanent skip through a different door, and one in how the eligible contact for a notice gets resolved when the account’s stated owner isn’t the account’s actual point of contact. Both were real, both were shippable as their own fixes, and both got tracked separately rather than folded into the same hotfix under time pressure. Bundling three different-shaped fixes into one urgent patch is how you introduce a fourth problem while fixing the first three.

The fix, and why it had to go out the moment it shipped

The fix itself restores the thing that got commented out nearly seven years earlier: compare the flag’s timestamp against each category’s own cycle window instead of checking bare equality forever. A flag from the current cycle still correctly suppresses a duplicate. A flag from any prior cycle no longer blocks the current one.

There was no separate backfill step, and that mattered for how carefully this had to be verified before deploy. The moment the corrected query runs on its normal schedule, every account that had been silently and permanently excluded becomes eligible immediately, and gets a real email within minutes. That’s the right outcome, but it means a fix like this ships once, live, to real inboxes, with no dry run against production data possible beforehand beyond the bounded read-only counts already run. Verified on a test account with the same stuck shape first, confirmed the fix released it without also releasing genuine same-cycle duplicates, then shipped it as a hotfix and re-ran the same count query against production afterward.

What actually changed how this gets caught next time isn’t the one-line fix, it’s that the verification query became a standing check instead of a one-off. A permanent flag masquerading as a one-cycle flag will pass every test that doesn’t specifically construct an account that renews more than once after being flagged, and nobody had, for nearly seven years. The thing that finally caught it wasn’t a monitor. It was a customer noticing silence where a reminder should have been, and someone deciding that silence was worth nearly seven years of digging into instead of a one-line apology.