Case Study · Sunny Sahijwani

Sizing production for a replatform launch — from real traffic data, not guesswork

Role: Technical Lead for a European D2C fragrance brand (Laravel 11 + Next.js, 8 markets, multi-currency)

Client anonymized throughout — happy to walk through full details in a personal conversation.

The question every replatform has to answer

The day you leave a SaaS platform like Shopify, capacity stops being someone else's problem. Shopify absorbs your Black Friday; your own servers don't — unless you sized them for it. Before our first market launch I had to answer, with real money on the line: how much server do we actually need?

Most teams answer this by feel — buy something big, hope it holds. I answered it with the one dataset every replatforming business already owns: the analytics of the store being replaced.

Step 1: Profile the real traffic

The existing Shopify store had years of traffic history. From Shopify Analytics I worked primarily with two reports: "Online store sessions over time" at hourly granularity and "Total sales over time", profiled across three distinct regimes:

From the sale-period peaks I derived a target load envelope: peak-hour sessions and orders converted into requests per second, with headroom on top — because the number that sizes your infrastructure is not your average day; it's your best day, plus margin.

Step 2: Translate traffic into hardware — two different bottlenecks

The frontend and backend of a Laravel + Next.js stack don't scale on the same axis, so they got different server profiles:

Topology at launch: deliberately small but redundant — one frontend and two backend servers behind a Cloudflare load balancer. Not the biggest instances money could buy: the instances the data said we needed, with a scaling path if the data changed. All three sat with a single hosting provider — a concentration whose true cost became clear later (see the failover postmortem).

flowchart TB
  A["Shopify Analytics — 'Online store sessions over time' (hourly) + 'Total sales over time'"] --> B["Traffic regimes: weekday baseline · weekend · sale-period peaks"]
  B --> C["Target RPS envelope = sale peak + headroom"]
  C --> D1["Frontend: CPU-bound Next.js SSR — CPU-optimized box, 8 PM2 forks behind nginx least_conn"]
  C --> D2["Backend: RAM-bound PHP-FPM — RAM-rich boxes, maximum worker pool"]
  D1 --> E["Launch topology: 1 frontend + 2 backend behind a Cloudflare LB"]
  D2 --> E
  E --> F["k6 proof: 24,440 requests, ~198 RPS sustained, zero HTTP errors"]
  F -. "findings feed back: FPM is the ceiling, DB at 25%, two mis-keyed rate limiters removed" .-> E
    

The sizing pipeline: analytics → traffic regimes → hardware math per bottleneck → load-test proof, with findings fed back into the topology.

Step 3: Prove it before customers do

Projections are hypotheses. Before launch I wrote k6 load-test shell scripts, ran them against the production stack while watching from the server side, and got the envelope confirmed: 24,440 requests over 2 minutes at ~198 RPS sustained, zero HTTP errors — comfortably above the derived sale-peak target.

The test earned its keep twice over. It identified PHP-FPM as the true scaling ceiling (the database sat at 25% connection capacity — so scaling means more FPM capacity, not a bigger DB). And it exposed two mis-keyed rate limiters — one bucket shared by all anonymous traffic behind Cloudflare's NAT — that had been silently causing a ~6% masked failure rate. They were removed and replaced with a deliberate access policy: the frontend servers whitelisted for unthrottled GraphQL, everything else rate-limited.

The payoff — twice

The launch and the first sale period ran on this topology without a capacity incident: page performance held through peaks the analytics had predicted almost to the hour.

The second payoff came under much worse circumstances. When the hosting provider itself suffered a total outage mid-sale (that story is its own postmortem), I had to stand up an emergency environment at a second cloud provider with a €40K+/hour sale running. Because the capacity envelope was already known, resizing the failover box was arithmetic, not guesswork — I set it to 8 vCPU / 32 GB RAM before it saw a single customer, confident it would hold peak sale traffic. It processed 4,000+ orders that day without strain.

The principle

Capacity planning is data + arithmetic + a load test. The traffic data tells you what to expect, the hardware math tells you what to buy, and the load test tells you whether you were right — before your customers do. "Buy the biggest instance" is not a strategy; it's a way of paying monthly for not knowing your own numbers.

More case studies