Drupal Coding Best Practices

Posted by admin
Tuesday March 17th , 2026 6:45 p.m.



Drupal Coding & PR Standards

These standards exist to ensure that code reviews and PR feedback are grounded in shared, documented conventions — not personal preference. Blocking a PR based on these rules is always appropriate; blocking one based on undocumented opinion is not.


1. Use Composer for dependency management and autoloading

Composer is the standard for managing PHP dependencies in modern Drupal projects. All dependencies must be declared in composer.json.

require_once is never acceptable — not in modules, not in themes, not anywhere in the codebase.


2. Use Drupal's Request/Response API — never raw superglobals

Drupal has adopted Symfony's HttpFoundation component since Drupal 8. That means $_GET, $_POST, and other raw superglobals are off the table. Using them bypasses Drupal's request handling layer, introduces security risk, and produces code that is harder to test and maintain.

Use \Drupal::request() or, better, inject the request_stack service.


3. Follow Drupal's Inversion of Control and Dependency Injection conventions

Direct instantiation — new SomeService() inside a method or function — is not acceptable in Drupal 8+. Services must be accessed through the service container, either via constructor injection (preferred) or, in procedural contexts, \Drupal::service().

This is not stylistic preference; it is a core architectural requirement.

Hardcoded instantiation

Dependency injection

Hides dependencies from callers

Declares dependencies explicitly

Ties the call site to a concrete class

Accepts any compatible interface implementation

Cannot be mocked for unit testing

Fully testable with mock objects

Violates Drupal's architectural conventions

Adheres to Drupal/Symfony standards


4. Prefer OOP over procedural code; use static methods only when justified

Drupal 8+ is built on an object-oriented, Symfony-based architecture. Procedural code is a legacy pattern and should not appear in new work. Static utility methods may be acceptable in limited, well-reasoned cases — but that reasoning should be explicit and defensible.

See: When to use static methods in PHP


5. Do not commit commented-out code to production branches

Commented-out code creates noise, obscures intent, and suggests incomplete work. The only exception is code that is deliberately preserved because it has a reasonable chance of being reinstated — and that case should be rare and accompanied by an inline explanation of why it is being kept.

Work-in-progress code belongs in a git stash or a local feature branch, not in a PR.

 
 
 
 
 
 
 
 
 
 

Category

Best Practice

Rationale and Source

Services & Dependency Injection

Register classes as services and use dependency injection to access them.

This is the foundation of modern, object-oriented Drupal development and follows the Symfony framework's approach. It makes code loosely coupled and easier to test.

 

Avoid the global \Drupal class within object-oriented code (controllers, plugins, services, etc.).

The global class should primarily be used in global functions (like hook implementations) where dependency injection is not possible.

 

Inject dependencies via the constructor instead of calling the global container.

This explicitly defines a class's dependencies, making the code's requirements clear.

Coding Standards

Follow the official Drupal Coding Standards, using tools like phpcs with Drupal standards to enforce them.

Ensures consistency across the project, making the codebase easier for other developers to read and contribute to.

 

Utilize static analysis tools such as PHPStan or Psalm to catch errors early in the development process.

Helps verify code correctness and proper API usage.

Module Development

Write object-oriented code for services and complex logic.

Leverages modern PHP features for better code organization and maintainability.

 

Place custom classes in the modulename/src directory to utilize PSR-4 autoloading.

This ensures proper loading of classes without manual includes.

 

Use the Configuration Management system for configuration values instead of hardcoding them.

Allows configuration to be deployed between different environments (development, staging, production) reliably.

General Best Practices

Never hack core or contributed modules; use hooks, overrides, and patches to extend functionality.

Modifying core makes updates difficult and can introduce security vulnerabilities.

 

Follow the Single Responsibility Principle (SRP): a class should have only one job.

Promotes focused, efficient classes that are easier to manage and test.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Project Setup

  • Use Drupal’s recommended project structure via Composer:
composer create-project drupal/recommended-project my_site_name
  • Leverage version control (e.g., Git) to manage codebase changes.
  • Document project goals, requirements, and assumptions in a dedicated README file.
  • Use DDEV or Lando for consistent local development environments.

2. Coding Standards

  • Follow Drupal’s Coding Standards:
  • Use phpcs with Drupal coding standards:
composer require --dev drupal/coder ./vendor/bin/phpcs --standard=Drupal .
  • Utilize PHPStan or Psalm for static analysis.
  • Avoid hardcoding configuration values; use Drupal’s Configuration Management.

3. Module Development

  • Custom Module Naming: Prefix modules with your project or company name to avoid namespace conflicts.
  • Follow the Drupal API Documentation:
  • Use hook_* implementations for extending functionality.
  • Write object-oriented code for services.
  • Avoid custom code duplication; explore contributed modules first.
  • Use the Dependency Injection pattern instead of relying on global services.

4. Theming

  • Use Twig templates for rendering HTML output.
  • Organize CSS, JavaScript, and images under /themes/{your_theme}/assets/.
  • Implement BEM (Block Element Modifier) for CSS naming conventions.
  • Use libraries.yml to manage asset inclusion.
  • Follow the Drupal theme layer practices:
  • Avoid inline styles.
  • Minimize JavaScript logic in templates; use Drupal.behaviors.

5. Performance Optimization

  • Enable caching:
  • Use Render Caching for blocks, views, and entities.
  • Leverage Drupal’s Page Cache and Dynamic Page Cache.
  • Use lazy loading for images.
  • Optimize database queries by reviewing with devel or webprofiler.
  • Minimize external API calls; use caching mechanisms for their results.
  • Aggregate and minify CSS and JavaScript.

6. Content Management

  • Use structured Content Types, fields, and taxonomies for organized content.
  • Implement Paragraphs or Layout Builder for reusable and flexible content components.
  • Use the Media module for managing images, videos, and files.

7. Security Best Practices

  • Apply updates regularly using Composer:
composer update drupal/core --with-dependencies
  • Avoid exposing sensitive data (e.g., API keys) in the codebase.
  • Use Database Encryption for sensitive user data.
  • Use trusted host patterns in settings.php.
  • Implement proper user permissions and roles; follow the principle of least privilege.

8. Automated Testing

  • Write Unit Tests and Functional Tests for custom modules and themes using PHPUnit.
  • Use Behat for behavioral testing.
  • Include SimpleTest for module-specific testing scenarios if needed.

9. Deployment and Maintenance

  • Use Configuration Management to deploy settings across environments:
drush config:export drush config:import
  • Automate deployments using tools like Jenkins or GitHub Actions.
  • Perform database backups before major updates.

10. Documentation and Collaboration

  • Document custom modules and functionality within the code using DocBlocks.
  • Maintain a knowledge base or wiki for non-technical stakeholders.
  • Use issue tracking tools like Jira, Trello, or GitHub Issues.

11. Monitoring and Logging

  • Use the Syslog module for advanced logging.
  • Monitor site performance with tools like New Relic or AppDynamics.
  • Track PHP errors with Sentry or Loggly.

12. SEO and Accessibility

  • Use the Pathauto module for clean URL aliases.
  • Ensure compliance with WCAG standards:
  • Use the Siteimprove module for accessibility checks.
  • Implement metadata and structured data with the Metatag and Schema.org Metatag modules.

13. Contributed Modules

  • Select actively maintained and security-reviewed modules.
  • Regularly review Drupal’s Security Advisory.
  • Avoid over-reliance on unnecessary modules; periodically audit module usage.

14. Community Engagement