A multi-tenant event platform that replaced paper, spreadsheets and manual payments for a client — registrations, verified payments, certificates and reminders, run by background workers.
- −35%
- manual reconciliation
- −40%
- organiser follow-up
- 0
- duplicate-charge incidents
One attendee, from an anonymous visit to an issued certificate — through dedup, their organiser’s own payment keys, a signed webhook and a worker that never blocks the request.
SmartFormFlow01 / 07
Public, unauthenticated, multi-step — and every field they touch is tracked.
Drop-off is recorded per field, so the funnel shows where people quit, not just how many.
Closing three cross-tenant data leaks
404, not 403
- Problem
- The multi-tenancy migration exposed three leaks: forms filtered by userId, and two lookups with no organisation check at all.
- Approach
- A manual audit of every backend system; organizationId on every where-clause; isolation tests that expect 404 so another org’s data looks like it doesn’t exist.
- Outcome
- Zero leaks in audited endpoints, with the isolation test as a pre-deploy gate.
Normalising real-world contact fields
1 contact / person
- Problem
- Dedup only matched fields literally named “phone” or “email” — “Mobile Number” or “WhatsApp No.” quietly created duplicate people.
- Approach
- A field-alias registry mapping dozens of real label variants to canonical phone and email, with near-miss logging to grow it from real usage.
- Outcome
- Deduplication that works on how people actually label forms.
A silent Razorpay receipt-limit failure
40 chars
- Problem
- Order creation failed for some organisations with no error at all: the receipt field has a 40-character limit that prefixed UUIDs exceeded.
- Approach
- Strip dashes, keep 30 characters, prefix “rcpt_” — once, at the API boundary. Amounts stay in display units everywhere else; paise only at the call.
- Outcome
- Reliable orders across orgs, and one place for unit conversion.
Three silent failures, three standing rules
3 rules
- Problem
- SMTP broke when Compose read “$” as a variable, Nodemailer bound before env vars loaded, and certificate jobs died on a dynamic import.
- Approach
- Quote “$” in .env; build the transporter lazily on first use; static top-level imports across the worker layer.
- Outcome
- Each incident became a rule instead of a patch — the quoting rule alone has prevented several repeats.
Two-phase migrations on live client data
0 data lost
- Problem
- Schema changes had to land on a live client’s data, where migrate-dev or hand-edited migrations could corrupt history or rows.
- Approach
- Phase 1: additive, nullable columns applied directly and marked resolved. Phase 2: constraints and indexes only after deploy and backfill.
- Outcome
- No data loss across every migration since — now the standing protocol.