Skip to header Skip to main navigation Skip to main content Skip to footer
Drupal Maintenance and Developments in Austin TX
Drupal Care

Main navigation

  • Home
  • Why Drupal Care?
  • What’s Included
  • About
  • Our Approach
  • Prices & Plans
  • Portfolio (opens in new tab)
  • Blog
  • Videos
  • Contact
  • Site Evaluation

Drupal Code Review Services: Security, Upgrade Readiness, and Architecture Findings

Alaa Haddad, professional Drupal developer based in Austin, TX   Drupal Care
  1:53 PM CDT, Tue September 15, 2026
Share

A code review answers a question most site owners cannot answer themselves: is the custom code on this site sound, and what will it cost us next?

That question comes up at predictable moments — before an upgrade, when inheriting a site, before extending something significantly, or after a launch that did not go smoothly. It is a cheap question to answer and an expensive one to guess at, because the cost of bad custom code is not paid at once. It is paid at every upgrade, every incident, and every time someone new has to understand it.

This page covers what a Drupal code review examines, what it typically finds, and what a useful review report looks like.

Why Drupal Code Review Is Not Generic PHP Review

A competent PHP developer can review Drupal code for the things that are true of all PHP: readability, structure, obvious bugs. That is worth having and it misses the category of problem that costs Drupal sites the most.

Drupal-specific review asks different questions. Is this using the Entity API or going around it? Does this render array declare its cache metadata? Is this hook implementation going to survive the next major version? Is this plugin discoverable the way current Drupal expects? Is access being checked at the data layer or assumed at the presentation layer?

Code can be clean, well-organised, fully tested, and still carry every one of these problems. Framework knowledge is what turns a review from a style opinion into a risk assessment.

When a Review Pays for Itself

The moments when it is most clearly worth it:

  • Before a major upgrade — the review tells you the size of the job before you commit to a date.
  • When taking over a site — you are about to be responsible for code you did not write.
  • Before extending something substantially — building on an unsound foundation multiplies the eventual cost.
  • After a launch that went badly — to distinguish bad luck from a systemic problem.
  • Before an acquisition or investment — the codebase is part of what is being bought.
  • When the same class of bug keeps recurring — usually an architectural signal.

Correctness: Does It Do What It Claims

The first pass is whether the code does what it is supposed to, particularly at the edges.

The recurring omissions are unglamorous: what happens when a field is empty, when a referenced entity has been deleted, when a user has an unexpected combination of roles, when an external service is slow or returns an error, when two people save the same thing at once.

Drupal makes some of these easy to get wrong. An entity reference field that assumes its target still exists will fatal when it does not. A field assumed single-value that is configured as multi-value will silently use only the first. Neither shows up until real content hits it.

Security: The Checks Drupal Would Have Made

Security review of Drupal custom code is largely a search for places the code stepped outside the framework's protections.

What gets examined: entity queries and whether they check access; database queries and whether values are passed as parameters rather than concatenated; output and whether anything is being marked safe to silence escaping; file handling and whether uploads are constrained and private files actually private; and any custom permission or access callback, since those are frequently more permissive than intended.

Also worth checking — and often not — is what the code logs. Custom code that writes request payloads or configuration into the log for debugging can put credentials in a place many people can read.

Upgrade Readiness: What Breaks at the Next Major

For most clients this is the section with the most immediate financial meaning, because it converts "we should upgrade at some point" into a number.

The review runs static analysis for deprecated API use, then reads what the tools cannot see: whether hook implementations follow current patterns, whether plugin classes are discoverable the way newer core expects, whether services are injected or fetched statically, and whether the theme's template overrides have drifted from the core versions they were copied from.

The distinction that matters in the report is between debt and breakage. Deprecated code that still works is debt — schedulable. Code that will produce a fatal error on the target version is breakage, and it blocks the upgrade entirely. Those are different urgencies and should never be presented in one undifferentiated list.

Performance and Cacheability

Performance findings in custom code cluster in a few places.

Queries inside loops are the classic: code that loads a list and then loads a related entity for each item, producing a hundred queries where one would do. Drupal's entity storage supports loading many at once, and the fix is usually small.

Cacheability is the more specifically Drupal issue. Render arrays that omit cache metadata get cached wrongly or force caching off; either is a real cost. A frequent finding is code that sets max-age: 0 to fix a stale-content bug — which works, permanently disables caching for that path, and leaves the actual dependency undeclared.

// Disables caching to avoid staleness — expensive and hides the real issue.
$build['#cache']['max-age'] = 0;

// Declares what the output actually depends on, so it can be cached
// and invalidated precisely when that thing changes.
$build['#cache']['tags'] = $entity->getCacheTags();
$build['#cache']['contexts'] = ['user.roles'];

Architecture: Is It in the Right Layer

Beyond individual defects, a review looks at whether things live where they belong: business logic in modules rather than themes, presentation in themes rather than modules, structure in configuration rather than hard-coded.

It also looks at module boundaries. A single custom module handling twelve unrelated concerns cannot be tested, reused, or removed — and it means any change touches everything. The opposite failure exists too: fifteen tiny modules with circular dependencies, where nothing can be installed independently.

