A SaaS platform replacing email + spreadsheet bid workflows for general contractors and subs. Founder, lead engineer, and on-call. End-to-end TypeScript on Node + Postgres, hardened with RBAC, 446 tests across 42 Jest suites, and zero-downtime migrations in CI.
Mid-size general contractors run bid cycles in email and Excel. A single industrial project can pull in 15+ trades, thousands of pages of drawings, and a half-dozen revisions — all coordinated by one PM with a spreadsheet of phone numbers. Bids get missed. Latest drawings get lost. Audit trails don't exist.
ProjectsToBid is the system of record for a bid cycle. GCs invite subs, publish drawings + scope, set due dates, and get back structured bids they can compare. Subs see only what they need. Admins see everything, including a full audit log.

Boring on purpose. Next.js front end and route handlers, Postgres for the system of record, Supabase Storage for plans & documents, and Resend for transactional email. RBAC enforced at the API boundary, then re-checked at the row level for sensitive data.
Every actor in a bid cycle sees a different slice of the same data. GCs see the full project, subs see only their invitation + their bid, and admin staff see everything across tenants. Permissions are declarative, versioned, and unit-tested.
The project detail endpoint was the hottest path on the platform — hit on every navigation. p95 was sitting around 3 seconds, which felt awful and was getting worse as bid cycles grew.
Three changes, in order of impact: collapse a per-bid N+1 into a single joined query with jsonb_agg, paginate the document list (most cycles have 200+ revisions), and cache the role-scoped permission envelope per request instead of recomputing it for each related entity.
Every change goes through the same pipeline: type-check, lint, 446 Jest tests, policy snapshot tests, and a migration dry-run on a copy of production data. Migrations are written in two phases (expand then contract) so we can deploy without locking tables or downtime, even on the bid_documents table that holds the bulk of cycle history.