Part 1 of 8 in Strangler Fig migration

Why We Migrated from Zend Framework 1 to Symfony 8

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 in use had gone the same route: no security updates for years, with no path back onto a supported release without touching the rest of the stack
  • 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 hasn't shipped a security fix since 2016. PHP 5.6 hasn't shipped one since it reached end-of-life in December 2018. And the two were locked together: ZF1 leans on dynamic object properties throughout — Zend_Db_Table_Row exposes columns as undeclared properties, Zend_Config does the same for config keys — a pattern PHP itself started deprecating from 8.2 onward. Upgrading PHP without upgrading the framework wasn't an option; upgrading the framework meant leaving ZF1 behind. Staying meant betting nothing new would ever be found in either — a bet with no upside and an open-ended downside.

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 these days 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 right now. 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 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