Shazil Khan
Back to portfolioCRM and mobile app · Texas

Roofing CRM and mobile app

The system a roofing and construction company runs on. I took it over half-built, became its only engineer and shipped the app to both stores.

Role
Only engineer since February 2026: backend, web, mobile, infrastructure and store releases
Timeline
2026, ongoing
Stack
React Native, Expo, NestJS, MongoDB, Next.js 16, Socket.io, Cloudflare R2, APNs, FCM, Claude API
Client
A roofing and construction company, through a Dallas agency
Mobile app screens with demo data
49screens in the iOS and Android app
124,227archive files imported, zero errors
~75bugs fixed in one workflow sweep
0.01%of requests failed at 100 concurrent users

The situation

A half-built CRM the company was already running on, left by a previous developer with no working calendar sync, no working QuickBooks sync, no forgot-password and no monitoring.

Anyone could create an admin account through a public sign-up route, QuickBooks routes answered without a login, and months of production work had never been committed.

What I built

  • A native iOS and Android app in React Native and Expo with full CRM parity: jobs, photos with an offline upload queue, document scanner, worksheets and payments, calendar, reports and admin. Live on the App Store as an unlisted staff app and on Google Play.
  • A 70-screen design system built before the app, with a high-contrast theme for use on a roof at noon. The app uses its tokens directly.
  • Push notifications sent directly through APNs and Firebase Cloud Messaging, with no customer names or addresses in the message.
  • QuickBooks Online working and extended: approving a job creates the customer and job number, approving the worksheet creates the invoice, and receipts arrive as Purchases with the photo attached.
  • Two-way Google Calendar and Outlook sync, including repeating meetings and Central-time handling.
  • PandaDoc e-signature: contracts auto-fill from the job and the signed PDF files itself back on the job.
  • An AI insurance-estimate reader on Claude. It reads the carrier's PDF into line items, code re-adds every line against the printed totals, and anything unclear waits for a person.
  • Estimating with a 162-item price list, EagleView measurements and manager approval on discounts.
  • A management dashboard and lead report whose revenue matched the office's own spreadsheet to within $300.
  • A public lead-intake API with per-source keys, UTM and gclid attribution and duplicate merging.
  • A self-hosted database with nightly backups, 16,099 images moved to Cloudflare R2, and 13 years of job history imported with a rollback ID on every batch.
  • Server monitoring that emails only on confirmed problems, and releases that switch in under 10 seconds and roll back by themselves.

What was hard

The iPhone app froze on Add photos

The picker opened while a sheet was still closing, so iOS attached it to a dismissing window and left an invisible layer swallowing every touch. Android never showed it. I fixed the pattern across the whole app and added a test that fails if any sheet skips the handoff.

The CPU steal that wasn't

Staff reported the CRM lagging. A reports query was using 1.7 GB of memory, which I cut to 674 MB, and a hosting CPU cap was showing up as CPU steal. Tuning alone would never have fixed the second one.

Open doors in a live system

I found and closed a public sign-up route that could create admin accounts, QuickBooks routes with no login, calendar routes returning customer data without a login, and password hashes reaching signed-in users.