How to deprecate and remove an extension

Last updated on
8 June 2026

Introduction

This document explains how to deprecate and remove an extension. It sits alongside the how to deprecate document which explains how to deprecate other code.

Remove a core module by incorporating it into another core module or subsystem

  1. Any APIs it provides should be individually deprecated.
  2. Set the module to lifecycle: obsolete in module_name.info.yml and an update added to uninstall it.
  3. Remove the module in the next major release, where the update to uninstall it must be removed (incrementing hook_update_last_removed() to ensure it is run before the module is uninstalled).

An example of this is when the field type provided by entity_reference module was moved to a core field type, leaving the module empty and safe to uninstall.

Remove a core extension and move it to a contributed project

Two Meta issues are needed to track and coordinate the work of removing an extension. One issue is filed against the latest minor of current major version for deprecating the extension and the other against the next major version for removing the extension.

All issues for the core extension are marked "Postponed" except for those that fall under the allowed changes for extensions approved for removal. A template for the issue comment is provided below.

Deprecate in the current major version

Create a Meta issue to follow progress of deprecation

Make a child issue of the core meta issue for deprecating code in the current major version. For example, the Drupal 9 meta was #3118154: [meta] Deprecate dependencies, libraries, modules, and themes that will be removed from Drupal 10 core by 9.4.0-beta1, and all extension deprecations should be children of that issue.

Make this new issue a Meta ("Plan" issue type), because several child issues will be needed. There may be more children depending on the scope of the changes required.

Use the task to deprecate issue summary template for this issue.

Create the contrib project with a stable release

A stable release is needed so that site owners are able to take action as soon as they see warnings and change records.

The stable release of the contrib project should be released before the alpha version of the Major release in which the modules are removed. The stable release must support the version of core when the extension was deprecated.

The first task for the new maintainer of the extension in contrib is to create an empty project. If there are any difficulties with this, then make an issue in the Drupal.org infrastructure issue queue.

Once the above items in the meta are complete, create the contrib project. Use a sub-tree split OR filter-repo to to retain git history. The following "git subtree" instructions are for a module but are also applicable to a theme.

# 1. Create a working directory and clone drupal.
$ mkdir working_dir
$ cd working_dir
$ git clone https://git.drupalcode.org/project/drupal.git
$ cd drupal

# 2. Split core/modules/MODULE_NAME
$ git subtree split -P core/modules/MODULE_NAME/ -b MODULE_NAME

# 3. Split modules/MODULE_NAME and merge with history for core/modules/MODULE_NAME
# Find the commit where modules were moved to core/modules
$ git log -- modules/MODULE_NAME
# If no output, SKIP TO STEP 4. Otherwise, note the SHA of the most recent commit.
$ git tag core-move SHA
$ git checkout core-move^
$ git subtree split -P modules/MODULE_NAME/ -b MODULE_NAME-pre-core
$ git checkout MODULE_NAME
# Rebase the post-core-move branch onto the pre-core move. Note
# that we don't want the actual commit that did the move: it has
# no effect in our split since the module is top-level in both the
# new branches.
$ git rebase --onto MODULE_NAME-pre-core core-move

# 4.Create new repo from the subtree split.
$ git checkout MODULE_NAME
# Create repository in a new directory.
$ mkdir MODULE_NAME
$ cd MODULE_NAME
$ git init
$ git pull drupal_dir/drupal MODULE_NAME
$ git remote add origin https://git.drupalcode.org/project/MODULE_NAME.git

The following alternate instructions using "git filter-repo" are applicable especially if you have a core module that existed as a single file at module/MODULE_NAME.module before core modules were moved to individual subdirectories in July of 2006.

You will need to install git-filter repo from https://github.com/newren/git-filter-repo or a code repository such as apt install git-filter-repo

# 1. Create a working directory and clone drupal.
$ mkdir working_dir
$ cd working_dir
$ git clone https://git.drupalcode.org/project/drupal.git
$ cd drupal

# 2. For ease of copy/paste set up an env variable with the module name e.g. book or forum
$ export MODULE_NAME=something

# 3. For documentation consistency, create a branch from the default branch
$ git checkout -b $MODULE_NAME-current

# 4. Find the commit before the module was moved to core/. If the log is empty, skip to step 7
$ git log -2 -- modules/$MODULE_NAME
# Take the second SHA (the last commit hash to the module before the move) and create a new branch:
$ git checkout -b $MODULE_NAME-pre-core SHA


# 5. Find the commit before the module was moved to modules/$MODULE_NAME . If the log is empty, skip to step 7
$ git log -2 -- modules/$MODULE_NAME.module
# Take the second SHA (the last commit hash to the module before the move) and create a new branch:
$ git checkout -b $MODULE_NAME-pre-directory SHA