Neither is a bug. Both make every future change more expensive, which is exactly what a review should surface.

Configuration and What Is Not in Git

A Drupal review that only reads code is incomplete, because a great deal of a Drupal site's behaviour lives in configuration.

Worth establishing: whether configuration is exported and in version control, whether the exported configuration matches what is running in production, whether anything is being changed directly in production, and whether the site relies on content that cannot travel through configuration management at all.

Configuration drift is one of the most common findings and one of the least visible. Everything works, until a deployment silently reverts a change someone made in the admin interface three months ago and nobody can explain what happened.

Tests, and What They Actually Prove

Where tests exist, they get read — not counted.

The useful question is what would have to break for a test to fail. Tests that assert a page returns HTTP 200 prove the page did not fatal, which is worth something and considerably less than it appears. Tests that mock the thing under test prove the mock works. Tests with no negative case — that never assert something is refused, rejected or absent — cannot distinguish working code from code that always says yes.

A small number of tests that genuinely exercise business rules is worth more than a large suite that exercises the framework.

If you would like this applied to your own custom modules, a review can cover the whole codebase or one concern. Request a quote with roughly how many custom modules you have.

What a Review Deliverable Should Contain

A review is only useful if it can be acted on. A good report contains:

  • What was examined, and explicitly what was not — scope stated honestly.
  • Findings ordered by consequence, not by file, and not by how easy they are to fix.
  • For each finding: where it is, what happens because of it, and what to do — not just a rule name.
  • A separation of blocking from schedulable, especially for upgrade findings.
  • What remains uncertain and what would resolve it.
  • A recommended order of work, since not everything can be done at once.

What it should not be is a linter's output pasted into a document. Automated tools are part of the process and they cannot tell a dangerous omission from a stylistic preference — that judgement is the deliverable.

Findings That Come Up Most Often

  • Contributed or core code edited in place, so the change disappears at the next update.
  • Static service calls rather than dependency injection, making the code untestable.
  • Missing or wrong cache metadata on render arrays.
  • Entity queries without an access check.
  • Deprecated API use that is fine today and blocks the next major version.
  • Configuration in production that is not in version control.
  • One module doing too many unrelated things.
  • Debug code left in place — logging, dumps, or hard-coded test values.

These are not exotic. They are what ordinary work looks like under deadline, which is why an outside pass finds them and the team that wrote them does not.

Reviewing Your Own Code, and Why It Is Hard

Teams should review their own code, and it does not remove the value of an outside review, for a structural reason: you cannot see the assumptions you share.

If everyone on the team learned Drupal from the same codebase, its habits look like the correct way to do things. If a pattern has been used for three years, it reads as normal regardless of whether it is sound. An outside reviewer has different assumptions, which is precisely the point.

The practical middle ground: internal review for everything, an outside review at the moments listed above, when the cost of being wrong is highest.

How a Review Is Actually Conducted

The order matters, because reading code without context produces opinions rather than findings.

It starts with the site rather than the source: what it does, who uses it, what would be expensive if it broke. A module handling a contact form and a module handling billing deserve very different scrutiny, and without that context every finding gets the same weight — which is the same as giving none of them any.

Then the mechanical pass: static analysis for deprecations and obvious defects, a dependency check for abandoned or outdated modules, and a look at what is in version control against what is running. This is fast, and it produces the list of things that do not need a human to find.

Then the part that is actually the review — reading the custom modules and themes in full, tracing the paths that matter, and asking of each piece of code why it exists and what happens when its assumptions do not hold. This is where access checks, cacheability, layer violations and edge cases surface, and none of it is automatable.

Finally, the findings are ranked. That ranking is the judgement being bought: a missing access check on an admin-only route and a missing access check on a public endpoint are the same defect class and completely different problems. A report that does not distinguish them has moved the work of prioritising back onto the client, who has least information to do it with.

Reviews are usually scoped to a fixed number of days, which keeps the cost predictable and forces the ranking to be honest — if everything cannot be examined, what matters most gets examined first.

Next Steps

If you want a rough sense before commissioning anything, run a static analysis pass over your custom modules and themes. It will not find architectural problems, missing access checks or cacheability issues, but it will tell you how much deprecated API use you carry, which is a reasonable proxy for how much attention the code has had.

A full review typically covers all custom modules and themes, configuration management practice, upgrade readiness against a target version, and a security pass — delivered as a written report with findings ranked by consequence and a recommended order of work. Scope can be narrowed to one module or one concern when that is what is needed.

I have worked in web development since 2005 and I am the author of Drupal modules and themes. The code I publish under my own name is on my drupal.org profile, if you would like to see how I write it before asking me to review yours.

Request a quote with your Drupal version, roughly how many custom modules and themes you have, and what is prompting the review. Or get in touch to discuss scope first. The broader engineering approach is described on the Drupal developer page.

Drupal Services
Drupal Code Review

Footer menu

  • About
  • Privacy Policy
  • Terms & Conditions
  • Flash Web Center, LLC (opens in new tab)
  • Web Designer In Austin (opens in new tab)
  • Log in
  • Contact
  • Sitemap

Copyright © 2026 Flash Web Center, LLC | All rights reserved

Developed & Designed by Alaa Haddad