Part 1 of 4 in Strangler Fig migration

Why We Migrated from Zend Framework 1 to Symfony 8 in 2026

Zend Framework 1 has been end-of-life since 2016. PHP 5.6 receives no security patches. But the application still worked — so why take the risk? The decision process behind migrating a production HR platform from ZF1 to Symfony 8.

A balance scale weighing a cracked gray scaffold marked with a warning shield against a solid blue scaffold

This is the first article in a series documenting the migration of a production HR and Payroll application from Zend Framework 1 (PHP 5.6, MySQL) to Symfony 8 (PHP 8.4, PostgreSQL). This article covers the context and the decision process: why migrate, why Symfony, and why not a rewrite.

The starting point

The application had been in production for years. It handled payroll for hundreds of employees across multiple countries: salary calculations, tax deductions, leave management, recruitment pipelines, document signing. It was a working piece of software that the business depended on.

But the technical foundation was crumbling:

  • Zend Framework 1, last stable release December 2012, officially end-of-life since September 2016
  • PHP 5.6, end-of-life since December 2018, receiving no security patches
  • MySQL, the version running had known vulnerabilities
  • No automated deployment, no CI pipeline, no tests
  • The sole infrastructure knowledge lived with one person

The risks of staying

We evaluated what staying on ZF1 meant in concrete terms:

Security. ZF1 has no security releases. A vulnerability discovered in 2025 in a ZF1 component is a zero-day with no patch. Same for PHP 5.6: CVE entries accumulate, and the only mitigation is migration away from the version.

Maintenance. PHP 5.6 cannot run on modern infrastructure. Docker images for 5.6 are becoming harder to find, and many PHP extensions no longer compile. Adding a new feature meant fighting the framework. ZF1 has no autowiring, no attribute-based routing, no modern ORM.

Talent. Finding a developer who knows ZF1 in 2026 is difficult. The framework was popular in the late 2000s, but most developers experienced with it have moved on. The bus-factor risk was real.

Why not a big-bang rewrite

A rewrite was the obvious alternative: build the new Symfony application from scratch, deploy it, and turn off the old one. We rejected this for three reasons:

No feature freeze. The business could not stop adding features for six months. Payroll compliance changes, new country requirements, legal adjustments. These came in regularly throughout the migration. A rewrite requires a frozen scope.

Unknown unknowns. A legacy codebase contains business rules that nobody remembers. Payroll tax calculations, rounding rules, specific client configurations. Rewriting meant rediscovering these one bug report at a time.

No parallel runs. With a rewrite, you deploy the new system and hope it works. If something is wrong, rolling back means downtime. The Strangler Fig pattern let us run both systems in parallel and switch routes gradually.

Why Symfony 8 over Laravel or a modern framework

We did evaluate alternatives:

Laravel. The most popular PHP framework in 2026. Excellent ecosystem, huge community. But the application was structured as a traditional MVC with Doctrine-like patterns (Zend_Db_Table). Symfony's bundle system and Doctrine integration aligned better with the existing mental model. Laravel's Eloquent ORM would have required a different database mapping philosophy.

API Platform. The application needed REST APIs for its multi-tenant SaaS future. Symfony + API Platform v4 provided this natively. Laravel can do APIs, but API Platform's resource-driven design matched our domain model closely.

No framework. Keeping ZF1 with modern PHP shims was considered but rejected. The framework environment was the problem, not just the PHP version. We would still have no autowiring, no attributes, no modern tooling.

Symfony 8 was chosen because it provided the clearest migration path from ZF1's architecture: bundles mirror modules, Doctrine maps to Zend_Db_Table, and the event dispatcher could bridge the two systems.

The migration in numbers

  • 9 months from first containerized ZF1 setup to bridge removal
  • 488 commits on the migration branch
  • 7 ZF1 modules ported: HR, Payroll, Base, Login, License, System, Default
  • 4 bridge components built and later deleted
  • 12,361 lines of bridge code removed on the final day
  • 1 developer leading the core work with sporadic contributor commits

The decision to migrate was not about rewriting for the sake of freshness. ZF1 was a genuine production risk. The decision to use Symfony and the Strangler Fig pattern was about managing that risk incrementally rather than compounding it with a rewrite.

The next article in this series explains how the Strangler Fig bridge was built: the catch-all route that let ZF1 and Symfony coexist in the same process.


— Delaa