# 6. create a new empty git repo for the split module in working_dir
$ cd ..
$ mkdir $MODULE_NAME
$ cd $MODULE_NAME && git init
# Make sure that your git name and email is correctly set up before you do anything more...

# 7. Use git filter-repo to create branches where the module is at the root of the repo
$ git filter-repo --source=../drupal/.git --refs=$MODULE_NAME-current --subdirectory-filter=core/modules/$MODULE_NAME
# You may need to skip either of the next 2 sets of commands if you didn't have history from the other locations
$ git filter-repo --source=../drupal/.git --refs=$MODULE_NAME-pre-core --subdirectory-filter=modules/$MODULE_NAME
$ git filter-repo --source=../drupal/.git --refs=$MODULE_NAME-pre-directory --path=modules/$MODULE_NAME.module --path-rename modules/$MODULE_NAME.module:$MODULE_NAME.module

# NOTE: sometime the file move is (apparently) followed by git filter-repo so the last command ends up being redundant

# 8. Rebase the history together. Check the results carefully!
# Rebase the post-core-move branch onto the pre-core move. Skip this if you do't have it.
$ git checkout $MODULE_NAME-pre-core
$ git rebase $MODULE_NAME-pre-directory
$ git checkout $MODULE_NAME-current
$ git rebase $MODULE_NAME-pre-core


# 9. Create the branch you are going to push, and push to origin
$ git checkout -b 1.x $MODULE_NAME-current
$ git remote add origin git@git.drupal.org:project/$MODULE_NAME.git
$ git push origin 1.x

  1. Configure GitLab CI to run tests and code style checks for the module. This may require copying core's phpcs.xml.dist file and removing references to paths that are no longer applicable. There may also be rules in the phpstan baseline that need to be transferred over to the contrib project.
  2. Ensure tests run successfully.
  3. Check it works and can be installed with composer.
  4. Create a stable release before the alpha version of the Major release in which the modules are removed. The stable release must support the version of core when the extension was deprecated.

Deprecate the core extension

Make a child issue of the issue created in Create a Meta issue to follow progress of deprecation for the actual deprecation.

Use the deprecate extension issue summary template for this issue.

Remove in the next major version

Create a Meta issue to follow progress of removal

Make a child issue of the core meta issue for removing deprecations in the next major version. For example, the Drupal 10 meta was #3213858: [META] Remove deprecated modules and themes from the Drupal 10 branch, and all extension removals should be children of that issue, since these are required steps only once the extension is deprecated.

Make this new issue a Meta because several child issues are needed to address, at least, the following points. There may be more children depending on the scope of the changes required.

Use the tasks to remove issue summary template for this issue.

When moving the extension to contrib, after making the move, you may need to add a composer.json if your project has external dependencies. You will also need to remove the version: entry from the info.yml and add a core_version_requirement entry.

Test the upgrade path

Make a child issue of the issue created in Create a Meta issue to follow progress of testing the upgrade path.

Use the test the upgrade patch issue summary template for this issue.

Templates

Issue summary templates.

Tasks to deprecate meta

Title: [meta] Tasks to deprecate the EXTENSION_NAME {module/theme}

<h3 id="summary-problem-motivation">Problem/Motivation</h3>
Track the steps needed to deprecate extension EXTENSION_NAME. See <a href="https://www.drupal.org/about/core/policies/core-change-policies/module-or-theme-removal-process#s-remove-a-core-module-and-move-it-to-a-contributed-project">Remove a core extension and move it to a contributed project</a> of the deprecation policy.

