Back to Projects
Featured

T Creative Studio

A production SaaS platform replacing 5+ fragmented tools for a multi-vertical creative business

Solo EngineerSoloJul 2025 – Jan 2026

Overview

Role

Solo Engineer

Team

Solo

Timeline

Jul 2025 – Jan 2026

Replaced five fragmented tools (Square POS, Zoho CRM, Google Sheets, Instagram DMs) with a single platform for a multi-vertical creative business after evaluating GlossGenius, Vagaro, and Acuity.

The Problem

Situation

T Creative Studio LLC is a multi-vertical creative business in San Jose, CA spanning lash extensions and skin treatments, permanent jewelry welding, handmade crochet goods and 3D printing, and HR/business consulting. The owner (Trini Lam, Kevin Lam's sister) was running operations across a patchwork of disconnected tools—multiple Instagram accounts for DMs, Excel spreadsheets for client tracking, Zoho for CRM, Square for payments, and marketplace apps (GlossGenius, Vagaro, Booksy) for booking. No single platform supported her multi-vertical pricing models: duration-based lash appointments with deposits, walk-in jewelry welding, e-commerce with shipping, and hourly consulting invoicing. I designed and engineered a production SaaS platform from scratch to replace all of it.

My Goal

Design and engineer a single unified application to consolidate booking, payments, loyalty, messaging, e-commerce, CRM, and compliance for T Creative Studio LLC — replacing GlossGenius, Vagaro, Square marketplace apps, Zoho, and Instagram DMs with one owned system that serves real clients and processes real payments.

My Approach

1

Designed a 40-table Postgres schema organized by business domain (booking, payments, loyalty, messaging, commerce, CRM, compliance, operations) using Drizzle ORM with 35 tracked migrations — all monetary values stored in cents to avoid floating-point errors

2

Built a multi-vertical booking system supporting duration-based lash appointments with deposits, walk-in jewelry welding, e-commerce with EasyPost multi-carrier shipping, and hourly consulting invoices — each vertical with its own pricing model and booking flow

3

Implemented a double-entry loyalty points ledger (balance = SUM of transactions, never a denormalized counter) with admin-configurable reward catalog and a waitlist system using one-time UUID claim tokens with 24-hour auto-expiry

4

Integrated Square SDK v44 for webhook-driven payment reconciliation: order created at booking confirmation, Square payment.completed webhook auto-links payment, awards loyalty points, sends receipt via Resend, records in Zoho Books, and purchases EasyPost shipping label — all in one HMAC-verified handler

5

Built a unified inbox with Supabase Realtime for multi-channel messaging (internal, email, SMS via Twilio), replacing fragmented DMs across multiple Instagram accounts

6

Designed a consistent integration pattern across 9 external services: singleton client module, isXConfigured() guard for graceful degradation, sync_log table for audit trailing — app boots fully even when individual integrations are unconfigured

7

Implemented defense-in-depth security: Supabase RLS at the database layer, CSP + HSTS + X-Frame-Options headers, HMAC-SHA256 webhook signature verification, Cloudflare Turnstile bot protection on all public forms, JWT tokens for waiver signing links

8

Shipped 39 transactional email templates as React components (Resend + React Email), 128 test files across Vitest and Playwright E2E, and 6 engineering reference documents including a disaster recovery runbook

The Outcome

Replaced five fragmented tools (Square POS, Zoho CRM, Google Sheets, Instagram DMs) with a single platform for a multi-vertical creative business after evaluating GlossGenius, Vagaro, and Acuity.

Eliminated double-bookings with PostgreSQL advisory locks per staff member and prevented gift-card double-spend with row-level locking and CHECK constraints.

Automated 19 workflows (booking reminders, lash fill reminders, membership renewals, daily flash reports, low-stock alerts) via Inngest with per-item retry, replacing ~8–10 hours/week of manual DMs and spreadsheet checks.

Built three role-scoped dashboards (owner, staff, client) with revenue forecasting, commissions, and self-serve booking — plus a database-driven admin layer so the owner edits all site content and pricing without code.

Shipped a CI pipeline (typecheck, migration validation via testcontainers, Vitest, Playwright E2E) with S3 nightly backups and a documented recovery runbook.


Technical Decisions

Why I chose X over Y

The platform handles real money — deposits, gift cards, memberships. Required advisory locks for booking serialization, SELECT FOR UPDATE for gift card double-spend prevention, CHECK constraints, 168 RLS policies, materialized views for analytics, and pg_trgm for fuzzy search. No other database provides all of these.

Server actions for ~40 authenticated mutations (colocated with UI, type-safe, progressive enhancement). Route handlers for 6 specific cases: unauthenticated endpoints, payment processing with idempotency, webhook HMAC verification, binary responses (PDF/CSV), file uploads, and cron triggers.

Per-step retry isolates failures (one SMS failure doesn't re-send all emails). Built-in observability dashboard. No Redis infrastructure to maintain. The app just exposes /api/inngest route.

Drizzle generates readable SQL for debugging advisory locks and RLS. Supports raw sql template literals. Zero runtime overhead. Schema-as-code with deterministic migration files.

Defense in depth. If a developer forgets a guard in a server action or the proxy has a bug, the database itself still refuses unauthorized access. For a platform handling payments and personal data, this guarantee matters.


Key Trade-offs

Every decision has costs

Supabase Auth over custom auth

Gained

  • +Production-grade auth with RLS integration out of the box
  • +Google OAuth, magic link, and email verification without writing security-critical code
  • +No custom session management to maintain or audit

Gave Up

  • Coupled to Supabase's auth flow and token format
  • Cannot customize session management as deeply as a custom implementation

Why Worth It

For a solo engineer building a business application, shipping production-grade auth without owning security-critical code is the right tradeoff. Would revisit for SAML/SSO if the business needed enterprise clients.

Drizzle ORM over Prisma or raw SQL

Gained

  • +Type-safe SQL with zero runtime overhead and no binary engine
  • +Schema-as-code with drizzle-kit migration generation
  • +Can drop to raw SQL with type inference intact for complex analytics queries

Gave Up

  • Smaller ecosystem and fewer tutorials than Prisma
  • More initial setup than Prisma's auto-generated client

Why Worth It

Drizzle has no binary engine overhead, generates migrations cleanly, and lets you write raw SQL without losing type safety. For 40 tables and complex multi-join reports, this was clearly the right call.

Procedural Three.js geometry over GLTF models for the 3D landing page

Gained

  • +Zero network cost — geometry generated on GPU
  • +Loads fast on mobile without a loading bar
  • +No binary assets to version control

Gave Up

  • Lower visual fidelity than hand-modeled GLTF scenes

Why Worth It

A landing page that loads fast beats a beautiful one that makes users wait. Procedural geometry delivered the 3D experience without the download cost.

Next.js Server Actions over tRPC or REST for mutations

Gained

  • +Type safety between client and server with zero boilerplate
  • +No additional abstraction layer to maintain
  • +Webhook endpoints still use API Routes where needed

Gave Up

  • Less explicit API contract than tRPC
  • Next.js-specific, reducing portability if frontend ever changes

Why Worth It

tRPC adds real value with multiple frontends or a public API. For a single Next.js app with one frontend, Server Actions give equivalent type safety with significantly less ceremony.

Square SDK over Stripe

Gained

  • +No hardware migration — existing in-studio POS terminal kept
  • +No staff retraining required
  • +Webhook-driven reconciliation links online and in-person payments seamlessly

Gave Up

  • Square's developer ecosystem is smaller than Stripe's
  • SDK documentation is less comprehensive

Why Worth It

Trini was already processing in-person payments with Square. Migrating to Stripe would have required replacing physical hardware and retraining staff. Operational continuity outweighed ecosystem size.

Monolith on Vercel over microservices

Gained

  • +Free to refactor across the entire codebase
  • +Shared types between all modules with no cross-service contracts
  • +Atomic deployments with zero inter-service communication overhead

Gave Up

  • Cannot scale individual services independently
  • Webhook processing and cron jobs share the same deployment unit as the UI

Why Worth It

Microservices are justified by team scale and independent deployment needs. For a solo engineer on a single-tenant SaaS, the monolith's refactoring freedom and operational simplicity win. Would extract webhook processing and cron jobs first if horizontal scaling became necessary.

Building the staff/RBAC system before Trini hired any staff

Gained

  • +Infrastructure ready when business growth required it
  • +No costly schema migration after launch
  • +Assistant portal and commission tracking shipped as first-class features

Gave Up

  • Engineering time spent on features not immediately needed

Why Worth It

Retrofitting a multi-tenant RBAC system and commission engine into an existing schema is significantly more expensive than building it right the first time. The forward investment paid off when Trini expanded the business.

Challenges & Solutions

The hardest problems I solved

1

Solution

PostgreSQL advisory locks scoped per staff + location. hashtext(staffId + ":" + locationId) serializes booking attempts without blocking different locations.

2

Solution

SELECT FOR UPDATE acquires exclusive row lock, check balance, UPDATE. CHECK constraint (balance_in_cents >= 0) as database-level safety net.

3

Solution

Store-enqueue-process architecture: verify HMAC, store raw event, enqueue to Inngest, return 200 in <1s. Inngest handles per-step retry asynchronously.

4

Solution

In-process daily counter with email_queue table fallback. Queue drains on next cron run. Sync log audit trail for every send attempt.

5

Solution

isSquareConfigured()/isZohoConfigured() guards. try* pattern: side effects never block core flow. Health endpoint: healthy/degraded/failing.

What I Learned

  • Advisory locks are the correct mechanism for range-based overlap checks, not unique constraints.
  • Defense in depth (proxy + server actions + RLS) is worth the development overhead for financial platforms.
  • Non-fatal side effects (try* pattern) prevent cascading failures from blocking user-visible operations.
  • Split booking actions into mutations/queries/side-effects early — organic growth creates large files.
  • testcontainers for migration validation in CI catches schema issues before production.

Future Plans

  • +Native mobile app (React Native + Expo) so Trini can manage bookings and view the dashboard on-device without a browser
  • +Multi-location support for studio expansion — current schema is single-tenant but designed to extend with a locations table and location-scoped RLS policies
  • +AI-powered scheduling optimization to suggest optimal appointment slots based on historical booking patterns and service duration data
  • +SAML/SSO integration for enterprise consulting clients who need to authenticate via their own identity provider