Last updated 30 May 2017.

Note: This document is still under discussion. See #2550249: [policy, then meta] Document @internal APIs both explicitly in phpdoc and implicitly in d.o documentation

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. The goal is to provide a greater degree of clarity about what constitutes Drupal's APIs and what constitutes implementation details.

Note that in addition to changes allowed here, Drupal 8 also includes experimental modules as part of the code base. These modules have their own versioning which does not follow semver and any APIs provided by those modules should be considered exempt from this policy.

API Definition

All classes, methods, functions, etc. in Drupal 8's API have one of the following three distinctions:

  • @api: Contrib/custom code should feel free to depend on these APIs. We will try as hard as possible to not change this, except in cases for a critical issue such as a security issue.
  • @internal: We can and will change these if there is any good reason to; should not be called by contrib/custom code.
  • (undocumented): If an API carries neither of these distinctions, we treat it as "we'll try very hard not to break it, but we may. These will receive a release notes mention.

For core developers, in a nutshell: If it's not documented below, probably best/safest to just deprecate, since it's not possible to know if a contrib/custom module out there has an esoteric use case.


Anything not marked @internal or covered by the below should not break BC without a specific reason.

See for more nuanced discussion.


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

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.
Render arrays (including form arrays)
While the Render and Form APIs themselves are stable, the exposed data structures used to build and cache pages and forms are not. We will change these structures where necessary to make usability improvements or add features in minor versions. This means alter hook implementations may need to be updated.
Variables passed to preprocess functions
The content of variables passed to preprocess functions may change in similar ways as render arrays. Since other preprocess functions may also make those changes, preprocess implementations should be written in such a way as to account for the change or be prepared to update
Themes, markup, templates, and CSS
We will change the markup and CSS that core generates for user interface and theme improvements. Specific core themes (Bartik, Seven) are considered internal and may change. The stable base themes (Stable, Classy) are considered public API, with stable templates, markup, and CSS, so themes needing BC support should extend one of those base themes.
HTTP headers
The particular HTTP headers that core sends should not be considered an API. Neither interactions with headers via PHP (set through #attached), nor reverse proxy configurations. Specific HTTP headers may be relied upon, but this will usually be documented either in settings.php or inline.
The contents of automated tests provided by core modules are never considered part of the API. While the testing framework itself may be treated as an API individual test classes are always internal.
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.
Particular plugin classes 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 etc.
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.
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.


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

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) for anything not marked @internal.

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.

See also: What patches may be added to a stable release of Drupal 7 core?