Dialect inventory
What the codebase assumes, counted: removed functions and constructs with the file and line each appears on, the extension APIs it calls, the deprecated syntax it uses, and the split between code we can change and code we cannot. The same pass runs over the application, the themes and the plugins, because a legacy plugin is a finding and not a footnote.
Removed-function and syntax remediation
Mechanical replacement first — anonymous functions for create_function(), foreach or ArrayIterator for each(), mbstring for the removed mb-regex aliases and the encoding conversions that replaced convert_cyr_string(). Each change is small and each is verified, because a mechanical rewrite applied at scale without verification is how one wrong pattern becomes four hundred wrong lines.
Deprecation handling, before it becomes fatal
Running the current branch at the highest error reporting it will accept, which surfaces on the branch in use what the next branch will enforce. On the estates we have seen, most of it is a handful of patterns repeated across years of templates, and one of them is usually a suppression that has been hiding a real fault.
Extension and encoding surfaces
The dependencies that are not the language but move with it: mbstring's changed function signatures, iconv and mbstring choices for conversions, session and filter constants that were removed, and any bundled extension that later branches unbundled. Each is either provided, replaced, or established as a genuine blocker before the runtime change is scheduled.
Abandoned component strategy
For each third-party component with no current release, a decision with reasons: update if a maintained release runs on the target branch, replace with a maintained equivalent, isolate if it is peripheral, or accept the risk explicitly if the exposure is structural and the business decision is to carry it. We say which applies rather than leaving it to be discovered.
A test harness that did not exist
Smoke coverage over the paths that carry traffic — the routes in the site's own inventory, the forms, the authenticated actions, the imports — so the change can be verified and so the next upgrade is cheaper than this one. It is built for this project rather than installed as a framework: the goal is evidence at every step of the migration, not a test suite as an artifact.
Sequenced remediation, against a stated target branch
The removal list belongs to the target branch, so the work is planned against that target and split into batches that can each be verified independently — divided along the code's own boundaries, a plugin or a theme or a module, rather than by file count. A failing batch then has one owner, one cause and one revert, which is what keeps a migration from turning into an outage.
The change delivered with the runtime move
Remediation and the branch change land as one engagement, in one sequence, with the code changes verified on the current branch before the runtime moves and on the new branch immediately after. Doing them separately means paying for two windows and attributing the result to neither.