Skip to content

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.

  • Go
  • gRPC
  • Kafka
  • PostgreSQL
  • PHP
  • MySQL
  • Kubernetes
  • AWS

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.

Measured outcomes: Breaking up a PHP monolith without taking the platform down
OutcomeResultHow it was measured
Production downtime during migrationImprovementEliminatedStated outcome across the four extracted services over the migration. No incident count is published.
Services extracted from the monolithScale4Go services carved out of the PHP/MySQL monolith.
Peak-concurrency response delaysImprovementEliminatedDuring peak concurrency, backend requests averaged 5-10 seconds before the migration. The migration eliminated those prolonged delays and improved throughput.

Architecture

Extraction path: one PHP monolith to four Go services

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.