Legacy PHP modernization · SaaS and product teams

Modernize Legacy PHP, Step by Step

I help product teams make business-critical PHP systems easier to change, upgrade and scale — incrementally, while the existing product keeps running.

Prefer a fixed-scope first step? See the €1,200 Migration Readiness Audit →

  • Incremental upgrades, refactors and extractions
  • Product stays live while the work happens
  • You work with me — the engineer doing the work
  • Serbia-based · CET/CEST · B2B contract · EUR invoicing · English communication

migration-candidates.txt

monolith/
├── billing        keep in PHP · upgrade framework
├── admin          keep in PHP · low traffic
├── search         extract to Go · latency + CPU
├── event-pipeline extract to Go · throughput
└── reporting      keep, but move off the primary DB

code after the decision

Your PHP application still works. Changing it is the problem.

Not every legacy PHP system has all of these. If two or three are familiar, the next conversation is about sequencing work — not picking a new language.

Every feature touches unexpected parts of the system

A billing change surprises shipping. A small ticket becomes a tour of the monolith.

Releases have become risky

Deploys wait for a quiet window. Rollback is folklore. Nobody wants to ship on a Friday.

Unsupported PHP or framework versions block upgrades

The runtime is past its support window, or a major framework bump is pinned by abandoned packages.

Business logic is buried in the monolith

Rules live in controllers, SQL, and jobs that nobody owns. Changing behaviour means archaeology.

Tests make refactoring difficult — or there are none

Critical paths have no characterisation tests, so every cleanup is a production bet.

Scaling one part means scaling everything

A worker or an endpoint is the constraint, but capacity still means more of the whole PHP fleet.

Engineers are afraid of the critical code

The money path has a reputation. Estimates inflate around it. New hires learn not to touch it.

Some parts would be healthier as separate services

Not because microservices are fashionable — because one workload needs its own deploy, scale, or runtime.

What I actually do

Modernization is a set of options. A rewrite in Go is one of them, and usually not the first.

Upgrade the existing PHP application

  • PHP runtime upgrades
  • Symfony or Laravel modernization
  • Dependency upgrades
  • Technical debt that is actually blocking delivery
  • Tests around critical behaviour

Restructure the monolith

  • Identify domain boundaries
  • Separate tightly coupled components
  • Introduce clearer APIs and interfaces
  • Prepare parts of the application for independent deployment

Extract services when it makes sense

  • A named workload with a contract
  • Independent deploy only where there is an operational reason
  • Rollback before the second extraction

Move selected workloads to Go

  • High-concurrency workers
  • Fan-out and long-lived processes
  • Performance-sensitive APIs
  • Isolated infrastructure components
Keep PHP where PHP works. Replace or extract only what creates measurable engineering value.

Start with the system, not with a rewrite

Before a large migration, look at the running architecture. The output is a concrete modernization roadmap rather than a generic rewrite proposal.

A modernization assessment looks at

  • Current architecture
  • Runtime and framework versions
  • Dependencies that pin upgrades
  • Risky components
  • Deployment process
  • Testability of the money paths
  • Performance bottlenecks
  • Possible service boundaries
  • Candidates for PHP → Go extraction — if any
  • Migration priorities

What you leave with

A written sequence: what can stay, what should be upgraded, where boundaries are missing, and which workload — if any — is worth extracting. A valid result is “keep this in PHP.”

Need a straight answer on the next PHP change?

Send the runtime, the framework, and the part of the system that is painful to touch. You get a technical conversation, not a rewrite pitch.

How modernization works

Incremental modernization instead of a big-bang rewrite.

  1. 01

    Understand

    Architecture, constraints, and the business-critical paths. What actually hurts, not what a rewrite would look like.

  2. 02

    Prioritize

    Identify what causes engineering or business risk. Upgrade, refactor, extract, or leave it alone.

  3. 03

    Modernize incrementally

    Change one slice while the product keeps shipping. No freeze, no big-bang cutover.

  4. 04

    Measure and continue

    Verify the result against the baseline before touching the next part of the system. Stopping is allowed.

Production backend experience

Production PHP and Go

Symfony, Laravel, APIs, workers, and the operational path around them — not greenfield demos.

Data and infrastructure

PostgreSQL, Redis, queues, Docker, Kubernetes, CI/CD. Schema and deploy work included, not bolted on later.

Architecture and incidents

Boundaries, data ownership, service extraction, and production debugging on systems that already make money.

Legacy systems

Unsupported runtimes, tangled monoliths, and migrations sequenced so each step can be reversed.

A real selective extraction: shared uploads out of two PHP products

Two PHP products — Blog and Academy — each contained their own file and image upload implementation. The logic was duplicated, so upload and validation rules had to be kept in sync across more than one codebase.

I proposed extracting upload functionality into a separate backend service.

  • A separate upload service implemented in Go
  • Both PHP backends integrated with the service
  • One PHP application migrated at a time
  • No simultaneous big-bang cutover
  • One implementation of upload behaviour
  • One place for validation changes
  • Blog and Academy stopped duplicating upload code
  • Upload workload became independently deployable
  • Upload workload became independently scalable
  • Heavy image-upload periods no longer required scaling an entire PHP application just to increase upload capacity

The extraction added another deployable service and a network dependency, so the boundary had to justify the additional operational complexity.

Pavel Beliaev

Pavel Beliaev

Senior Backend Engineer

I am a senior backend engineer working in production PHP and Go: architecture, upgrades, and incremental migrations. Engagements run B2B with European product teams.

Questions teams usually ask

Do you recommend rewriting the entire PHP application?

Almost never as a first move. A full rewrite means two systems, a product freeze risk, and rediscovering rules that only exist in the current code. Most of the value is in upgrades, boundaries, and extracting the few workloads that actually need a different runtime.

Can modernization happen while the product stays live?

That is the intended shape of the work. Changes go out in slices, behind flags or side-by-side consumers where needed, with the existing path kept deployable. I do not promise zero incidents or zero downtime. I do design so a step can be reversed instead of becoming a weekend cutover.

Do we have to migrate from PHP to Go?

No. Keep PHP where PHP works. Go is for named workloads where concurrency, long-lived processes, or independent scaling is the actual lever. A valid outcome of the assessment is that nothing should move.

Can you work with our existing engineering team?

Yes. That is the default. I work in your repository, your review process and your tracker. Your team keeps the domain knowledge. I add modernization sequencing and the capacity to execute it without stalling the roadmap.

Can we start with an assessment?

Yes, and that is usually the right first step. The assessment looks at the running system and produces a concrete modernization roadmap — including the recommendation to upgrade in place and leave Go out of it.

Next step

Not sure whether to refactor, upgrade or extract services?

Let's look at the system and identify the safest next step. You talk to the engineer who would do the work.

Serbia-based · CET/CEST · B2B contract · EUR invoicing · English communication