Drupal core backend backwards compatibility and internal API policy (Drupal 8 and above)

Last updated on
29 February 2024

This document defines what parts of the Drupal codebase are considered stable and reliable APIs, and which parts module and theme developers should not rely on.

For changes to render arrays, themes, markup, CSS, and JavaScript, refer to the frontend backwards compatibility policy.

Note that in addition to changes allowed here, Drupal 8 and above also includes experimental modules. These modules have their own versioning that does not follow semver (semantic versioning), and any APIs provided by such modules should be considered exempt from this policy.

API Definition

All classes, methods, functions, etc. in the Drupal 8 and above API are designated as either public or internal.

Public APIs
Contributed and custom code can depend on these APIs. We will try as hard as possible to not change this, except in critical cases such as a security issue. Any change to a public API will be documented with a change record and in the release notes.
Internal APIs
Contributed and custom code should avoid calling internal APIs because they might change in minor releases and such changes may not be documented in change records.

The Drupal core deprecation policy applies to both public and internal APIs: wherever possible, old APIs should be deprecated for removal in the next major version instead of being removed immediately. For public APIs, the deprecation process is a requirement; for internal APIs, we provide BC and deprecation where possible, but breaking changes may be made in situations where BC is not possible or has negative consequences. (Even for internal APIs, core contributors should always try to follow the deprecation process first, or document in issue discussions why deprecation is not used.)

@internal

The following parts of the code base should always be treated as internal and may change between minor versions. For patch versions, we aim to avoid any breaking changes even to @internal code unless required to fix a serious bug.

Refer to the frontend BC policy.

