How Drupal Rector cuts deprecation work before a Drupal 12 upgrade
Drupal Rector fixes the deprecated API uses that Upgrade Status reports where a fix is available, and everything else still needs a developer. For a team with custom modules and themes that must move to the next major version, the first step is to run Upgrade Status on the current site against the next major version. Upgrade Status finds the deprecated calls, suggests the drupal-rector command line tool where it applies, and sorts each result into Rector fixable or manual review. The Rector fixable group is where the effort reduction comes from, and the manual review group is where the planning time goes. Run the check on the code you own, not on a copy with local edits that nobody can reproduce later.
The timing comes from the Drupal core release schedule. Drupal 11 is the current major version, Drupal 12 is scheduled for the week of December 7, 2026, and Drupal 10 reaches end of life on December 9, 2026. A team on Drupal 10 that waits for Drupal 12 to ship has very little time left on its current version, so the Upgrade Status run belongs in the current planning cycle, not the release week. Confirm the Drupal 11 minor release you will target and the PHP version your hosting provides, because Drupal 12 requires PHP 8.5 or later and that check can block the code work entirely. Ask your team to list which custom modules and themes have the most findings before deciding the sequence.
Rector is one step in the supported path, not a full upgrade. Its changes are automated rewrites of code, so every changed file needs a review in version control, a run of the site's automated tests, and a check of the behavior that the tests do not cover, such as forms, permissions and custom entity handling. Contributed modules must also have a release compatible with the target version, and Upgrade Status reports that separately. Plan for Rector to shorten the mechanical part of the work while the manual review group, the contributed module readiness check and the testing set the actual duration. Ask any partner to show the Upgrade Status report for your code and to separate the Rector fixable items from the manual items in the quote.
What changes for custom code and the platform, and what to do about it
This table compares the platform, code and release changes that affect a Drupal Rector upgrade, with the impact of each and the action your team needs to take. Read it row by row before scoping, because the PHP requirement and contributed module readiness can block work that Rector cannot touch.
| Change | Impact | Action needed |
|---|---|---|
| PHP 8.5 requirement for Drupal 12 | Drupal 12 requires PHP 8.5 or later, while Drupal 11 requires PHP 8.3 or later, so server upgrades gate the code upgrade. | Confirm current PHP version on all environments and plan the server upgrade before running Rector changes. |
| Deprecated API uses that Rector fixes | Upgrade Status reports these uses and suggests drupal-rector, which can apply mechanical changes to custom code. | Run Rector on a branch, review the diff, and commit the changes in reviewable batches. |
| Deprecated API uses for manual review | Results flagged for manual review need developer judgment because the change is not mechanical. | Assign each item to a developer, fix it, and re-run Upgrade Status until the report is clean for custom code. |
| Contributed modules without a compatible release | Custom code cannot pass checks cleanly if a contributed dependency has no release for the target version. | Check each contributed project on Drupal.org, plan replacements or patches, and record the decision. |
| Release cadence on Drupal.org | Bugfix releases come out on the first Wednesday of each month and security releases on the third Wednesday, so test windows recur. | Schedule upgrade work around these dates and keep a routine for applying security releases during the project. |
What to back up and inventory before running Rector
Drupal Rector rewrites code in place, so the work only stays safe if you can restore the site and see exactly what changed. These six items give you a rollback point, a scope, a baseline and a review path before the first automated change is made.
Full database and codebase backup
Take a complete database dump and confirm the codebase, including custom modules, themes, composer.json and composer.lock, is committed to version control before any Rector run. A backup you have not restored is not a rollback plan, so restore it to a scratch environment and confirm the site boots. Keep the dump and the commit hash together so you can return to an exact state.
Inventory of custom code and owners
List every custom module and theme, the path it lives in and the named team or person who approves changes to it. Rector will touch code you did not expect, so owners need to know which files are in scope and who reviews their diffs. Include custom code inside custom directories and any patched contributed projects, since those are easy to miss.
Upgrade Status baseline report
Run Upgrade Status against the current site for the target major version and save the full report as a dated baseline. Upgrade Status finds deprecated API uses but cannot fix them, and it labels each result as Rector fixable or manual review. The saved baseline lets you measure what Rector removed after each run and prove that no new findings were introduced.
Test environment matching production
Build a non-production environment that runs the same PHP version and database engine and version as production. Rector output and test results are only meaningful when the runtime matches, so check the PHP minimum for your target version against your current stack. Confirm your web server, database and PHP settings in the test copy before running any automated change.
Clean working tree and change branch
Start from a clean working tree on a dedicated branch so uncommitted local edits do not mix with Rector changes. Commit each Rector run as its own change set, and review each one as a diff before the next run. Small, separate commits make a regression traceable to one rule set and easy to revert.
Contributed module compatibility list
List every contributed module and theme with its installed version, the release that supports the target major version, and its maintainer status. Rector can fix deprecated calls inside contributed code, but a project without a compatible release blocks the upgrade regardless of what Rector does. Record which modules need replacing, patching or upstream updates so that these decisions are made before the automated changes begin.
How to run Rector fixes and reviews in order
This sequence moves custom Drupal code from deprecated API use to a reviewed, tested state in small batches. Each batch ends with a tagged commit and a restorable backup, so any change can be reversed without losing the work already reviewed.
Confirm PHP and hosting requirements for the target version
Before any code changes, the team confirms that the target Drupal version's PHP and database requirements are met in the staging environment. Drupal 11 requires PHP 8.3 or later, and Drupal 12 requires PHP 8.5 or later, so the platform team should check the runtime version and the database version against the Drupal.org system requirements. The step is done when a staging site on the target PHP version boots the current codebase and passes a basic smoke test. If the requirements are not met, the server upgrade is scheduled first, because Rector results on an unsupported runtime cannot be trusted. Rollback: no code has changed yet, so the only rollback is reverting the staging server runtime to its prior version.
Run Upgrade Status on the current site
The developer lead installs Upgrade Status on a copy of the current site and runs it against the next major version, because the checks must run on the live codebase with the target version in mind. The report lists deprecated API uses and separates findings that Rector can fix from findings that need manual review. The step is done when the report is saved as a baseline with a count of findings per custom module and theme. Rollback: running the report changes nothing, so no rollback is needed beyond discarding the staging copy.
Run drupal-rector on custom code in a branch
A developer creates a feature branch from the main branch and runs drupal-rector only against custom modules and themes, not contributed projects or core. Each change is applied as a diff, and the developer reviews every changed file against the Upgrade Status finding it addresses before accepting it. The step is done when the branch contains only changes that match a finding in the baseline report. Rollback: the branch is discarded or reset to the commit before Rector ran, so the main branch is never touched at this stage.
Commit fixes in reviewable batches
The developer groups the accepted Rector changes into small commits, for example one commit per custom module or per deprecated API, so that a reviewer can read each batch on its own. Each commit message names the Upgrade Status finding it resolves. The step is done when every batch has passed code review and the branch builds on the staging site. After each approved batch, the team creates a tagged commit and takes a database and file backup of the staging environment, so there is a known restore point. Rollback: revert the batch commit or restore the tagged state and its backup.
Handle manual review items
Findings that Rector cannot fix, such as changed method signatures, altered hook behavior or removed APIs without a direct replacement, are assigned to a named developer with a ticket that links to the Upgrade Status line. The developer rewrites the affected code by hand, and a second developer reviews it. The step is done when every manual item is closed or explicitly accepted as a known risk with an owner. Rollback: each manual fix is its own commit, so it can be reverted independently of the automated batches.
Re-run Upgrade Status
After all batches and manual fixes are merged into the branch, the developer runs Upgrade Status again on the same target version and compares the new report against the baseline. The expected result is that every Rector-fixable finding is gone and the remaining findings are only those accepted in the manual review step. The step is done when the comparison is attached to the change record. If new findings appear, they are treated as regressions and fixed before moving on. Rollback: return to the last tagged batch and repeat the affected part of the sequence.
Run automated and functional tests
The QA lead runs the project's automated test suite, PHPUnit and any kernel or functional tests the custom code already has, along with a scripted walkthrough of critical user journeys on the staging site. Failures are triaged to the batch that introduced them. The step is done when the agreed test set passes on the target PHP version and the walkthrough has been signed off by the business owner. Where the custom code has little test coverage, the team should confirm with the client what manual test scope is acceptable before sign-off. Rollback: failing batches are reverted to their tagged commit and re-reviewed before testing resumes.
Define a rollback point after each batch
Every approved batch closes with a named Git tag and a verified backup that can be restored to a clean environment. The operations lead confirms that the backup restores the database and the file system together, and that the tag builds from a clean checkout. The step is done when the rollback procedure has been rehearsed on a non-production copy and the tag, backup location and restore steps are recorded in the change log. Rollback: the restore procedure itself is the rollback, and it should be tested before any production change is scheduled.
Which findings Rector fixes and which need a developer
Upgrade Status categorizes some findings as Rector fixable and suggests the drupal-rector command line tool for them. Rector rewrites deprecated API calls in custom code into their documented replacements, which removes repetitive edits across many files. Rector is a mechanical tool, though, so the output still needs review before it is merged. The reviewer's job is to confirm that the rewritten code does what the original did, not only that it now uses a current function name.
- Renamed or moved calls: Check that the new call receives the same arguments in the same order and returns the same type the surrounding code expects.
- Changed defaults: Look for rewrites that add or drop optional parameters, since a changed default can alter query results or cache behavior.
- Dependency injection changes: Confirm that services Rector injects or replaces are available where the class is instantiated, including in tests.
- Formatting noise: Separate whitespace and reordering changes from functional changes so the diff stays reviewable.
- Tests: Run the custom module test suite and a functional smoke test on the changed paths, because Rector cannot judge whether a behavior change is intended.
Before approving a Rector commit, confirm that each changed file is covered by a test or a manual check on the affected page or workflow.
Some findings have no automatic replacement, or the correct replacement depends on what the code is meant to do. Upgrade Status lists these for manual review. They usually sit in custom logic that calls changed APIs, in hooks or plugins whose signatures changed, and in Twig templates and theme code that rely on markup or variables that were removed or renamed. A developer who knows the business rule has to decide the fix.
- Deprecated calls without a replacement: Identify the intended outcome first, then rebuild the feature on the current API instead of forcing a mechanical swap.
- Logic that depends on changed APIs: Review access checks, entity queries and form handling, where a small change can widen or narrow what users see.
- Custom templates: Check Twig templates against the current theme and markup, including variables passed from preprocess functions.
- Theme code: Test layouts, components and JavaScript behavior in the browsers and devices your users rely on.
- Unclear ownership: Record decisions in the ticket so the next upgrade does not repeat the same analysis.
Before closing a manual finding, confirm that a developer who did not write the original code has reviewed the change against the requirement behind it.
Findings in contributed modules are different because the team does not own that code. Rector can only change files in the custom code you control. For a contributed module, the question is whether a release compatible with the target Drupal version exists, whether the maintainer has planned one, or whether a different module or a custom replacement is needed. Upgrade Status reports these findings, but resolving them is a dependency decision, not a code fix.
- Compatible release available: Plan the update and test it against your custom code, since an updated module can change hooks or configuration that your code relies on.
- No compatible release yet: Check the project's issue queue and decide whether to wait, sponsor the work, or move to an alternative.
- Replacement required: Estimate the rebuild of the affected features and the data migration of their configuration.
- Patches: Track any local patch against the upstream issue, so it is removed or reapplied correctly during updates.
- Security coverage: Confirm that any module you keep, patch or replace still receives security updates from its maintainers.
Before approving the upgrade, confirm in writing with the project maintainers or your vendor which release supports the target version, and record the decision for each module.
What makes Rector-assisted upgrades take longer
This table lists the drivers that change how long a Rector-assisted upgrade takes and what each one asks you to check before committing to a plan. Use it to size the work on your own codebase, since none of these drivers has a fixed duration. The main takeaway is that the share of findings needing manual review and the state of contributed modules usually matter more than the raw count of deprecations.
| Driver | Effect on effort | What to check |
|---|---|---|
| Volume and spread of custom code | More modules and themes multiply the findings to review and the test runs needed after each change. | Count custom modules, themes and custom entity types, and how many depend on each other. |
| Share of findings in manual review | Rector fixes mechanical changes, while manual findings need a developer to read the code and decide the change. | Compare the Rector fixable and manual review counts Upgrade Status reports for each project. |
| Contributed modules without a compatible release | Custom code that depends on a module with no release for the target version may need replacing or waiting. | Check each contributed project's release list for the target major version before fixing code. |
| Undocumented custom APIs and missing tests | Without documentation or tests, developers cannot confirm that a Rector change preserved behavior, so review slows down. | Identify custom services, hooks and plugins with no tests, and whether any owner can explain their intent. |
| Number of Drupal versions being crossed | Each major version adds a separate set of deprecations, and a site on an older version needs intermediate steps first. | Confirm the current version and the path to the target, since Upgrade Status checks run against the next major version. |
| PHP and database upgrades needed first | Server changes gate the code work, so a PHP or database gap can block Rector runs and testing. | Compare the current PHP version and database with the target version's requirements on Drupal.org before starting. |
| Team review capacity | Rector output is a set of proposed diffs, and the review queue grows faster than the fixes if few people can approve changes. | Confirm who reviews generated changes, how many reviewers are available, and how changes are batched and merged. |
How to test Rector changes and roll them back safely
Verify every Rector change set before it reaches a shared environment, and treat the Rector diff as the primary review artifact. Run the custom module and theme test suites first, wherever they exist, because a passing PHPUnit or Kernel test run is the cheapest signal that a rewritten call still behaves the same. Where a custom module has no tests, say so in the change record, since that gap determines how much manual checking the batch needs. Then read the diff line by line for behavior changes that tests rarely catch, such as altered default arguments, changed return types, or a rewritten hook that now runs in a different order. Confirm with your own developers which fixes are mechanical and which were flagged for manual review, and keep the manual items in a separate commit.
Functional checks should follow the business paths the batch could touch: content types and their field widgets, forms and their validation, user roles and permission-gated pages, and the integrations that exchange data with the site. Add visual checks on the custom themes, comparing key templates and components before and after the change, because Rector rewrites PHP and does not judge rendered output. Use a staging copy built from a recent sanitized database so these checks run against realistic configuration. Record which checks passed, who ran them, and the commit they covered.
Plan rollback before the first change set is applied. Tag the commit at the end of each batch so a bad change can be reverted cleanly, and keep the contributed module set pinned in composer.lock so a rollback restores the same dependency versions. If a batch touched data or configuration, restore the pre-change database backup rather than attempting to reverse configuration by hand, and confirm that the backup restores into a test environment before you rely on it. Create a rollback point after each batch, not only at the start of the project, so that a fault found three batches later can be isolated to one set of changes. Confirm your backup retention and restore timing with your own operations team, since those figures depend on your infrastructure.
How much effort does Rector-assisted upgrade work take, and what does it need from our team?
What drives the cost and effort of Rector-assisted upgrade work?
The main drivers are the volume of custom code, the share of Upgrade Status findings that need manual review, the readiness of each contributed module, and how much automated test coverage exists to confirm behavior after changes. Rector handles the mechanical part for findings it can fix, so a codebase with many fixable findings and little manual work costs less to clear than one dominated by hand-rewritten logic. Ask any provider to split the quote into the audit, the Rector pass, manual remediation, contributed module decisions and testing, since those are the parts that scale differently. Costs for the upgrade work are one-time, while the code you leave behind is a recurring maintenance cost if it stays on deprecated APIs.
Can our own developers run drupal-rector without outside help?
Yes, for the findings Upgrade Status marks as Rector fixable, your developers can run the drupal-rector tool themselves, provided someone reviews each change before it merges. The tool rewrites deprecated calls mechanically, so the review should confirm that the new code keeps the original behavior and passes the existing tests. Findings categorized for manual review are different: they need developers who know the custom code well enough to decide how the logic should change, and they often involve design choices rather than a find-and-replace. If your team lacks that knowledge for a specific module, that module is where outside help adds the most value.
What does the upgrade work need from our team?
The work needs access to the code repository and a current copy of the site configuration, a named owner for each custom module and theme who can answer questions about intended behavior, and a test environment that matches production closely enough to catch regressions. It also needs decision makers who can approve replacing or dropping contributed modules that lack a release for the next major version, because those choices affect features and budget. Plan for someone to review and approve each Rector change in version control. Confirm these inputs in writing before work starts, because missing owners or test environments are a common reason fixable findings take longer than expected.
How long does a Rector-assisted upgrade take?
Duration depends on the same drivers as cost: the amount of custom code, the number of manual findings, how many contributed modules need replacement, and how fast your team can review and approve changes. Timing is also set by the Drupal release calendar. Bugfix releases come out on the first Wednesday of each month and security releases on the third Wednesday, and Drupal 12 is scheduled for the week of December 7, 2026, while Drupal 10 reaches end of life on December 9, 2026. Ask for a timeline built from your own audit results rather than a generic estimate, and check it against those dates in the Drupal core release schedule.
Does Rector replace the whole Drupal upgrade?
No. Rector covers deprecated API use in custom code, and only the subset of that work with a known automated transformation. It does not upgrade contributed modules, change the PHP version, update the database, rebuild themes that depend on removed components, or validate business behavior. Those tasks remain part of the upgrade plan, and some of them, such as checking that a contributed module has a compatible release, can block the whole project regardless of how many findings Rector clears. Treat Rector as one reduction in the custom-code workload inside a larger upgrade plan.
What should we ask for in a written scope for this work?
A written scope should list the Drupal versions in and out of scope, the modules and themes covered, and the audit results used as the baseline. It should state how many findings are expected to be Rector fixable versus manual review, based on the audit, and who reviews and approves each automated change. Ask the provider to define the testing approach, the acceptance criteria, the handling of contributed modules without a compatible release, and what happens when new findings appear during the work. Also confirm deliverables such as a change log, a remaining-findings report and handover documentation, and whether ongoing support is a separate retainer.
How DeployOpen scopes, runs and supports a Rector-assisted upgrade
DeployOpen plans Drupal Rector work as part of a written, fixed-scope engagement inside your own infrastructure. We use Upgrade Status findings to decide which deprecated API uses Rector can fix, then review every automated change and document the manual work that remains.
How we scope
- The written scope lists which custom modules and themes Rector will process, and which Upgrade Status categories are in or out.
- We record the Upgrade Status report against the current site and the target major version before any code changes start.
- Manual review items, contributed modules without a compatible release and core removals are listed as separate scope lines with owners.
How we build
- We run drupal-rector on a branch of the custom code, so each automated change is reviewed as a normal pull request.
- Rector output is checked against the Upgrade Status report again, and any findings it does not clear are handled by hand.
- Automated and manual changes are tested in a staging environment that mirrors your production hosting and configuration.
How we hand over
- You receive the Rector configuration, the commit history of automated changes and a list of manual fixes with their reasons.
- The handover document records the remaining deprecations, the test results and the contributed modules still awaiting an update.
- Your team walks through the review notes so future Rector runs can be repeated by your own developers.
How we support
- Ongoing support, upgrades and continuous testing are available as a retainer, which can cover later Drupal minor and major releases.
- Retainer scope, response expectations and cadence are agreed in writing, so you can confirm them against your own change control.
- Questions about the Drupal 12 timeline should be checked against the Drupal core release schedule before the work is planned.

