Why Drupal code review and coding standards decide upgrade cost
A code review against Drupal coding standards and deprecation checks finds the custom code that will break, or cost more to move, when core and contributed projects change. It shows you the upgrade bill before you pay it. Custom modules and themes written by an agency or an internal team often work at launch and still hide deprecated API calls, core template overrides, hard-coded paths and services that depend on internal classes. Each of these turns a routine minor release into a project. The review inspects the codebase, ranks findings by upgrade risk and produces a remediation list that your team or a partner can act on.
Timing matters because the version window is closing. 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, according to the Drupal core release schedule. Teams still on Drupal 10 should treat the review as input to their next upgrade plan, not as a launch task. Confirm the current dates on that page before you set a schedule, because a plan built on an outdated date will fail. Three moments justify the review: before go-live, at agency handover, and before a major upgrade. Handover is the most valuable, because the receiving team inherits every shortcut without having written any of it.
The review is inspection and remediation, not a certification. It does not produce an audit report, and no independent body certifies the code as compliant. Ask the reviewer to state which standards and tools were applied, which findings were fixed in the review and which were handed back to you, and what was not covered, such as performance or security logic. Since Drupal and most contributed projects are licensed under GPL-2.0-or-later, your organization can usually modify and redistribute the custom code it commissions, so confirm code ownership and repository access in the written scope before work starts.
Review approaches and when each fits
This table compares manual peer review, automated static checks and a full handover audit on what each catches, what it misses, who runs it and when it fits. Read it as a layered set: most Drupal projects need the automated checks on every change, peer review on every merge, and a handover audit only at the points where ownership moves.
| Approach | What it catches | What it misses | Who runs it and when it fits |
|---|---|---|---|
| Manual peer review | Design problems, misuse of Drupal services and hooks, unclear naming, missing access checks and logic that does not match the requirement | Style drift that nobody reads for, deprecated API calls in code paths the reviewer does not open, and issues across the whole codebase | Developers on the team, on every merge request. Fits ongoing feature work where reviewers know the business rules |
| Automated coding standards checks | Violations of the Drupal coding standards such as formatting, docblocks and naming, enforced the same way on every change | Whether the code is correct, whether a feature meets its requirement, and most architectural decisions | Run by CI on every commit. Fits any project, and it is the cheapest way to keep review focus on logic |
| Deprecation and upgrade readiness scans | Calls to APIs deprecated in the Drupal version you are moving to, such as those reported by Upgrade Status style tooling | Behavior changes that are not flagged as deprecations, and custom code that is never executed by the scanned paths | Run by the team before each core or major upgrade and on a schedule. Fits projects that expect to move versions on the stated release cycle |
| Full handover audit | Coding standards and deprecations together, plus security issues, configuration held outside version control, missing tests, undocumented custom modules and themes | Unknown runtime behavior, business rules nobody wrote down, and problems that appear only under production load or real data | Run by an independent reviewer when an agency or team hands over a site. Fits transfers of ownership, and the scope should be agreed in writing before the audit starts |
| Typical cost driver | Reviewer time per change and the number of people qualified to review custom code | Setup of CI rules and the size of the custom codebase | Scope of the written audit and the number of custom modules and themes. Ask for the scope and the evidence format before agreeing a quote |
How to set up a review and CI enforcement
This sequence turns a one-off code review into standards that run on every change. It starts from an inventory of custom code and target versions, and ends with blocking gates and a handover checklist that the next team can run without help.
Inventory custom code and target versions
Someone on the team lists every custom module, theme and custom entity in the codebase, and records which Drupal core version and PHP version each must run on. Drupal 11 requires PHP 8.3 or later and Drupal 12 requires PHP 8.5 or later, so the target versions decide which deprecations and language features count as problems. Confirm the current core and PHP targets with the Drupal core release schedule and PHP requirements pages before fixing the list. The step is done when every custom directory has an owner and a target version written next to it.
Agree the coding standard and exclusions
The engineering lead and the client's technical owner agree which rule sets apply, such as Drupal coding standards for PHP, Twig and JavaScript, and which paths are excluded. Exclusions should be narrow and written down, for example generated code, vendored libraries or third-party files that the team does not own. Each exclusion needs a named reason and an owner who can remove it later. The step is done when the standard, its version and the exclusion list are committed to the repository as configuration, not held in someone's memory.
Run a baseline scan
Run the agreed linters, static analysis and Drupal deprecation checks against the current codebase in a clean environment that matches the target versions. Keep the output as a report saved with the repository, so the team can see the starting state. Expect the first run to be noisy on inherited code, and do not fix anything yet. The step is done when the scan completes on every custom path and the baseline report is stored and reproducible from a single command.
Triage findings by risk and effort
The team sorts findings into groups: deprecated APIs that break on the target core version, security-sensitive patterns such as unescaped output or unchecked access, style issues, and false positives. Security and upgrade blockers go first, and style issues can be batched or deferred. Each accepted false positive gets a documented suppression with a reason. The step is done when every finding has a decision (fix now, fix later with a ticket, or suppress with a reason), and the counts by group are recorded.
Fix the blocking findings
Developers resolve the findings marked as blocking, starting with deprecations for the target Drupal version and any security-sensitive patterns. Changes go through normal code review, with the reviewer checking the fix rather than only the new code. The team reruns the scan after each batch to confirm the count is dropping for the right reasons. The step is done when the blocking group is empty on the default branch and the scan report shows no unexplained failures.
Add checks to the pipeline as gates
Engineers add the scan to the CI pipeline on every merge request, marking the blocking rules as required checks and the style rules as warnings that report but do not stop a merge. The pipeline should run in the same container image as the baseline so results match local runs. The step is done when a deliberately introduced deprecated call fails the pipeline, and a deliberately introduced style issue appears as a warning only. Keep that test branch as evidence that the gate works.
Write the handover checklist
Before an agency or internal team hands over the codebase, the responsible lead writes a checklist covering: the current baseline report, the list of suppressions with reasons, the target core and PHP versions, the CI configuration location, how to run the scan locally, and the owner of each exclusion. The incoming team should confirm each item by running it themselves. The step is done when the receiving team reproduces the scan result on their own machine and signs off the checklist in writing.
What a handover checklist must verify
Coding standards items confirm that custom code follows the Drupal coding standards for PHP, JavaScript, CSS, Twig and YAML, and that the same tools run in both the agency pipeline and your own pipeline. The reviewer should be able to rerun the checks without the agency's laptop. Evidence is a clean run log from a named tool version against the exact commit being handed over, plus the configuration file that defines the rules. The agency's technical lead signs that the run passed. Your lead engineer signs that the run is reproducible in your environment.
- Linter output: A log from PHP_CodeSniffer with the Drupal standard, run against all custom modules and themes, showing zero errors.
- JavaScript and CSS checks: Lint results for custom scripts and stylesheets, with the ruleset file committed to the repository.
- Twig and YAML validation: Confirmation that templates and configuration YAML parse and follow the agreed conventions.
- Naming and placement: Custom code sits in agreed directories, uses the module machine name prefix everywhere, and contains no files copied from contributed projects.
- Exceptions register: Every suppressed rule has a written reason and an owner.
Confirm the exact standard versions and tool versions with the agency before signing, because a rule set that differs from the one your team runs will produce different results.
Deprecated API checks confirm that custom code does not call functions, classes or hooks that Drupal core or contributed projects have marked as deprecated. This matters most on upgrade paths, because deprecated calls are the ones that break when a later major version removes them. The Upgrade Status tooling and the deprecation output from PHPStan or Rector are the usual sources of evidence. Drupal's core release schedule shows Drupal 11 as the current major version and Drupal 12 as scheduled, so the target version should be stated in the handover document.
- Target version stated: The handover names the Drupal version the code was checked against and the PHP version, which the PHP requirements page sets at 8.3 or later for Drupal 11.
- Scan report: An Upgrade Status or equivalent scan of custom code, attached as a file, with every finding either fixed or listed.
- Deprecation log: A clean run with deprecation output enabled during the test suite, with no notices from custom modules.
- Plugin and hook signatures: Event subscribers, plugins and hook implementations match the current signatures.
- Open findings: Any remaining item has a ticket, a planned fix version and the name of the person accepting the risk.
Ask the agency to show the scan command and output, not only a summary sentence.
Configuration items confirm that the site's behavior is captured in exported configuration that is tracked in version control, so the receiving team can rebuild the site from the repository rather than from a database copy. The checklist should verify that exports are current, that environment-specific values are kept out of the repository, and that the config import works on a clean database. Evidence is a fresh export with an empty diff, a documented import procedure, and a list of settings deliberately excluded. The agency's deployment lead and your release manager both sign.
- Clean export: Running the configuration export on the handover branch produces no changes, which shows the repository matches the running site.
- Clean import: A rebuild from the repository on an empty database completes without errors and reproduces the configuration.
- Secrets excluded: Database credentials, API keys and SMTP passwords come from environment variables or a secrets store, not from exported YAML.
- Environment settings: Development-only modules and settings are disabled or excluded in the production configuration.
- Ignore list: The configuration ignore settings are documented with a reason for each entry.
Ask for the import procedure in writing and run it yourself on a staging copy before accepting the handover.
Test and security items confirm that the custom code is covered by automated tests and that access checks and input handling follow Drupal's security practices. Automated tests give the receiving team a regression baseline for later upgrades. Security review covers routes that expose data, forms that accept input, and custom queries. The Drupal Security Team's advisory process is a useful reference for what a reviewer should look for in custom code, because most reported issues in contributed projects come from missing access checks or unsafe output. Evidence is a test run log, a list of routes with their access requirements, and a security review sign-off. The agency's security reviewer and your security team both sign.
- Test run: Unit, kernel or functional test results for custom modules, with the command used and the pass count.
- Access checks: Every custom route and controller has an access requirement or permission, and the list is attached.
- Input handling: Form values are validated, and database queries use placeholders or the entity API instead of concatenated strings.
- Output escaping: Twig templates render user data through the default escaping, with raw output justified in writing.
- Dependency review: Bundled libraries are listed with versions and their licenses.
If the agency has no automated tests, treat that as an open finding and agree a plan before handover.
Documentation and licensing items confirm that the receiving team can operate and change the code, and that the licenses of everything bundled are known. Drupal and contributed projects hosted on Drupal.org that are derivative works are licensed under GPL-2.0-or-later, according to the Drupal.org licensing page, so custom code that combines with them has to be reviewed against that license. Bundled third-party libraries may use other licenses. Evidence is a README for each custom module and theme, an architecture note, and a software bill of materials or license inventory. The agency's project lead signs the documents, and your legal or compliance contact signs the license inventory.
- Module and theme READMEs: Each custom component states its purpose, dependencies, configuration and how to run its tests.
- Architecture note: A short document describing services, custom entities and integration points, with diagrams where useful.
- License inventory: A list of every bundled library with its version and license, generated by a tool where possible.
- Ownership of code: The contract states who owns custom code and the license under which it is delivered.
- Runbook: Steps for deploying, clearing caches, running updates and rolling back.
Confirm the license terms of custom code in the signed contract, since the Drupal license does not answer that question for your organization.
Security risks found during code review
Code review on a Drupal project is one of the cheapest places to catch exposure before it reaches production. The cards below cover the findings that most often create security risk in custom code, and they end with how review results should feed the patch routine that keeps the site current.
Unsafe output rendering
Reviewers should flag any render array, Twig variable or string concatenation that outputs user-supplied or stored data without escaping. Drupal's Twig autoescaping covers most templates, but |raw filters, Markup::create() calls and hand-built HTML in controllers bypass it. Check that user input passes through the Form API, the Render API or sanitized Twig output, and that any intentional raw output is limited to content that has already been sanitized at save time.
Missing access checks
Routes, controllers and entity queries are the most common place where custom code exposes data. Check that every route in *.routing.yml declares a permission or access callback, that controllers verify entity access with $entity->access('view') or equivalent, and that Entity Query and database queries respect node and entity access rather than bypassing it with raw SQL. A route without an access requirement is a finding even when the page looks harmless.
Hard-coded secrets
API keys, client secrets, database credentials and webhook tokens committed to custom modules or settings files can leak through repository access and backups. Reviewers should search diffs for literal credentials and confirm that values are read from environment variables, secret stores or the project's secrets handling, with settings.php or services.yml referencing them rather than containing them. Any secret that has already been committed should be treated as exposed and rotated.
Outdated contributed dependencies
Contributed modules and third-party libraries pulled in through Composer often carry the most open advisories on a Drupal site. Review the composer.lock file for projects with known security releases, abandoned modules, or libraries pinned well behind their current release. Ask the team which contributed projects are actively maintained, and record the version and maintainer status of each one in the handover documentation.
Unsupported Drupal versions
Custom code written against a Drupal major version that has left security support is exposed to every core advisory published after that date, and the code itself may depend on APIs that will not survive an upgrade. Drupal 7 security support ended on January 5, 2025, and Drupal 10 reaches end of life on December 9, 2026. Reviewers should record the target version, confirm the PHP runtime meets its minimum, and flag any custom code that depends on deprecated APIs.
Feeding review into patch routines
The Drupal Security Team publishes advisories with CVE identifiers and risk scores, and it releases security fixes in Wednesday windows, with security releases on the third Wednesday of each month. Map each advisory against the modules your review has already inventoried, then schedule patches for that window. Review findings should also update the patch routine: a finding that repeats across modules is a signal to add a check to CI rather than rely on a later audit.
Limits to confirm before you commit to a review scope
A code review is only as useful as the targets it is measured against. Confirm the Drupal and PHP versions, the tooling versions and the age of the inherited code before you agree a fixed scope, because each one changes how much work the review contains. The cards below cover the checks that most often shift scope after kickoff.
Exact Drupal and PHP targets
Confirm the Drupal core version and the PHP version your production hosts will run, not the versions a developer used locally. Drupal 10 requires PHP 8.1 or later, Drupal 11 requires PHP 8.3 or later and Drupal 12 requires PHP 8.5 or later, so the same custom code can pass on one target and fail on another. Write both targets into the review scope before any findings are triaged.
Tool support for the chosen Drupal version
Check that each static analysis and coding standards tool you plan to enforce supports the Drupal core version and PHP version in scope. A tool release that predates a new Drupal major can report false positives or miss deprecations, and a pinned tool that is too old can block a CI upgrade. Ask the reader's team to record the exact tool versions in the scope document.
Drupal 6 or 7 patterns that need migration
Custom code written for Drupal 6 or 7 cannot be reviewed into a Drupal 10 or 11 codebase in the usual way. Drupal.org documents that those sites are migrated into a new site and that themes must be rebuilt, so hook-based modules and procedural theme code need a migration decision, not a line-by-line review. Confirm which custom code is legacy before estimating review effort.
Inherited modules and contributed dependencies
Confirm which contributed modules the custom code depends on and whether each one has a release for the target Drupal version. A review of your own code can pass while a dependency blocks the upgrade, so the scope should state whether contributed modules are in or out. Record the version of each one as part of the baseline.
Review is not an audit or certification
A code review produces findings, a remediation list and evidence of what was checked. It does not produce an audit report, an attestation or a certification, and it does not replace an assessment by an independent auditor or certification body where one is required. Confirm with your compliance lead which evidence an assessor will expect before you agree what the review delivers.
What do teams ask before hiring a review partner?
What drives the cost of a Drupal code review?
The main drivers are the size of the custom codebase, the number of custom modules and themes in scope, the gap between the current Drupal version and the target version, and the volume of findings that need remediation. A review of a small set of custom modules on a current version costs far less effort than a review of a large estate with many custom themes that must move from Drupal 10 to Drupal 11. Ask for a quote that separates the review itself from any fixes, because the fixes are often the larger share of the work. Review cost is typically one-time for a project, while re-checks after each release are recurring and smaller.
How long does a Drupal code review take?
Duration depends on how much custom code exists, how many modules and themes must be read, how well the code is documented, and how quickly the client answers questions. Repositories with clear structure and passing automated checks move faster because reviewers spend less time on basic issues. Remediation time is separate and depends on the number of deprecated API calls and standards violations found. Ask the partner to state the review phases, the milestones and the decisions that will hold the schedule before you sign a fixed-scope agreement.
What does the client need to provide for a review?
Teams should prepare read access to the custom code repositories, the Composer manifest and lock file, and the configuration export. Reviewers also need a non-production environment with a recent database copy or representative content, and details of the PHP version, web server and database in use. Name an owner for each decision, such as who approves coding standard exceptions, who accepts risk on deferred findings and who signs off the handover. Without these owners, findings tend to stall in the backlog.
Can an internal team run Drupal coding standards checks on its own?
Yes, a capable internal team can run the standard static checks and read the deprecation reports itself. The tools are open source, and Drupal core and contributed projects document how to run them. What internal teams often lack is time to triage results and experience judging whether a deprecated pattern will break at the next major version. A partner adds value in the judgment layer, the handover checklist and the CI configuration that keeps standards enforced after the review ends. Many teams split the work, running checks in-house and bringing in an outside reviewer for a final pass.
Which Drupal versions and PHP versions should the review target?
The target version should follow the Drupal core release schedule, which lists Drupal 11 as the current major version, Drupal 12 as scheduled for the week of December 7, 2026, and Drupal 10 reaching end of life on December 9, 2026. Per the Drupal.org PHP requirements page, Drupal 10 requires PHP 8.1 or later, Drupal 11 requires PHP 8.3 or later and Drupal 12 requires PHP 8.5 or later. Confirm the PHP version in your hosting environment before the review, because a code change that passes on one PHP version may still fail on the version you run. Ask the partner which target versions the review will cover.
Does a code review also cover security risks in custom code?
A code review checks custom code against coding standards and deprecation rules, and it can flag patterns that are commonly risky, such as unsafe input handling or direct database queries without placeholders. It does not replace security testing of the running application or its contributed dependencies. Security advisories for Drupal core and contributed projects are handled separately by the Drupal Security Team, so keep those updates on your normal release routine. Ask the partner whether security findings from the review are reported separately from style issues.
What does a review partner deliver at handover?
A typical handover includes a written findings report grouped by severity and by module, a list of deprecated API uses with suggested replacements, and a checklist that the internal team can repeat. The partner should also deliver the CI configuration that runs the coding standards and deprecation checks on each merge request. Ask for the exact list of deliverables in the written scope, along with who keeps the CI configuration updated after handover. Confirm the format of the report and whether it will be usable by your own developers without the reviewer present.
What recurring costs follow a code review?
Once the first review is complete, recurring costs come from keeping checks current as Drupal core and contributed modules release updates. Bugfix releases arrive on the first Wednesday of each month, and new major versions typically arrive about every three years according to the core release schedule. Each new major version may introduce new deprecations that need a fresh review pass. Budget for ongoing developer time to fix findings, and ask whether a partner offers a retainer for periodic re-checks.
How DeployOpen scopes a code review engagement
A code review engagement starts with a written scope that names every repository, custom module, custom theme and target Drupal version in the review. Custom code is usually spread across a Git repository for the site, a set of custom modules under the modules/custom directory, a custom theme and sometimes a shared component library. Listing each of these, along with the Drupal core version the code must run on today and the version it must be ready for next, turns a vague request to "look at the code" into work that can be priced and checked. Because Drupal 10 reaches end of life on December 9, 2026, many teams will need the target version fixed early, and the scope should say which one applies.
Deliverables are agreed in the same document before work begins. Typical items are a findings report that ranks issues by severity and separates coding standards problems from deprecated API usage, a proposed CI configuration that runs the agreed checks on every merge request, and a handover checklist that the internal or agency team can sign off. The timeline is fixed in writing, and the scope should state what happens if the codebase is larger than the sample agreed at the start. Ask for the list of items that are out of scope, such as fixes to the findings, pen testing or performance work, and confirm who owns the findings and the CI files at the end.
Most review work is one-time, but code keeps changing after handover. For that reason DeployOpen also offers ongoing review as a retainer, which covers review of merge requests, periodic deprecation checks against new core and contributed releases and updates to the CI rules as the team adopts new standards. Ask any provider, including DeployOpen, to state in writing what the retainer covers, how requests are prioritized and how quickly the team can expect a response. DeployOpen does not hold certifications or partner status that replace this review, so the value comes from the scope, the deliverables and the team's own follow-through.
How DeployOpen works on this
DeployOpen plans, implements, tests, documents and supports self-hosted Drupal inside your own infrastructure, so code review and coding standards are handled as part of the same written scope. Engagements are fixed-scope, with deliverables and timelines agreed in writing before any review work starts.
How we scope
- Inventory the custom modules, themes and configuration that the review will cover, and separate them from contributed code.
- Agree in writing which Drupal core version and PHP version the code must meet, such as Drupal 11 on PHP 8.3 or later.
- Define the deprecation check target and the standards ruleset, including any exceptions your team has already approved.
How we build
- Run Drupal coding standards and deprecation checks against the custom codebase and record findings by severity and module.
- Review pull requests against a written checklist covering services, hooks, plugins, security handling and upgrade risk.
- Wire the same standards checks into your CI pipeline so failing builds show the rule and the offending file.
How we hand over
- Deliver a review report listing each finding, its remediation and the owner responsible for fixing it.
- Provide a handover checklist that your internal team or next agency can run on every future change.
- Document the CI configuration, rule versions and how to update them when Drupal core or the standards change.
How we support
- Offer ongoing review and standards maintenance as a retainer after the initial handover, if your team wants it.
- Re-run deprecation checks ahead of each planned Drupal core upgrade so the upgrade scope stays visible.
- Confirm with your team the review cadence, reviewers and escalation path before the engagement closes.

