Breaking up a PHP monolith without taking the platform down
Geniebook · Software Engineer III · Nov 2021 — Aug 2025
Led the extraction of four Go microservices from a PHP/MySQL monolith, delivered over roughly five quarters with no production downtime.
Context
Concurrent load had outgrown the monolith. Under peak traffic the single PHP/MySQL application slowed for everyone at once, because every feature shared one process, one deploy, and one database. There was no way to give the parts under load more room without giving it to all of them, and no way to change one part without redeploying the rest.
Decisions
Absorb the concurrent load inside the monolith, or decompose it into services?
Chosen
Extract four Go microservices, each owning its own responsibility and its own datastore
Decomposition addressed the slowdown and the coupling in the same move. Separating concerns into independent services meant the parts under concurrent load could be scaled and changed on their own, rather than every deploy carrying the whole platform with it.
Rejected alternatives
Scaling the monolith vertically to absorb the concurrent load
More capacity would have bought headroom without separating anything. The slowdown returns at the next step up in concurrency, and every team stays coupled to one deploy in the meantime.
How do you carve services out of a live monolith without a downtime window?
Chosen
Backward-compatible contracts with staged rollouts, sequenced over ~5 quarters (4 building, 1 testing/validating/deploying)
Keeping each contract backward-compatible let the monolith path and the extracted service serve traffic at the same time. Each service could then move behind a staged rollout, one slice of traffic at a time, with the previous path still live underneath it.
Rejected alternatives
A single big-bang cutover in a maintenance window
It would have needed a window the platform did not have, and it offered no incremental way back: a switch that fails, fails for everything at once.
Outcomes
Every figure below is shown with what it measures.
| Outcome | Result | How it was measured |
|---|---|---|
| Production downtime during migration | Eliminated | Stated outcome across the four extracted services over the migration. No incident count is published. |
| Services extracted from the monolith | 4 | Go services carved out of the PHP/MySQL monolith. |
| Peak-concurrency response delays | Eliminated | During peak concurrency, backend requests averaged 5-10 seconds before the migration. The migration eliminated those prolonged delays and improved throughput. |
Architecture
Before, clients reach a single PHP/MySQL monolith that serves every feature from one process and one database. After, the same clients reach an API edge that routes to four independent Go services, each with its own PostgreSQL store, communicating over gRPC for request/response and Kafka for events. During the migration both paths are live at once: the edge sends a growing share of traffic to each extracted service while the monolith continues to serve the rest behind a backward-compatible contract, which is what removed the need for a downtime window.