My headline feature — automatic follow-ups in the same email thread — was quietly a no-op for every brand-new email. It took me a while to see why, because nothing errored. It just... didn't follow up.
Here's the trap.
To send a follow-up in the SAME conversation, I need Gmail's thread ID. My extension intercepts the email at send time and grabs what it can. Problem: at that exact moment, Gmail hasn't assigned a thread ID yet — it's assigned after the message is actually delivered. So for a fresh email, the thread ID I captured was empty.
My old logic saw an empty thread ID and permanently skipped that email with reason "no_thread." Which means: the feature worked fine in tests (where I fed it a thread ID) and was dead on arrival for every real first email a user sends. The worst kind of bug — green tests, broken product.
The fix had to respect a hard privacy constraint, which made it interesting. EmailKnow only holds the gmail.metadata scope — read email HEADERS, never bodies. And that scope explicitly forbids two things: the q search parameter and reading message content. So I couldn't just search "find the message I just sent."
So the backend backfills the thread ID the honest, allowed way, on the Cron that runs every 17 minutes:
- Take emails that still have no thread ID, aren't replied yet, still have a pending follow-up, and were sent within the last 24h.
- List the SENT mailbox with labelIds=SENT and format=metadata (headers only — no q param, no body, ever).
- Match by recipient + subject + a time window. If several match, take the one closest to the send moment.
- Write the thread ID back (idempotently: only where it's still null).
And the key behavior change: an email that's due for follow-up but still has no thread ID no longer gets thrown away as "no_thread." It stays pending and retries next cycle, until it either resolves or ages out of the 24h window. Failing safe, not failing silent.
The lesson I keep relearning: a test that passes because YOU supplied the perfect input isn't testing the real world. The real world hands you an empty string at the worst possible moment.
What's your best "green tests, dead feature" story?
— building EmailKnow in public, #3
Top comments (0)