Data points migrated
Manual work reduced
Subscription automation
Project Snapshot.
The essential facts. Sector, timeline, surface area, and where it lives in production.
Brief, Build, and Outcome.
The Challenge
The client needed a production-grade platform that could ingest a massive parallel dataset, gate it behind a subscription paywall, and handle complex automated workflows all while maintaining sub-2-second page loads and a polished UX. Off-the-shelf tools could not handle the data volume or the custom logic.
Our Approach
We started with a custom design phase in Figma so the client could review every screen in real time, then engineered the platform end-to-end with a dedicated focus on scale and reliability.
The Outcome
The platform launched on schedule and replaced what had been a fragile manual process reducing operational workload by ~80% while serving customers a polished, fast, fully automated experience.
What changed.
A side-by-side of what the client had before we engaged, and what we shipped.
- 240K records living in four near-duplicate spreadsheets that had quietly drifted apart over the years
- Subscriptions confirmed by hand. Somebody checked the inbox and updated a sheet
- Anyone with the public link could pull the entire dataset; there was no real access layer
- Assets dropped into whichever folder was open at the time
- Welcome emails sent manually, usually a day late, occasionally forgotten
- The heaviest query took 8–12 seconds and timed out on bad days
- Every new screen was redrawn from scratch. Buttons didn't match across the product
- One normalized Postgres schema, populated by a migration script we can re-run without fear
- Stripe runs every checkout, renewal, failed card, and refund. No one touches a dashboard
- Tier and role are checked at the database, so a bug in a route handler can't leak paid data
- Three S3 buckets (public, paid, processed), with signed URLs that expire when the subscription does
- Onboarding, billing, retention, and product emails all fire from product events instead of someone's calendar
- Median page load sits at 1.4 seconds, even on the heaviest views
- 40+ screens share one Figma library and one Tailwind config. It finally feels like one product
Achievements & deliverables.
We took a process that lived in four near-duplicate spreadsheets and one overworked person, and turned it into a Stripe-billed product that runs itself.
240,000+ data points migrated
Built a custom ingestion pipeline to move a massive parallel dataset into a fresh Postgres-backed schema with zero data loss.
Subscription billing with delivery logic
Implemented Stripe end-to-end (checkout, plans, webhooks, dunning) with custom server-side delivery rules per subscription tier.
AWS S3 + VPS infrastructure
Provisioned and configured S3 for asset storage and a VPS deployment for the application layer, with secure environment isolation.
Automated email workflows
Onboarding, billing, retention, and transactional emails wired through a single send pipeline no manual sends required.
Auth + role-based access
Sign-up, login, password recovery, and role-gated data access integrated cleanly with the subscription tier model.
Custom Figma design system
Designed every screen in Figma with real-time client review, then handed off to engineering with a documented component library.
How We Built It, Phase by Phase.
Every project follows a deliberate sequence. Discovery, design, foundation, build, and launch. Here is exactly what happened, and what was delivered at each step.
Sitting With the Data
Before any code, we spent two weeks with the client and the data. The legacy dataset wasn't one thing. It was four copies of the same records that had drifted apart over the years. The first real decision was which version got to be the truth. We picked a source-of-truth hierarchy, drafted the new schema, and modeled subscription tiers as their own entity instead of feature flags scattered through the codebase.
- Source-of-truth hierarchy doc
- New Postgres schema (ERD)
- Subscription tier model
- Risk log + rollback plan
Figma First
We designed the whole product in Figma before writing a line of UI. The client joined live review calls twice a week, which stopped us building screens nobody wanted. By the end of the phase, every state was already in the file: empty, loading, error, paid, lapsed, refunded.
- 40+ screens, every state covered
- Component library + design tokens
- Twice-weekly client review (~25 hours total)
- Handoff doc with motion + edge cases
Auth, RBAC, and the Bones
Standard scaffolding, except we stopped to get role-based access right early. A mistake here would have leaked paid data to free users, and we did not want to discover that in week ten. Role checks happen at the database layer, so a bug in a route handler can't bypass them.
- Production VPS + S3 set up
- Sign-up, login, password reset
- Role matrix enforced at the DB layer
- CI pipeline with a staging mirror
Migration + Stripe
The first migration run took six hours and corrupted about 300 rows because of an encoding mismatch in one of the source files. We added a quarantine table, made the job idempotent, and re-ran it in 90 minutes with zero data loss. Stripe got wired the same sprint. Checkout, Customer Portal, and webhooks that update entitlements inside a single Postgres transaction.
- ETL pipeline with quarantine + checkpointing
- Stripe checkout + Customer Portal
- Tier-gated middleware
- Webhook reconciliation report
Automation + Performance
Email automation took longer than expected. The client kept finding edge cases ("what if someone resubscribes after 90 days?") and we wanted each one handled. Performance work moved load times from a 6-second average to 1.4 seconds, mostly by paginating two ugly queries and caching one expensive aggregate.
- Event-driven email pipeline
- Two query rewrites + a cache layer
- QA across active / lapsed / refunded states
- Accessibility audit
Launch
Soft launch on a Tuesday morning. We kept staging warm for a week in case we had to roll back. We didn't. The client trained their team off a 12-page runbook we wrote during QA.
- Production cutover
- Monitoring + alerting
- 12-page operator runbook
- 30-day post-launch support
Inside Each Feature We Built.
Not just what we shipped. How it works under the hood, and the engineering decisions behind each piece.
The 240K-record Migration
Two source spreadsheets, four near-duplicate copies, and one legacy table. We had to merge all of it into one schema without losing anything. And without trusting any single source completely.
- Source-of-truth hierarchy decided in week one. The spreadsheet maintained by the ops lead won when records conflicted
- First run timed out at six hours and corrupted 300 rows from an encoding mismatch; we added a quarantine table and a UTF-8 normalization step
- Final run was idempotent, finished in 90 minutes, and emitted a reconciliation report with row counts per source
- Ran it three times in staging before production. Each run produced byte-identical output, which was the bar
Stripe + Tier-gated Access
Subscriptions, plan changes, refunds. And most importantly, what users can actually see once they pay.
- Tier entitlements are written by Stripe webhooks inside one Postgres transaction; the UI only ever reads them
- Plan changes (upgrade, downgrade, cancel, reactivate) tested against a state machine we drew on a whiteboard before coding
- Failed payment? Dunning emails fire automatically, but access stays live for a 3-day grace window per the client's policy
- Customer Portal saved us from rebuilding plan-management UI ourselves
Access Control at the Database
We didn't want a bug in a route handler to leak paid data to a free user. So we pushed access checks down into the database itself.
- Every sensitive query goes through a scoped function that takes the user's tier and role
- JWT sessions rotate on plan change so a freshly-upgraded user immediately sees their new access
- Audit log records every role and tier change with timestamp + actor
- Manual penetration test before launch found one missing check; fixed the same afternoon
S3, VPS, and the Brief Serverless Detour
We tried serverless first. The cold starts on dashboard loads were ugly. Two weeks in we moved to a VPS and the latency problem disappeared.
- S3 split into three buckets (public, paid, processed) with separate IAM roles per bucket
- Signed URLs for paid downloads expire when the subscription does. Not when the URL was generated
- VPS hardened: SSH on a non-standard port, key-only access, firewall locked to the load balancer
- Nightly DB + S3 backups with monthly restore drills (we've actually done them)
Email That Sends Itself
Onboarding, billing, retention, product alerts. All triggered by what users do, not by someone hitting send.
- One template registry, one send function, locale fallback baked in
- Roughly 200 lines of ad-hoc send code deleted from the early prototype when this landed
- Delivery, open, and click metrics tracked back to user records so the client can see what's working
- The "no more manual emails" rule has held for nine months and counting
The Figma Design System
40+ screens, every state covered, twice-weekly client review during design. By launch the client had spent roughly 25 hours in the file.
- Tokens (color, spacing, type) mirrored 1:1 in tailwind.config.ts
- Every screen has empty / loading / error / success / lapsed states. No improvising in code
- Handoff doc includes motion timings, edge cases, and accessibility notes
- QA found zero design-vs-build mismatches at launch. This almost never happens
Every Tool, and Why We Picked It.
A grouped breakdown of every technology in the build, with the reasoning behind each choice.
Frontend
- Next.js
App Router with server-rendered pages. The product is data-heavy and Google had to be able to read it.
- TypeScript (strict)
Most of the bugs we caught in code review during this build were type bugs that would have shipped without strict mode.
- Tailwind CSS
One config that mirrors the Figma tokens. New screens take an hour instead of a day.
Backend & Data
- PostgreSQL
Picked over a hosted document store because the 240K records had real relationships and we needed real joins. Indexed early.
- Next.js API Routes
One repo, one deploy, one set of types. No separate Node server until the load actually called for it. It hasn't.
- Custom ETL
Off-the-shelf tools couldn't handle the source-of-truth merge across four near-duplicate datasets, so we wrote our own.
Infrastructure
- AWS S3 (3 buckets)
Public assets, paid downloads (signed URLs), and processed thumbnails. Each on its own bucket with its own IAM role.
- VPS
We tried serverless first; cold starts wrecked dashboard load. Moved to a VPS, halved the bill, kept latency flat.
Payments
- Stripe + Customer Portal
Webhooks are the only place tier entitlements get written. The UI reads them, never writes them. That rule has held.
Automation
- Event-driven email
One template registry, one send function, every email triggered by a product event. We deleted ~200 lines of ad-hoc send code when this landed.
- Webhook orchestration
Stripe event → entitlement update → access change, all in one Postgres transaction. A partial failure can't leave a user half-upgraded.
Design
- Figma
Twice-weekly client review during design. By launch the client had spent ~25 hours in the file and there were no surprises in QA.
Raja and his entire Code Nativex team successfully completed a project that connects all the front- and back-end components and all the required logic.



