How would you plan PHP version upgrades across a fleet of dozens of PHP applications that currently run different versions?
answer
- inventory the version of every app
- branch support dates set the deadline
- one minor per year, continuously
- CI matrix with the next version
- shared libraries move first
basics
~20 sInventory every app's PHP version and dependencies, rank by support dates and risk, and make upgrading a yearly routine: next version in CI, deprecations fixed continuously, shared libraries first, and a standard base image per version.
solid answer
~50 sStart with an **inventory**: each app's running version (`PHP_VERSION`), its declared requirement in `composer.json`, extensions and blocking dependencies. Rank apps by the **support phase** of their branch, since apps on an end-of-life branch get no security fixes, and by business risk. Then change the model from projects to **routine**: every app's CI runs its current version plus the next one, deprecations are kept near zero, and each November's release starts a bounded upgrade window. Upgrade **shared libraries and base images first**, so they support both old and new versions and every app can move independently. Pick a fleet policy, such as "no app more than one branch behind current and none on a security-only branch without a date", and report against it. Some apps are better frozen and isolated, or retired, than upgraded; make that an explicit decision.
go deeper
Recall that PHP branches stop receiving security fixes after a published date, so every application eventually has to move.
Explain how a CI matrix with the next PHP version and a deprecation sweep make one application's upgrade routine.
Describe how you would sequence shared libraries, base images and applications so teams can upgrade independently, with rollback.
Set a fleet policy, such as no app on an end-of-life branch and none more than one behind, fund it yearly, and own the explicit decision to retire or isolate apps that will not move.
## Why this is a judgment question There is no single correct plan. The trade-offs are engineering time against security exposure, one big coordinated move against many small ones, and uniformity against letting each team choose. What an interviewer looks for is a plan that turns a recurring crisis into a routine, grounded in how PHP releases actually work. ## The facts the plan rests on - PHP ships a new minor **every year** in November; each branch gets active support, then security-only fixes, then reaches **end of life**. The dates are published per branch on php.net. - Each minor has a **migration guide** listing new features, incompatibilities and deprecations relative to the previous minor. Crossing several branches means reading several guides. - Deprecations are raised as **`E_DEPRECATED`** notices, and the shipped production ini hides them, so they must be surfaced deliberately. - A running application can report its version through `PHP_VERSION` or `PHP_VERSION_ID`; its intended version is declared in `composer.json`. ## Step 1: inventory and triage Build one table for the fleet: | Column | Why it matters | |---|---| | running PHP version | where each app actually is, not where the docs say | | declared `php` requirement | what the app claims to support | | branch support phase | active, security-only or end of life | | blocking dependencies | frameworks and libraries that pin an old PHP | | test coverage and owner | how safe an upgrade is and who does it | | business criticality | how much risk a failed switch carries | Apps on an **end-of-life** branch, exposed to the internet, go first. Apps with no owner and no tests are the hardest and need a separate decision. ## Step 2: make upgrading continuous The expensive pattern is a big-bang upgrade every few years that crosses four minors and a major at once. The cheaper pattern treats the yearly release as a scheduled event: 1. **CI matrix**: every app runs its test suite on its current version and on the next one, including release candidates in the autumn. 2. **Deprecation budget**: deprecations fail or are reported in CI, and the count per app is tracked like any other debt. 3. **Upgrade window**: after each November release and its first patch releases, teams move one minor within an agreed period. 4. **Standard runtime images**: one maintained base image per supported PHP version, with the same extensions and ini settings, so an upgrade is a one-line change plus a test run. ## Step 3: move shared code first Internal libraries, SDKs and templates used by many apps are the critical path: - Make them support **both** the old and the new version, using feature detection or `PHP_VERSION_ID` checks where behaviour must differ. - Widen their `php` constraint to cover both versions before any app moves. - Only drop the old version once no consumer needs it. This decouples apps from each other: no team has to wait for another. ## Step 4: decide what not to upgrade Some applications are not worth the upgrade cost: a legacy tool with a handful of users, or a system scheduled for replacement. Options: - **Retire** it, or merge it into a maintained app. - **Isolate** it: network restrictions, no public exposure, a fixed replacement date. This is an accepted risk, not a solution. - **Rewrite the critical part** and leave the rest. The key is that this is an explicit, dated decision owned by someone, not an app that silently stays on an end-of-life branch. ## Step 5: policy and reporting A simple fleet policy gives teams a target and leadership a metric, for example: - no application on an end-of-life branch; - no application more than one branch behind the current stable release (8.5 today); - the next branch green in CI before its GA date. Report the inventory against it every quarter. The number that matters is how many apps would need more than one minor step to reach current, because that is where upgrades turn into projects. ## Common failure modes - Upgrading the runtime image before the apps are ready, so everything breaks at once. - Leaving deprecations until upgrade day because production logs looked clean. - Letting each team pick its own base image, so ini differences cause surprises in the switch.
- How do you handle an internal library used by apps on both PHP 7.4 and PHP 8.5?Keep one codebase that runs on both for a transition period: avoid syntax newer than the oldest supported version, gate behaviour differences with `PHP_VERSION_ID` or `function_exists()`, and test it in CI on both versions. Declare a `php` constraint that covers both. Once no consumer runs 7.4, raise the constraint and remove the old branches.
- Would you jump straight to the newest release or stay one version behind?Both are defensible. Adopting the newest release a few patch releases after GA maximises the support window and keeps upgrades small. Staying one behind lets libraries, extensions and tooling catch up. The deciding factors are how quickly your key dependencies declare support and how much CI coverage you have to catch regressions.
saying these in an interview costs you the question
- Upgrade all applications at once in one coordinated big-bang release.
- Wait until a branch reaches end of life before planning its upgrade.
- Clean production logs prove an app is ready for the next PHP version.
- Shared libraries should drop the old PHP version before the apps migrate.
- An app on an end-of-life branch is fine as long as it still runs.