The removal of extension EXTENSION_NAME was approved in [#TBA].

<h3 id="summary-remaining-tasks">Remaining tasks</h3>
<ol>
  <li>Begin finding someone to maintain the contrib version of the extension.</li>
   <li>Add the extension to the list of the <a href="https://www.drupal.org/docs/core-modules-and-themes/deprecated-and-obsolete#s-upcoming-deprecations">Upcoming Deprecations</a>.</li>
  <li>Move integrations implemented by other modules to the extension.
    Create child issues or child meta issues, as needed, to address the following points. Not all points will apply to all extensions.
    <ul>
      <li>Move tests to the extension.</li>
      <li>Move Help Topics to the extension.</li>
      <li>For a theme, move theme-specific integration to the theme.</li>
      <li>Remove the extension from one or more profiles.</li>
      <li>Remove references to the extension from database dumps.</li>
      <li>Remove templates from the extension’s markup.
        <ul>
          <li>Remove templates from themes that are staying in core, leave them in deprecated themes</li>
          <li>Keep skipping the template in the stable copies test.</li>
        </ul>
    </ul>
  </li>
  <li>Do a thorough search of core for any remaining references to the extension. If references are found, outside of the extension, then create issues to remove the references.</li>
  <li>Create the contrib project with a stable release, before the alpha version of the major release. Follow the process in <a href="https://www.drupal.org/about/core/policies/core-change-policies/module-or-theme-removal-process#s-create-the-contrib-project-with-a-stable-release">Create the contrib project with a stable release</a> for creating the sub tree split.
</li>
  <li>Create a child issue to deprecate the core extension. Use the <a href="https://www.drupal.org/about/core/policies/core-change-policies/module-or-theme-removal-process#s-deprecate-the-core-extension">Issue Summary template or core extension deprecation</a>.</li>
  <li>Create an issue, 'Ensure that EXTENSION_NAME does not get special core treatment' in <a href="https://www.drupal.org/project/issues/project_composer" rel="nofollow"> the packages.drupal.org issue queue</a> to ensure that the composer namespace for the contributed project matches the core one after removal. This is to allocate the <code class="language-php">drupal/EXTENSION_NAME</code> Composer namespace to contrib extension rather than the core extension.</li>
<li>Update the <a href="https://www.drupal.org/docs/core-modules-and-themes/deprecated-and-obsolete">Deprecated and obsolete extensions</a></li>
</ol>

Deprecate the extension

Title: Deprecate the EXTENSION_NAME {module/theme}

<h3 id="summary-problem-motivation">Problem/Motivation</h3>
Deprecate extension EXTENSION_NAME. See <a href="https://www.drupal.org/about/core/policies/core-change-policies/module-or-theme-removal-process#s-remove-a-core-module-and-move-it-to-a-contributed-project">Remove a core extension and move it to a contributed project</a> of the deprecation policy.

The removal of EXTENSION_NAME was approved in [#TBA].

This is postponed until all usages of this extension outside of the extension itself is properly handled.

<h3 id="summary-remaining-tasks">Remaining tasks</h3>
This is the actual deprecation and is limited to the following tasks. See the parent issue for the full process.
<ol>
    <li>Create a section on <a href="https://www.drupal.org/node/3223395">Deprecated and obsolete modules and themes</a> to provide recommendations for sites using extension EXTENSION_NAME. The recommendations are to include instructions for sites using the extension and for contributed projects that depend on then extension.</li>
    <li>Add the following to the extension info.yml file</li>
    <code class="language-php">
lifecycle: deprecated
lifecycle_link: https://www.drupal.org/node/3223395#s-EXTENSION_NAME</code></li>
    <li>Add <code class="language-php">#[IgnoreDeprecations]</code> to all tests in the extension.</li>
    <li>Add a change record and include a link the the doc page.</li>
    <li>Deprecate any library that is only used by the extension.</li>
    <li>Do a thorough search of core for any remaining references to the extension. If references are found, outside of the extension, then creates issues to remove the references.</li>
</ol>
<h3 id="summary-release-notes">Release notes snippet</h3>

Tasks to remove meta

Title: [meta] Tasks to remove the EXTENSION_NAME {module/theme}

<h3 id="summary-problem-motivation">Problem/Motivation</h3>
Track the steps needed to remove extension EXTENSION_NAME from core to contrib. See <a href="https://www.drupal.org/about/core/policies/core-change-policies/module-or-theme-removal-process#s-remove-a-core-module-and-move-it-to-a-contributed-project">Remove a core extension and move it to a contributed project</a> of the deprecation policy.

The removal of EXTENSION_NAME was approved in [#TBA].

<h3 id="summary-remaining-tasks">Remaining tasks</h3>
<ol>
  <li>Create a child issue to remove the core extension. Use the <a href="https://www.drupal.org/about/core/policies/core-change-policies/module-or-theme-removal-process#s-remove-the-extension">Issue Summary template</a>.</li>
  <li>Deprecate the extension in the current minor development branch. If the next major branch is open, the extension can be removed via a separate patch (to avoid dependent patches), otherwise an issue should be opened to remove the extension once the next major branch is open.</li>
  <li>Manually test upgrading from the core extension to the contrib extension. There is an <a href="https://www.drupal.org/about/core/policies/core-change-policies/module-or-theme-removal-process#s-test-the-upgrade-path">issue template</a> available for this.</li>
  <li>Triage core issues for EXTENSION_NAME in the core queue and move either to the contrib project or the relevant core component. Restore the status to what it was before it was postponed. Also, is the issue moved to contrib, then move any change record.
</li>
  <li>Move all core documentation for EXTENSION_NAME to the contrib project. Make issues in the contrib project to update or add links to the documentation on the project page.</li>
  <li>When the issue in https://www.drupal.org/project/project_composer is Fixed update the documentation with the new Composer command.</li>
</ol>
<h3 id="summary-release-notes">Release notes snippet</h3>

Test the upgrade path

Title: Test the upgrade path for EXTENSION_NAME {module/theme}

<h3 id="summary-problem-motivation">Problem/Motivation</h3>
Manually test the upgrade path from EXTENSION_NAME in core in current development branch to contrib in next major development branch works correctly..

<h3 id="summary-remaining-tasks">Remaining tasks</h3>
<h4>Install Drupal current development branch on a machine with composer.</h4>
<ol>
  <li><code>composer create-project drupal/recommended-project:x.y.* drupal-EXTENSION_NAME</code></li>
  <li>Go to the extension directory <code>cd [drupal-EXTENSION_NAME</code></li>
  <li>Enable development settings <code>cp web/sites/example.settings.local.php web/sites/default/settings.local.php</code></li>
  <li>Add Drush <code>composer require drush/drush</code></li>
  <li>Install Umami <code>vendor/bin/drush si demo_umami</code></li>
  <li>Enable settings.local.php at the end of <code>web/sites/default/settings.php</code></li>
</ol>
<h4>Enable and test</h4>
<ol>
  <li>Enable the extension <code>vendor/bin/drush en -y EXTENSION_NAME</code></li>
  <li>Test the extension functionality. Comment on what was tested.</li>
  <li>Confirm errors via update.php and warnings via <code>drush updb</code> about the missing core module.</li>
</ol>
<h5>Upgrade to contrib version and test</h5>
<ol>
  <li><code>
composer require drupal/EXTENSION_NAME
vendor/bin/drush cr</code></li>
  <li>Test the extension functionality. Comment on what was tested.</li>
</ol>
<h4>Upgrade to next major version and test</h4>
<ol>
  <li><code>
composer config minimum-stability dev
composer require drupal/core-composer-scaffold:dev-main drupal/core-project-message:dev-main drupal/core-recommended:dev-main drush/drush -W</code></li>
  <li> Update to next major version Either:
    <ul>
      <li>Via the UI: visit <code>update.php</code> for the local test site</li>
      <li>Via drush: <code>
vendor/bin/drush updb
vendor/bin/drush cr</code></li>
    </ul>
  </li>
  <li>Confirm the core copy of EXTENSION_NAME is not present.</li>
  <li>Test
    <ul>
      <li>Test the extension functionality. Comment on what was tested.</li>
      <li>Confirm errors <code>admin/reports/status</code></li>
    </ul>
  </li>
</ol>

Remove the extension

Title: Remove the EXTENSION_NAME {module/theme}

<h3 id="summary-problem-motivation">Problem/Motivation</h3>

<h3 id="summary-remaining-tasks">Remaining tasks</h3>
<ol>
  <li>The change record for this issue should include a link to recommendations page, https://www.drupal.org/node/3223395#s-EXTENSION_NAME. (For example, the <a href="https://www.drupal.org/node/3266403">CR for removing HAL</a>)</li>
  <li>Tag this issue 'Needs release note.'</li>
  <li>Remove the extension ;-).</li>
  <li>Update update path tests as needed. See <a href="https://www.drupal.org/docs/drupal-apis/update-api/writing-automated-update-tests">Learn how to write an automated update test</a>.</li>
  <li>Remove references from <code class="language-php">core/phpstan-baseline.neon</code>.</li>
  <li>Remove any spelling words specific to the extension from the dictionaries.</li>
  <li>Add the extension to the relevant removed list, either DRUPAL_CORE_REMOVED_MODULE_LIST or DRUPAL_CORE_REMOVED_THEME_LIST, in system.install.</li>
  <li>Check for references in @todo.</li>
</ol>

<h3 id="summary-release-notes">Release notes snippet</h3>
The EXTENSION_NAME {module/theme}has been removed from core, and can now be installed as a contrib {module/theme}.

Issue comment for postponing

The EXTENSION_NAME was approved for removal in [#policy issue].

This is now Postponed. The status is set according to two policies. The <a  href="https://www.drupal.org/about/core/policies/core-change-policies/module-or-theme-removal-process#s-remove-a-core-extension-and-move-it-to-a-contributed-project">Remove a core extension and move it to a contributed project</a> and the <a href="https://www.drupal.org/about/core/policies/core-change-policies/allowed-changes#s-extensions-approved-for-removal">Extensions approved for removal</a> policies.

The deprecation work is in [#TBA] and the removal work in [#TBA].

EXTENSION_NAME will be moved to a contributed project after the Drupal TBA.x branch is open.

Help improve this page

Page status: No known problems

You can: