Case Study 01 — Fullstack Return Scheduling & Operations

Quick Ship Today

"An automated return scheduling system and a content platform that let a logistics business run without waiting on a developer for every change."

~45 min Est. time saved per return (vs. drop-off at UPS/FedEx)
20+ Reusable CMS content blocks shipped
1 Engineer, end-to-end (design → deploy)

01 / Overview

Two Problems, One Platform

Quick Ship Today is a returns logistics business. Instead of driving to a UPS or FedEx counter, customers ship products back through the platform. I was brought on as the sole engineer to build the product from scratch.

There were really two problems here, and they pulled in different directions. Customers needed a return flow that could handle real-world messiness: smudged labels, inconsistent formatting, unpredictable order volume. The business side needed a marketing team that could ship landing pages on their own, without filing a ticket every time the copy changed. I built both: a return scheduling and order-processing system, plus a component-driven CMS layer sitting on top of the same Next.js app.

02 / The Challenge

State You Can Trust, Content You Don't Have to Touch

The scheduling side was the harder problem. A return isn't a single action. It's a sequence of states (scheduled, label received, processed, shipped) that has to stay consistent even when a step fails, a coupon is involved, or a payment through Square needs to be captured or refunded. Any gap in that state machine turns into a support ticket or a lost package.

On top of that, labels arrive as photos, not clean data. I needed to pull structured information, like tracking numbers, carrier, and addresses, out of images customers snap on their phones, then shape that into something the backend could reliably process every night, no matter how many orders came in that day.

On the content side, the constraint was different. The marketing team needed to build and edit landing pages independently, without pull requests and without waiting on me, while I still needed enough control to add new block types as the business grew.

03 / The Solution

Next.js, tRPC, and a Type-Safe Order Pipeline

I built the app on Next.js and TypeScript with tRPC end-to-end, so the same types describing an order or coupon on the backend are enforced on the frontend. No drift between what the API returns and what the UI expects, which matters a lot when you're modeling money and shipping state.

For content, I integrated Sanity CMS and built 20+ reusable content blocks, like hero sections, pricing tables, testimonial rows, and FAQ accordions, so the marketing team could compose new landing pages from existing pieces without touching code, while I kept control over what a 'block' was allowed to be.

To close the visibility gap on operations, I built an internal admin dashboard covering users, orders, tracking status, and a centralized error log, so issues could be caught and triaged without digging through Firebase logs by hand.

04 / Technical Deep-Dive

Reading a Photo of a Shipping Label at 2 A.M.

The hardest part of the system was turning a customer's photo of a shipping label into structured, processable data reliably and without a human in the loop. I used Google Cloud Vision's OCR to pull raw text from the uploaded image, then ran that text through an LLM to normalize it into a consistent format the app could work with. OCR output for something like a shipping label varies a lot depending on font, glare, and how straight the customer held their phone.

Order processing runs as a nightly batch job on Cloud Functions via Cloud Scheduler, so every return that came in during the day gets processed together instead of one at a time in real time. Failed orders retried automatically, and I designed the batch job with a future dedicated retry queue in mind, in case order volume ever grew enough to need one.

That batch design surfaced a real bug. Processing orders in bulk meant hitting external APIs, like OCR and carrier lookups, in a tight loop, which occasionally tripped rate limits and caused a batch to fail partway through. I fixed it by swapping the naive all-at-once approach for controlled concurrency using p-limit, streaming requests through a fixed number of concurrent slots instead of firing everything at once. It's a small change, but it's the difference between a batch job that's fast until it isn't, and one that's dependable every night.

Testimonial
"Working with Ayush has been an outstanding experience. His communication was clear and timely, making collaboration smooth and efficient. Ayush was highly available and responsive to project needs, ensuring that all tasks were completed on schedule. The quality of his work exceeded expectations, with careful attention to detail and a strong commitment to delivering high standards."
— Doug Warner
Visits: ...