Problem
REWIND was The Forum's back-to-school party for SFU students at Celebrities Nightclub on
September 20, 2026. The venue's ticketing contract meant general sales had to go through
TicketWeb, which takes a large cut of every ticket: $30 all-in. Discounted presale and
friends-and-family entry were ours to run, at $5 at the door with no fees.
So the club needed one site for the whole campaign: collect a mailing list before the
drop, run capped discount guest lists shared privately by promoters, switch to ticket
sales the moment the drop happened, and measure which ads produced signups. I was the
only person writing code, and I owned the architecture, schema, API, front end and
deployment.
Design choices
- Capacity enforced in one locked database function. Each guest list had 75 spots,
and the links went out in group chats, so signups arrived in bursts. Counting and then
inserting would let two people both see 74 and push the list to 76, and the club pays
for every extra spot at the door. A single PL/pgSQL function locks the list's row
(
SELECT … FOR UPDATE), then runs the duplicate, signup-window, age, rate-limit and
capacity checks and the insert while holding the lock. Different lists never block
each other.
- Postgres as the source of truth. Each signup is committed before any third party
is called, and every row records whether its Brevo sync succeeded. An outage leaves a
row marked for backfill instead of losing a signup. When Brevo rejected a whole
contact over a phone-number conflict, the sync retries once without the phone, under a
single deadline.
- Built to absorb change. The event was rebranded from "Back 2 School" to the
Y2K-themed "Rewind" after the site was built, and the poster art didn't exist yet. Key
art is a swappable layer, every string and price lives in one config file, and the
palette is a token set, so the rebrand landed as content changes rather than a rewrite.
- Released in the order the campaign needed. Lists are configuration, not code:
presale went live first, then general sales, then friends and family as its own pool
of 75. That last one was one config entry and one migration, without touching lists
already taking real traffic.
- Retargeting and attribution. The Meta Pixel and Conversions API report visits and
signups server-side, with UUID event deduplication. That let the club retarget ads at
people who visited the site but didn't sign up, and see which ads produced signups.
Impact
- Ran the full campaign end to end in production, through to an event that sold
600+ tickets, with 150 discounted guest-list registrations captured in our own
database.
- No list ever went over its cap during the signup bursts.
- About 15,000 lines of TypeScript and 850 of SQL, with 397 tests. Some of the
tests read the SQL migrations directly and fail if a database function's signature
drifts from the code that calls it.
- 91% automated background CRM sync rate and 100% server-side conversion delivery.
What I learned
- Lay out trade-offs in plain language before anyone commits: what a choice costs, what
it buys, and what I expect to happen. "There's no wrong choice, only a misinformed one."
- Spend more time on requirements up front, and book a second meeting after a first
attempt, because the real issues only surface once something exists.
- Design the schema to capture more than I think I need. It's easy to ignore a column,
and impossible to report on data that was never recorded.