Start with a behavioral inventory
Treat the running application as evidence. List entry points, scheduled jobs, integrations, writable directories, authentication paths, and the business actions users depend on. A dependency list from the package manager is useful, but it misses copied libraries, server modules, database routines, and undocumented callbacks.
Capture a small set of representative requests and their expected outputs. The goal is not exhaustive automated testing on day one; it is a reproducible boundary around the behavior you cannot afford to lose.
- Record the production PHP version and extensions.
- Export routes, cron jobs, queues, and external callbacks.
- Identify files and tables written at runtime.
- Save sanitized examples of critical requests and responses.
Separate environment work from application work
Upgrade the delivery environment independently where possible. Reproduce the current stack in a disposable environment, confirm the baseline, then introduce one version change at a time. When PHP, the web server, database, and application all change together, a failure produces too many suspects.
Use logs that include request identifiers and error context without exposing secrets. Deprecation notices belong in the upgrade environment before they become production errors.
Move through compatibility boundaries
Inventory dynamic properties, removed functions, implicit type conversions, legacy database APIs, and error-suppression patterns. Replace them in small, reviewable sets. Add tests around the boundary being changed, then run the representative request set.
A compatibility layer can be acceptable if it is named, documented, and scheduled for removal. Hidden compatibility behavior becomes the next generation of legacy debt.
- Commit one compatibility family at a time.
- Run static analysis at a level the codebase can currently sustain.
- Increase strictness after the noisy baseline is understood.
- Keep a rollback artifact for each deployment.
Deploy the upgraded application to a production-like environment, replay the representative requests, run scheduled work once, and compare logs and database side effects before promoting it.