# The database schema for core modules
While the database query API and schema API itself are stable, the schema of any particular table is not. Writing queries directly against core tables is not recommended and may result in code that breaks between minor versions.
# HTTP headers
The following should not be considered an API:
  • The particular HTTP headers that core sends
  • Interactions with headers via PHP (set through #attached)
  • Reverse proxy configurations

Specific HTTP headers may be relied upon, but this will usually be documented either in settings.php or inline.

# Automated test classes
  • The contents of individual automated test classes provided by core modules are never considered an API. Contributed and custom modules should not directly extend individual test classes as they may change at any time.
  • Test traits and abstract base classes should generally use deprecation where possible rather than breaking backwards compatibility, but they are still considered an internal API and may change if necessary.
  • The testing framework itself (BrowserTestBase, etc.) is considered a public API and should provide backwards compatibility between minor releases.
# Controllers and Form classes
Core modules contain many route controllers that are bound to individual routes, as well as form classes. These controllers are not part of the API of the module unless specifically marked with an @api tag on the method or class.
# Plugins
Particular plugins, whether class based or yaml based, should not be considered part of the public API. References to plugin IDs and settings in default configuration can be relied upon however.
# Entity handlers
Particular entity handlers should not be considered part of the public API. The interfaces which define those entity handlers though are part of the supported API.
# Paramconverters, Access checkers, Event subscribers, hook implementations etc.
Class implementations of paramconverters, access checkers, event subscribers and similar services which are never expected to be used directly either as services or value objects, are not considered part of the API. You should not extend from these classes and provide your own implementation instead. (However, note that service machine names are public API and changing them requires a deprecation process.)

Other instances of these kind of things are:

  • Route filters
  • Route enhancers
  • Hook implementations
  • Compiler passes
  • Stack middleware implementations
# Composer Plugins
Composer plugins in the Drupal repository are not included in Drupal download archives, but are instead packaged separately. Subclassing code from Composer plugins is not supported. All classes and methods in Composer plugins are considered internal, and should be explicitly marked as such for the avoidance of doubt. In the context of a Composer plugin, @internal means that the class or method is internal to that plugin only, and should not be used from other Composer plugins or from any other part Drupal core outside of that plugin. Commands provided by Composer plugins must follow the rules for command line tools and scripts.
# Strings
User-facing strings may change between minor releases. However, any significant change to strings must be committed at least one month prior to an expected minor release to allow localization teams time to update translation sets.
# PHP and JavaScript classes
In general, only interfaces can be relied on as the public API.
With the exception of base classes, developers should generally assume that implementations of classes will change, and that they should not inherit from classes in the system, or be prepared to update code for changes if they do. See also more specific notes below
# Constructors for service objects, plugins, and controllers
The constructor for a service object (one in the container), plugin, or controller is considered internal unless otherwise marked, and may change in a minor release. These are objects where developers are not expected to call the constructor directly in the first place. Constructors for value objects where a developer will be calling the constructor directly are excluded from this statement.
# Protected methods
Protected methods of a class should be assumed @internal and subject to change unless either the class or method itself are marked with @api. Drupal leaves most internal methods protected rather than private to allow for one-off customized subclasses when needed, but in most cases that "escape hatch" should not be relied upon indefinitely. If no alternative presents itself consider filing a feature request for a more directly supported approach.
# Protected properties
Protected properties on a class are always considered @internal unless they're on a base class marked with @api
# Public properties
Public properties on a class are always considered @internal, this does not include the use of __get() and __set() to provide access to protected properties.
# Public methods not specified in an interface
Public methods on a class that aren't on a corresponding interface are considered @internal.
# Methods may be added to classes
We will add protected or public methods to classes in minor releases. Extending classes should be prepared to update names of additional methods in case of conflict.
# Underscore-prefixed functions and methods
Functions or methods with an underscore prefix (e.g. _some_function()) are considered internal, and may be used to avoid name collisions when backporting protected methods critical or major bug fixes. Extending code should not call these functions or methods directly nor use the underscore prefix for added methods.
# Locations of procedural code
The specific location of a procedural function should not be relied upon. This includes both the filenames themselves, and whether a particular function is in a particular file.
# The installer
All code in Drupal's installer should be considered @internal and subject to change. Tools implementing their own installers are encouraged to implement them as independent components.
# Function arguments
Named arguments should not be considered part of the public API.
# Render arrays (including form arrays)
More information in the frontend BC policy, Render arrays (including form arrays)
# Themes, markup, templates, and CSS
More information in the frontend BC policy, Themes, markup, templates, and CSS
# Variables passed to preprocess functions
More information in the frontend BC policy, Variables passed to preprocess functions

Interfaces

Interfaces follow a similar pattern as above with respect to @api, @internal, or neither. However, in case of neither tag, the interface is treated as an API for callers but not for implementers. In more detail:

Interfaces tagged @api
Interfaces tagged @api can be safely used as type hints and implemented. No methods will be added, removed or changed in a breaking way.
Methods may be deprecated in minor releases.
Interfaces tagged @internal or in experimental or test modules
In general you should neither type hint nor implement interfaces marked @internal or included as part of experimental module, since methods may be added, removed or changed, or the interface itself may be renamed or removed. If you have a good reason to use the interface, you should be prepared to update your code for changes. Where possible we will group any breaking changes into minor releases as opposed to patch releases, but this is not guaranteed.
Interfaces within non-experimental, non-test modules not tagged with either @api or @internal
Interfaces that are not tagged with either @api or @internal can be safely used as type hints. No methods will be changed or removed from these interface in a breaking way.
However, we reserve the ability to add methods to these interfaces in minor releases to support new features. When implementing the interface, module authors are encouraged to either:
  • Where there is a 1-1 relationship between a class and an interface, inherit from the class. Where a base class or trait is provided, use those. This way your class should inherit new methods from the class or trait when they're added.
  • Where the interface is implemented directly for other reasons, be prepared to add support for new methods in minor releases

Commandline tools

The implementation for all command line tools and scripts packaged in Drupal are considered to be @internal. However, the parameters and options accepted by these tools are themselves considered to be a public API. Changes to parameters and options must follow the rules for backwards compatibility.

Configuration elements

Configuration elements that Drupal sites and modules must provide in order to work correctly are also considered to be part of the API, and must remain compatible from one release of Drupal to another. Configuration elements that are to be removed or changed must also follow the rules for backwards compatibility.

#Composer Scaffold Configuration
Drupal sites that follow the Recommended project or Legacy project templates for Composer-managed sites may contain scaffold configuration elements in their top-level composer.json file. This configuration include specifications for file mappings, the location of the Drupal web root, and other settings. See the documentation on Using Drupal's Composer Scaffold for details on the available settings.
#Project Message Configuration
The Project Message plugin allows project templates to include follow-on instructions for sites created from the template. See the Project Message README for details.
#Vendor Hardening Configuration
The Vendor Hardening plugin allows Composer-managed sites to provide lists of directories to remove. See the Vendor Hardening README for details.

No part of any of the configuration enumerated above will be changed or removed in a breaking way.

Minor releases vs. patch releases

Drupal core patch releases (8.y.z) will contain security fixes and bug fixes only. No new functionality will be introduced, and no refactoring will be included except that necessary as part of a security or bug fix. See the Drupal 7 backport policy for more information.

Minor releases (8.x.0) may include non-API-breaking refactoring, new features, or enhancements of existing features. In such cases the core team will work to ensure that these enhancements do not alter the existing public-facing API of core systems.

Necessary security hardening takes priority over API stability.

We will make every effort to address security issues without affecting the public API. However, in some cases it will not be possible to address a security vulnerability without an API change. In such cases we will work to minimize the scope of the API change and document it thoroughly.

Backward compatibility (BC)

When we do make changes in either minor or patch releases, we will make reasonable efforts to provide a backward compatibility layer (BC).

We will take reasonable efforts to not break code that developers may be relying on.

While core developers may change implementation detail code between minor versions when it is necessary to make an improvement, we will make a reasonable effort to minimize these changes.

Help improve this page

Page status: No known problems

You can: