Skip to content
experience

The Dubai client who wanted us to rewrite in Node 'because PHP is old'

Ayesha Siddiqui@laravel_ayesha

July 4, 2026 · 2 min read

Story time, because this happened again this month and I want to have a link to send next time.

Logistics client in Dubai. Laravel app we built in 2021, about 40k shipments a month, drivers, tracking page, invoices. They hired a new CTO from a big company. First meeting: "PHP is old, we need to rewrite in Node, NestJS, microservices. How long?"

I did not argue. I said: give me two days to profile, and I will give you two numbers. One for the rewrite, one for whatever the profiler finds. Then you choose.

What two days of profiling found

  • p95 on the API was 820ms.
  • The tracking page had an N+1 on shipment events, about 60 queries per page view.
  • The invoice report did a full scan on a 9 million row table. No index on (client_id, created_at).
  • Sessions were stored in files on an NFS mount. Yes. NFS.
  • OPcache was enabled but with validate_timestamps=1 and a tiny memory setting, so it was checking every file on every request, on the same NFS.

What we changed

; php.ini (production)
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0

With validate_timestamps=0 you must reload PHP-FPM on every deploy, otherwise the old code stays cached. We added it to the deploy script. Then: eager loading on the tracking page, one composite index, sessions moved to Redis.

p95 went from 820ms to 140ms. Three days of work including testing.

The two numbers

Rewrite in Node: 5 months, 3 developers, AED 180,000, and the usual risk that you rebuild the same app with new bugs. Profiling fix: 3 days, already done, here is the graph.

The CTO is not stupid. He looked at the graph for a long time and said "ok". We did agree on one thing he wanted: the live driver tracking with websockets is now a separate service. We used Laravel Reverb for it, but honestly Node would also have been a fine choice there. Use the tool where it fits. Rewriting a working business app because of the logo on the language is not engineering, it is fashion.

Lesson I keep learning: never argue language with a client. Argue with a profiler and two numbers. The profiler wins every time 😊

Get the best of #php_still_pays weekly

One email each Monday with the most useful posts. No spam; unsubscribe in one click.

Comments

11

Sign in to join the conversation.

Sign in to comment
Imran Hussain@imran_builds#3 in PHP Still Pays
'we did not change the server' energy again. Had a near identical one in Leeds, ecommerce client wanted Shopify headless because 'Laravel is slow'. It was one missing index on the orders table. Ey up.
3 net votes
Ankit Khurana@faang_or_bust5 rep in PHP Still Pays
lol 820ms p95 and you call 140 a win. go would do 20ms. php cope
5 net votes
Ayesha Siddiqui@laravel_ayesha#1 in PHP Still Pays
the 120ms left is MySQL and the shipping partner's API. Go would wait for them exactly as fast as PHP does 😐
1 net votes
Guillaume Mercier@guillaume_rale#10 in PHP Still Pays
Bon. The language is never the bottleneck in a CRUD with an external API. I have seen a Go service at 900ms because of the same missing index. Bref.
0 net votes
Ankit Khurana@faang_or_bust5 rep in PHP Still Pays
skill issue then. basic stuff btw
1 net votes
Rohit Verma@rohit_codes4 rep in PHP Still Pays
bhai chill karo. tumne khud abhi bola go 20ms karega, bina profiling ke. wahi galti jo uss CTO ne ki thi 😂
2 net votes
Oluwatobi Adeyemi@tobi_freelance#6 in PHP Still Pays
The two numbers part is the real lesson. Give the client the honest rewrite number next to the 3 day fix. They pick the fix every time, and you look like the adult in the room
3 net votes
Alasdair Reid@onprem_or_die1 rep in PHP Still Pays
aye right. a CTO who wants a rewrite before he's run a profiler. pure mince. at least it wasnae kubernetes
5 net votes
Ayesha Siddiqui@laravel_ayesha#1 in PHP Still Pays
he did mention kubernetes in the second meeting. I pretended my connection dropped
2 net votes
Bram Verhoeven@bram_on_a_bike2 rep in PHP Still Pays
validate_timestamps=0 is the one people forget. Without it OPcache stats every file on every request. On NFS that is not lekker at all.
4 net votes
Hamza Butt@hamza_flutter2 rep in PHP Still Pays
Dubai clients and 'make it modern' yaar 😂 same with my Flutter apps. 'can we do it in the new thing'. which new thing? 'the one my nephew told me about'
3 net votes