Web development case studies with numbers you can check
There are four projects on this page. For each one you get the number before I started, the number after, and a link to the site so you can open it and see the thing still running. I have said where a figure came from in every case.
Three of the four came out running on the same server they went in on. Nobody paid for a bigger instance. The capacity was already there on the invoice, being spent on work the application had no reason to be doing, which is usually where the money is hiding.
Four projects and what changed
Each figure links down to the case study it came from, where I go through what was wrong and what I changed about it.
Where these came from. Every figure on this page came off a monitoring dashboard or out of an automated email the platform itself sent. Where a number is the client's to release, I have said so on the page instead of quietly rounding it into something flattering. All four sites are live and linked, so opening them is the fastest sanity check available to you.
ArcticBlue: average CPU from 60% to 5% on the same server
Project facts
Client: ArcticBlue, Australia
Live site: www.arcticblue.au
My role: Sole developer on architecture, migration and hardening
Stack: Laravel, MySQL, Ubuntu, Cloudflare, DigitalOcean
Go live: April 2026
What was wrong
The production droplet sat above 60% average CPU and touched 100% often enough that the site went down inside business hours. A large share of that load was bot traffic nobody had ever filtered, so hostile crawlers were reaching PHP and MySQL and burning cycles that paying customers needed.
The constraint I had
Everyone's first suggestion in this situation is a bigger instance. That raises the monthly bill permanently and leaves the same wasted work sitting in the request path, so the brief I took on was to bring the load down without touching capacity.
What I changed
I rebuilt the application on Laravel with the data layer indexed properly and the heavy query paths reworked so they hold together when a few dozen people are on the site at once. Bot detection and blocking now runs in front of the application, which stops crawlers at the edge before they ever reach the database. The droplet itself did not change, same size and same region as it was on day one.
The measured result
Average CPU utilisation
Server load average
| Metric | Before | After | Change |
|---|---|---|---|
| Average CPU utilisation | Around 60%, peaking at 100% | Around 5% | 92% lower |
| Load average (1/5/15) | Around 5.0 | Around 0.4 | 92% lower |
| Downtime from CPU saturation | Recurring in business hours | None observed | Gone |
| Bot traffic handling | Unfiltered, reached the app | Blocked at the edge | Automatic |
| Instance size and monthly cost | Baseline | Unchanged | No increase |
ArcticBlue's first customer enquiry through the rebuilt site arrived within 24 hours of go live. Order and revenue figures belong to them, so I am leaving those out of this. The CPU and load numbers are mine to show and they are the ones that explain why the site stopped falling over.
Check it yourself. The CPU and load figures come from the DigitalOcean monitoring graph on the production droplet, comparing the ten days before go live against the days after. Open www.arcticblue.au and time it. The old arcticblue.net.au host returns a 301 to the new one, which you can see for yourself with curl -I, and that is how the site held on to its search rankings through the move.
Let's Calendar: from 5,000 contacts in 15 minutes to 100,000 in under a minute
Project facts
Product: Let's Calendar, bulk calendar invite SaaS
Live site: www.letscalendar.com
My role: Designed and built the import pipeline
Stack: PHP, MySQL, queued background workers
Verified run: 13 May 2026, import ID 539DHcMfNs
What was wrong
Bulk contact import gave up somewhere around 5,000 rows. Past that the request hit PHP's execution timeout or ran out of memory, and the user got back nothing they could use. Whatever error surfaced did not say what had gone wrong and there was no partial import left behind to salvage. That 5,000 ceiling was not even dependable, because two people importing at the same moment could break it well below the number. A run that did succeed still meant sitting on a loading page for roughly 15 minutes.
The constraint I had
Same instance again with no capacity increase, and one tenant's import was never allowed to slow down anybody else on the platform. That second half is why raising the time limit was never going to be the answer here.
What I changed
I took the whole import out of the HTTP request and moved it into chunked background work. It stopped running inside a page load, which also keeps it out of the way of live traffic. Validation happens row by row inside the pipeline as the rows go through it. The user now gets two emails from the system: one when the import is queued and being verified, then one on completion carrying the totals, the duplicates, the skips, and every failed row itemised with its row number and the reason it was refused.
The measured result
Contacts per import
Time to complete
| Metric | Before | After | Change |
|---|---|---|---|
| Maximum contacts per import | Around 5,000, lower under load | 101,096 verified, 500,000 tested | 20x verified |
| Wall clock time | About 15 min for 5,000 | Under 1 min for 101,096 | Around 300x throughput |
| Behaviour past the limit | Timeout or memory exhaustion, no result | Completes, bad rows reported one by one | Failure mode removed |
| Two imports at once | Degraded each other | Isolated background execution | No cross tenant impact |
| Error reporting | Nothing usable | Row number and reason, by email | Added |
| Instance size | Baseline | Unchanged | No increase |
On the run I have the emails for, 101,096 contacts went in and 100,747 were added. The 349 that failed were refused deliberately. That spreadsheet had unresolved CONCAT formulas sitting where the email addresses should have been, so those rows carried no address worth importing. The completion email listed all 349 by row number with the reason against each one.
Check it yourself. These figures come off the two emails the platform generated for import ID 539DHcMfNs on 13 May 2026. The verification notice went out at 16:37 and the completion report landed at 16:38. Those two timestamps are the whole evidence for the speed claim, and the system wrote both of them without my involvement.
AMRIOP: taking every manual step out of filing a receipt
Project facts
Product: AMRIOP, digital receipt management SaaS
Live site: www.amriop.com
My role: Architecture, backend, SSO and mailbox integration
Stack: Laravel, MySQL, OAuth SSO, mailbox APIs, Flutter app
Status: Live
What was wrong
The product worked and the onboarding was killing it. A new user filled in a signup form by hand. Then for every receipt they wanted tracked they went into their email, found it, downloaded the attachment, uploaded it to AMRIOP, and tagged it themselves with category, store and location. Five manual steps per receipt, on a tool whose whole promise is that you stop doing admin. People file a handful and then quietly give up.
The constraint I had
Reading somebody's mailbox is a trust and permission problem before it becomes an engineering one. The consent had to be explicit, narrowly scoped, and collected once while the user was already weighing up whether to trust the product at all, so burying it in a settings screen was ruled out from the start.
What I changed
Manual registration came out and single sign on went in. The mailbox permission grant now sits inside that same flow, so consent happens at the one moment the user is already deciding whether this product is trustworthy. After that the platform goes and collects retailer receipts from the inbox on its own, categorises them as they arrive, and puts them in a dedicated Receipt by Email tab with a count on it, which turns the review job into a glance.
The measured result
| Step in the user journey | Before | After |
|---|---|---|
| Signup | Fill in a form by hand | Single sign on, one click |
| Mailbox permission | Did not exist | Granted once, inside signup |
| Getting a receipt in | Open email, find it, download, upload | Fetched automatically |
| Categorising | User tags category, store and location | Categorised on arrival |
| Where receipts land | Mixed in with manual uploads | Own Receipt by Email tab, with a count |
| Manual steps per receipt | Four or five | Zero |
The honest framing on this one is that the win is a deleted workflow, so there is no percentage for me to quote at you. Seven receipts were sitting in that tab already categorised on an account where nothing had been uploaded. How this plays out on retention over a year sits with AMRIOP to publish. The part I can show is that the manual path no longer exists.
Check it yourself. Sign up at amriop.com with SSO and watch what the dashboard does while you upload nothing. The sync reports back what it found, and the receipts it collected appear under their own tab with retailer tags already on them.
I Siente: 2.5 times the order updates for 84% less per order
Project facts
Client: I Siente, India
Live site: www.isiente.com
My role: Designed and built the integration
Stack: Laravel, WhatsApp Business Platform on the Meta Cloud API, Shiprocket webhooks
Message type: Approved utility templates
What was wrong
Order status messages on WhatsApp were going out through Shiprocket's own messaging, billed at ₹7 for four status updates per order. That price is a reseller margin sitting on top of Meta's own rate. The costlier problem is that four updates leaves long gaps in the delivery story, and those gaps are where customers start writing in to ask where their order has got to.
The constraint I had
Shiprocket had to stay in place as the logistics provider, so the job came down to keeping them for shipping and bringing the messaging back in house.
What I changed
I integrated the WhatsApp Business Platform straight with Meta and wired it to Shiprocket's status webhooks. Every status change the courier reports now fires its own approved utility template, which takes the customer from four touchpoints across an order up to ten. Cost per message drops to Meta's published utility rate for India of ₹0.11.
The measured result
Messaging cost per order
Status updates the customer gets
| Metric | Before, via Shiprocket | After, direct Meta API | Change |
|---|---|---|---|
| Status updates per order | 4 | 10 | 2.5x more |
| Cost per message | ₹1.75 effective | ₹0.11 | 94% lower |
| Total messaging cost per order | ₹7.00 | ₹1.10 | 84% lower |
| Saved per order | n/a | ₹5.90 | While sending six more messages |
| What triggers a message | Shiprocket's own processing | A webhook per status change | Real time |
| Per 1,000 orders | ₹7,000 | ₹1,100 | ₹5,900 saved |
The like for like comparison is the sharper number. Ten updates through the reseller at ₹1.75 each would have come to ₹17.50, and the same ten direct from Meta cost ₹1.10, so 94% less for identical output. The 84% in the table is the conservative version, weighing what I Siente paid before against what they pay now, and it undersells the change because they are getting 2.5 times the messages for that money. A line item like this gets renewed every month and read by nobody, which is why it was worth an afternoon with Meta's pricing table.
Check it yourself. Meta publishes its per message utility rates for India openly, so the ₹0.11 half of this is verifiable on the WhatsApp Business Platform pricing page without taking my word for anything. The ₹7 for four statuses is what I Siente was being billed before the switch.
What the four have in common
Four clients across three countries on different stacks, with the same move sitting underneath each one.
The bill stayed where it was
ArcticBlue, Let's Calendar and I Siente all came out running on the same infrastructure they went in on. That capacity was already paid for every month, being spent on work the application had no business doing in the first place.
The ceiling was in the software
A 5,000 row import limit and 60% CPU on a quiet store are software problems wearing a hardware costume. You can resize the droplet and buy yourself a few months that way. The ceiling itself only goes once the work comes off the request path and out of the hot query.
Failure got honest about itself
The old import died without explaining anything and the old site simply went down under load. Bad rows now come back itemised by row number, bots get stopped at the edge, and every courier status change fires its own message. Systems that explain their own failures cost far less to keep running.
Manual steps got deleted
The AMRIOP fix removed the reason a person had to be in the loop. A tidier upload screen or a clearer help article would have left exactly the same work sitting on the user. Automation that still needs supervising is a workflow with extra steps bolted to it.
Somebody read the pricing page
I Siente's 84% came out of one person comparing a reseller's rate against the platform's published rate, then doing the integration work properly. There was nothing clever in the engineering. Recurring per transaction costs are worth auditing precisely because nobody ever gets round to it.
One person, holding the whole thing
On all four of these I was the developer at the keyboard, with nobody in between me and the code. That is why I can give you the before number and the after number off the same dashboard, because I was the one sitting there watching it move.
Other projects
Work I have shipped where the before and after figures are the client's to release. Listed here for scope.
HRBingo, HR and office management SaaS
A full HR platform covering employee records, leave, attendance and role based access across an organisation hierarchy, on a multi tenant Laravel backend. Live at hrbingo.com.
Cityfurnish, technical team lead
I led full stack delivery on more than eight projects for one of India's larger furniture rental platforms, covering Zoho CRM integration, automated invoicing, custom WordPress plugins and AWS infrastructure carrying live commercial traffic.
Google Calendar integration app
Built at Manomay Informatics inside a multi product SaaS suite, alongside Zapier and OpenAI API integrations that took manual operations down by 80% for enterprise clients.
Stripe and Zoom platform work
SaaS product development at Envate Technologies, including the payment and video integrations that delivered a 60% speed improvement on the core user flows.
Bring me the number you want moved
If something in your product runs slow, hits a ceiling, falls over under load, or quietly bills you more than it should, tell me what it is and what it costs you at the moment. You will get a straight answer on whether fixing it is worth the money, roughly what the work takes, and whether you need me for it at all. There is no proposal template waiting at the end of this.
More on how I work and what I build: website development services, the full service list, working with a developer based in Kanpur, or my background and work history.
Written by Prashant Singh, who did the work described above. Last reviewed August 2026.
© 2026 Prashant Singh – Web Developer. All rights reserved.
