feat: signup nudge email sequence for members without a chapter - #2835
Conversation
2fcdcde to
3d1ae67
Compare
|
This looks awesome! Could we tweak the backdate to one month, rather than 2 weeks. Here is the copy for that first reminder email https://docs.google.com/document/d/1R2DjZiSoBVMnfp0mBS4nz8rMKywlyhhmb9exuDYAUR0/edit?usp=sharing, I'll add to the document today for the second email. Thaaaank you :) |
…apter Members who sign up but never subscribe to a chapter get at most two emails, recorded in member_email_deliveries (typed log from #2832): 1. Nudge 7 days after signup to members created in the last 30 days who have no subscription and are not banned. 2. One follow-up a month after the nudge if they still have none. Stage eligibility derives from the typed log rows; delivery-confirmed writes via the EmailDelivery concern (email_type signup_nudge / signup_nudge_followup) make a lost send retry within the window while the (member_id, email_type) unique index prevents duplicates. Members who subscribe exit the sequence at any point. Daily entry point: rake chaser:signup_nudges (add to Heroku Scheduler after merge). Both emails currently use the copy from Kimberley's first-email draft; dedicated follow-up copy is pending (issue #2384).
1637d99 to
0ef2971
Compare
|
@KimberleyCook, the date changes have been applied. I've used the same content for both e-mails for now, so we can ship this today. It's a 5 minute job to replace the content of the second email, once it's ready. |
| end | ||
|
|
||
| def self.never_subscribed | ||
| Member.where.not(id: Subscription.select(:member_id)) |
There was a problem hiding this comment.
If the Subscription table grows very long, hm, could this be a very long SQL query? Perhaps there are other ways to tell the Member model to exclude all subscribers? A subquery or something.
There was a problem hiding this comment.
The query was already a subquery — where.not(id: Subscription.select(:member_id)) generates NOT IN (SELECT member_id FROM subscriptions), an uncorrelated subquery that Postgres runs as a hash anti-join, so it stays linear even as the table grows.
Your comment did surface a real bug though: both member_id columns are nullable in the database, and a single NULL row would make NOT IN exclude every member — the daily job would send nothing. Fixed by filtering NULLs inside the subqueries, with a regression test.
| @@ -0,0 +1,13 @@ | |||
| RSpec.describe 'rake chaser:signup_nudges', type: :task do | |||
There was a problem hiding this comment.
require the rails_helper - at the top of the file.
There was a problem hiding this comment.
Done — added require 'rails_helper' at the top.
| @@ -0,0 +1,100 @@ | |||
| RSpec.describe SignupNudgeEmailService, type: :service do | |||
There was a problem hiding this comment.
Done — added require 'rails_helper' at the top.
… specs Addresses review feedback from #2835: - never_subscribed and unemailed: both member_id columns are nullable in the DB, and a single NULL row would make NOT IN exclude every member, silently disabling the daily job. Filter NULLs inside the subquery. The query stays an uncorrelated subquery (hash anti-join), which is what the reviewer suggested. - regression test: rows with NULL member_id must not block sends - require rails_helper at the top of the two new spec files
Description
Implements #2384: automated emails to members who signed up via the website but haven't subscribed to a chapter.
A bounded two-stage sequence, recorded in
member_email_deliveries(typed log from #2832):Stage eligibility is derived from the typed log rows; delivery-confirmed writes (via the
EmailDeliveryconcern,email_type: signup_nudge/signup_nudge_followup) mean a lost send retries within the stage-1 window and no member receives the same stage twice — enforced by the(member_id, email_type)unique index. Members who subscribe at any point exit the sequence.Email copy: both emails currently use the copy from Kimberley's first-email draft (subject "Let's get you connected with codebar!"). Dedicated follow-up copy is pending from Kimberley (issue #2384) and will land as a small follow-up commit.
What happens when (plain language)
Every day, one scheduled job looks at recent sign-ups and sends at most two emails — then never bothers anyone again:
People who are exempt at every stage:
Members who signed up but never finished the signup form are included — the nudge is for everyone who created an account and walked away.
Driving it: one daily task (
rake chaser:signup_nudges) that must be added to the Heroku Scheduler after merge — see the ops note above.Ops note⚠️
After merge, add a Heroku Scheduler entry:
rake chaser:signup_nudges, daily — same mechanism asrake chaser:three_months. Without it the feature ships dark. Post-merge verification (first scheduled run createssignup_nudgerows for the current cohort): Morgan.Steps to verify
make test— service spec covers the full stage matrix (window edges, subscribed/banned, both rows = terminal, follow-up anchored to the nudge's send time); mailer specs cover headers, body, and typed logging; rake spec covers the entry point.