---
title: "Drupal 8 Composer Best Practices"
url: "/articles/drupal-8-composer-best-practices"
type: article
date: 2018-01-31
updated: 2021-01-12
---

# Drupal 8 Composer Best Practices

#  Drupal 8 Composer Best Practices 

 Whether you are new to Composer or a seasoned pro, check out these best practices for managing your Drupal 8 project.

 By

 [  James Sansbury   ](/about/james-sansbury) 

 January 31, 2018

  

Whether you are familiar with [Composer](https://getcomposer.org/doc/00-intro.md) or not, using it to manage dependencies on a Drupal project entails its own unique set of best practices. In this article, we will start by getting a Composer-managed Drupal project set up, and highlight some common questions and issues you may have along the way.

Before we dive in, though, you may be asking yourself, â&#128;&#156;Why Composer? Canâ&#128;&#153;t I just download Drupal and the modules I need without requiring another tool?â&#128;&#157; Yes you can, but you will quickly realize itâ&#128;&#153;s not a simple task:

1. Contributed modules or themes often depend on third-party libraries installed via Composer. Without using Composer for the project, youâ&#128;&#153;ll need to manage these individually when downloading, which can be quite a chore.
2. Some packages and modules only work with certain versions of PHP or Drupal. While Drupal core does help you identify these issues for modules and themes, itâ&#128;&#153;s still a manual process that youâ&#128;&#153;ll need to work through when choosing which versions to download.
3. Some packages and modules conflict with other packages. Youâ&#128;&#153;ll need to read the `composer.json` files to find out which.
4. When you upgrade a package or a version of PHP, youâ&#128;&#153;ll need to do all the above over again.
5. If youâ&#128;&#153;re thinking youâ&#128;&#153;ll use `drush dl` and friends, [theyâ&#128;&#153;ve been removed](https://github.com/drush-ops/drush/pull/2654) in favor of Composer.

Dependency management is complicated, and itâ&#128;&#153;s not going to get any easier. As [Ryan Szrama put it](https://twitter.com/DanAtSevaa/status/926843911519252481) , â&#128;&#156;if youâ&#128;&#153;re not using a dependency manager \[like Composer\], then *youâ&#128;&#153;re* the dependency manager, and you are unreliable.â&#128;&#157;

## Where do I start?

To begin, itâ&#128;&#153;s good to familiarize yourself with the [fantastic documentation on Drupal.org](https://www.drupal.org/docs/develop/using-composer) . If you havenâ&#128;&#153;t already, [install Composer](https://getcomposer.org/download/) in your development environment. When getting started, itâ&#128;&#153;s extremely important that your development environment is using the same version of PHP that the target production environment will use. Otherwise Composer may download packages that wonâ&#128;&#153;t work in production. (With larger development teams, using something like [Vagrant](https://www.lullabot.com/podcasts/drupalizeme-podcast/virtualization-with-vagrant) or Docker for local development can help ensure compatibility between local development instances and the production stack, but thatâ&#128;&#153;s [a separate topic](https://www.lullabot.com/articles/local-drupal-development-roundup)).

To start with, you need to get Drupal core installed. If you are familiar with using starter-kits like [Drupal Boilerplate](https://github.com/Lullabot/drupal-boilerplate) in Drupal 7, there is a Composer template for Drupal projects called, well, [drupal-project](https://github.com/drupal-composer/drupal-project). Itâ&#128;&#153;s the recommended starting place for a Composer-based Drupal 8 installation. Before you create itâ&#128;&#148;againâ&#128;&#148;be sure you are using the production version of PHP. Once youâ&#128;&#153;ve confirmed that, run:

```
$ composer create-project drupal-composer/drupal-project:8.x-dev \
example --stability dev --no-interaction

```

This will copy the *drupal-project* to the `example` directory, and download Drupal core and some handy packages. This is a good point to `cd` into the example directory, run `git init` , and to then create your initial commit for your project.

This also might be a good time to pop open the composer.json file that was created in there (which should look a lot like [this](https://raw.githubusercontent.com/drupal-composer/drupal-project/8.x/composer.json)). This file serves as your â&#128;&#156;recipeâ&#128;&#157; for the project.

## How do I download modules and themes?

If you are familiar with using `drush dl` in Drupal 7 to download contributed modules and themes, it can be a bit of a shift switching to a Composer workflow. There is a different syntax for selecting the version youâ&#128;&#153;d like, and in some cases itâ&#128;&#153;s best to not commit the actual downloaded files to your repository (more on that to follow).

For most instances, you can use `composer require [vendor]/[packagename]` to grab the package youâ&#128;&#153;d like. Most Drupal modules and themes are hosted on a custom Composer [repository](https://getcomposer.org/doc/05-repositories.md#repository), which *drupal-project* configures for us out of the box. That means we can use `drupal` as the vendor to download Drupal modules and themes that are maintained on drupal.org. If you arenâ&#128;&#153;t using *drupal-project*, you may need to [add the Drupal.org Composer repository](https://www.drupal.org/docs/develop/using-composer/using-composer-to-manage-drupal-site-dependencies#drupal-packagist) yourself.

For example, if you were wanting to download the [Devel module](https://www.drupal.org/project/devel), you would run the following command:

```
$ composer require drupal/devel

```

For most cases, that would download the most recent stable version of the Devel module that is compatible with your version of Drupal and PHP. If you used *drupal-project* above, it would download the module to `web/modules/contrib`. If you want to change the download destination, you can alter the paths in your composer.json. Look for the `installer-paths` under `extra` and adjust to your liking.

### Development Dependencies

The Devel module is a good example to use, as it brings up a few other things to note. For example, since the Devel module shouldnâ&#128;&#153;t be used in a production environment, you may only want it to get downloaded on development environments. With Composer, you can achieve this by passing the `--dev` flag to the `composer require` command.

```
$ composer require --dev drupal/devel

```

This will ensure that the Devel module is available for your developers when they run `composer install` , but that it wonâ&#128;&#153;t ship with your production release (which should get built using `composer install --no-dev` ). This is just an example, and your project needs may differ, but the use of `--dev` to specify dependencies only needed during development is a recommended best practice.

As a side note, if you are committing your exported Drupal 8 configuration to code, you may want to use the [Configuration Split](https://www.drupal.org/project/config_split) module to ensure that the Devel moduleâ&#128;&#153;s enabled status doesnâ&#128;&#153;t get pushed to production by mistake in a release.

### Nested Dependencies

Another thing to note is that the Devel module ships with its own `composer.json` file. Since we used Composer to download Devel, Composer will also read in Develâ&#128;&#153;s composer.json and download any dependencies that it may have. In this case (at the time of this writing), Devel doesnâ&#128;&#153;t have any required dependencies, but if you were to use Composer to download, say [Address module](https://drupal.org/project/address), it would pull in its additional dependencies as well, as Address module uses [Composer to specify some additional dependencies](http://cgit.drupalcode.org/address/tree/composer.json) .

### Downloading specific versions

While itâ&#128;&#153;s most often fine to omit declaring a specific version like we did above, there still may come a time when you need to narrow the range of possibilities a bit to get a package version that is compatible with your application.

Judging by the length of the Composer documentation on [Versions and constraints](https://getcomposer.org/doc/articles/versions.md), this is no small topic, and you should definitely read through that page to familiarize yourself with it. For most cases, youâ&#128;&#153;ll want to use the caret constraint (e.g., `composer require drupal/foo:^1.0` means the latest release in the 8.1 branch), but hereâ&#128;&#153;s some more details about versions and constraints:

1. Read up on [Semantic Versioning](https://semver.org/) if you arenâ&#128;&#153;t familiar with it. Basically, versions are in the `x.y.z` format, sometimes described as `MAJOR.MINOR.PATCH` , or `BREAKING.FEATURE.FIX` . Fixes increment the last number, new features increment the middle number, and refactors or rewrites that would break existing implementations increment the first number.
2. Use a colon after the vendor/package name to specify a version or constraint, e.g. `composer require drupal/foo:1.2.3` (which would require that exact version (1.2.3) of the foo package). If youâ&#128;&#153;re used to drush syntax (`8.x-1.2.3`), the Composer syntax does not include the Drupal version (i.e., there's no `8.x`).
3. Donâ&#128;&#153;t specify an exact version unless you have to. Itâ&#128;&#153;s better to use a constraint.
4. The caret constraint (`^`): this will allow any new versions except `BREAKING` onesâ&#128;&#148;in other words, the first number in the version cannot increase, but the others can. `drupal/foo:^1.0` would allow anything greater than or equal to `1.0` but less than `2.0.x` . If you need to specify a version, this is the recommended method.
5. The tilde constraint (`~`): this is a bit more restrictive than the caret constraint. It means composer can download a higher version of *the last digit specified* only. For example, `drupal/foo:~1.2` will allow anything greater than or equal to version 1.2 (i.e., 1.2.0, 1.3.0, 1.4.0,â&#128;¦,1.999.999), but it wonâ&#128;&#153;t allow that first 1 to increment to a 2.x release. Likewise, `drupal/foo:~1.2.3` will allow anything from 1.2.3 to 1.2.999, but not 1.3.0.
6. The other constraints are a little more self-explanatory. You can specify a [version range](https://getcomposer.org/doc/articles/versions.md#version-range) with operators, a specific stability level (e.g., `-stable` or `-dev` ), or even specify wildcards with `*`.

## What do I commit?

Now that weâ&#128;&#153;ve got a project started using Composer, the next question you may have is, â&#128;&#156;What files should I commit to my repository?â&#128;&#157; The *drupal-project* will actually ship a [default ](https://github.com/drupal-composer/drupal-project/blob/8.x/.gitignore) [`.gitignore`](https://github.com/drupal-composer/drupal-project/blob/8.x/.gitignore) file that will handle most of this for you. That said, there are a few things that may be of interest to you.

You may have noticed that after running some of the above commands, Composer created a `composer.lock` file in your repository. **The**  `composer.lock`  **file is a very important file that you want to commit to your repository anytime it changes.** When you require a new package, you may have omitted a version or a version constraint, but Composer downloaded a specific version. That exact version is recorded in the composer.lock file, and by committing it to your repository, you then ensure that anytime `composer install` is run on any other environment, the same exact code gets downloaded. That is very important from a project stability standpoint, as otherwise, Composer will download whatever new stable version is around based on whatâ&#128;&#153;s in `composer.json`.

Now that we understand more about what `composer.lock` is for, hereâ&#128;&#153;s a list of what to commit and what not to commit:

1. Commit `composer.json` and `composer.lock` anytime they change.
2. Thereâ&#128;&#153;s no need to commit anything in `./vendor` , Drupal core (`.``/web/core`), or contributed modules and themes (`.``/web/modules/contrib` or `./web/themes/contrib`). In fact, itâ&#128;&#153;s [recommended not to](https://getcomposer.org/doc/faqs/should-i-commit-the-dependencies-in-my-vendor-directory.md), as that ensures the same code is used on all environments, and reduces the size of diffs. If you really want to (because of how your code gets deployed to production, for example), it is possible, youâ&#128;&#153;ll just need to change the `.gitignore`, and *always* make sure the committed versions match the versions in `composer.lock`.
3. Commit any other custom code and configuration as usual.

## How do I keep things up to date?

Updating modules, themes, and libraries downloaded with Composer is similar to installing them. While you can run `composer update` to update all installed packages, itâ&#128;&#153;s best to save that for the beginning of a sprint, and update individual projects as needed after that. This ensures you only update exactly what you need without risking introducing bugs from upstream dependencies. If you arenâ&#128;&#153;t working in sprints, set up a regular time (weekly or monthly) to run `composer update` on the whole project.

To update the Devel module we installed earlier, you could run:

```
$ composer update --with-dependencies drupal/devel

```

You might be wondering what that `--with-dependencies` option is. If you run the update command without it, composer will update just the module, and not any packages required by that module. In most circumstances, itâ&#128;&#153;s best to update the moduleâ&#128;&#153;s dependencies as well, so get used to using `--with-dependencies`.

You can also update multiple packages separated by spaces:

```
$ composer update --with-dependencies drupal/foo drupal/bar

```

For a more aggressive approach, you can also use wildcards. For example, to update all drupal packages, you could run:

```
$ composer update --with-dependencies drupal/*

```

Once complete, be sure to commit the changes to `composer.lock`.

## What about patches?

Inevitably, youâ&#128;&#153;ll need to apply some patches to a Drupal module, theme, or possibly Drupal core. Since you arenâ&#128;&#153;t committing the actual patched modules or themes with a Composer workflow, youâ&#128;&#153;ll need some way to apply those patches consistently across all environments when they are downloaded. For this we recommend [composer-patches](https://github.com/cweagans/composer-patches). It uses a self-documenting syntax in `composer.json` to declare your patches. If youâ&#128;&#153;re using *drupal-project* mentioned above, *composer-patches* will already be installed and ready to use. If not, adding it to your project is as simple as:

```
$ composer require cweagans/composer-patches

```

While itâ&#128;&#153;s installing, **take this time to [go read the README.md](https://github.com/cweagans/composer-patches/blob/master/README.md) on GitHub**.

Once installed, you can patch a package by editing your `composer.json` . Continuing with our example of Devel module, letâ&#128;&#153;s say you wanted to patch Devel with a patch from [this issue](https://www.drupal.org/project/devel/issues/2912102). First step?â&#128;&#148;copy the path to the patch file you want to use. At the time of this writing, the most recent patch is `https://www.drupal.org/files/issues/2860796-2.patch` .

Once you have the path to the patch, add the patch to the `extras` section of your composer.json like so:

```
"extra": {
  "patches": {
    "drupal/devel": {
      "2860796: Create a branch of devel compatible with Media in core": "https://www.drupal.org/files/issues/2860796-2.patch"
    }
  }
} 

```

You can see that in the above example, we used the node id (`2860796`) and title of the Drupal.org issue as the key, but you may decide to also include the full URL to the issue and comment. Itâ&#128;&#153;s unfortunately not the easiest syntax to read, but it works. Consistency is key, so settle on a preferred format and stick to it.

Once the above has been added to composer.json, simply run `composer update drupal/devel` for the patch to get applied.

```
$ composer update drupal/devel
Gathering patches for root package.
Removing package drupal/devel so that it can be re-installed and re-patched.
Deleting web/modules/contrib/devel - deleted
> DrupalProject\composer\ScriptHandler::checkComposerVersion
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
Gathering patches for root package.
Gathering patches for dependencies. This might take a minute.
- Installing drupal/devel (1.2.0): Loading from cache
- Applying patches for drupal/devel
https://www.drupal.org/files/issues/2860796-2.patch (2860796: Create a branch of devel compatible with Media in core)

```

And finally, **commit the changes to both**  `composer.json`  **and**  `composer.lock` .

If any of your dependencies declare patches (for example, letâ&#128;&#153;s say a module requires a patch to Drupal core to work properly), youâ&#128;&#153;ll need to [explicitly allow that in your composer.json](https://github.com/cweagans/composer-patches#allowing-patches-to-be-applied-from-dependencies). Hereâ&#128;&#153;s a quick one-liner for that:

```
$ composer config extra.enable-patching true

```

## How do I remove a module or theme?

Removing a module or theme using Composer is fairly straightforward. We can use `composer remove` for this:

```
$ composer remove drupal/devel

```

If you added `drupal/devel` with the `--dev` flag, you might get a prompt to confirm the removal from `require-dev` . If you want to avoid that prompt, you can use the `--dev` flag in your remove command as well:

```
$ composer remove --dev drupal/devel

```

Once itâ&#128;&#153;s done, commit your `composer.json` and `composer.lock` file.

## Anything else to watch out for?

### Removing patches

If you need to remove a module or theme that has patches, the patches donâ&#128;&#153;t automatically get removed from the `composer.json` file. You can either remove them by hand in your editor, or run:

```
$ composer config --unset extra.patches.drupal/devel

```

### Lock hash warnings

On a related note, any time you manually edit `composer.json` in this way without a subsequent `composer update`, the hash in `composer.lock` will be out of date, throwing warnings. To fix that without changing the code you have installed, run `composer update --lock`. That will just update the hash in the lock file without updating anything else. Once complete, commit the `composer.json` and `composer.lock`.

### PHP Versionsâ&#128;¦again

As mentioned early on, itâ&#128;&#153;s [very important](https://www.doctrine-project.org/2017/07/25/php-7.1-requirement-and-composer.html) with a Composer-based workflow that the various environments use the same major and minor release of PHP. If you arenâ&#128;&#153;t using Vagrant or Docker for local development and are finding it difficult to standardize, you can [enforce a specific PHP version](https://getcomposer.org/doc/06-config.md#platform) for Composer to adhere to in `composer.json`.

```
$ composer config platform.php 7.1

```

Once added, donâ&#128;&#153;t forget to update your lock file and commit the resulting `composer.json` and `composer.lock` .

```
$ composer update --lock

```

### Speeding things up

There are some Composer operations that can be slow. To speed things up, you can install [Prestissimo](https://github.com/hirak/prestissimo), which will allow Composer to run operations in parallel. Itâ&#128;&#153;s best to do this globally:

```
$ composer global require hirak/prestissimo

```

## Itâ&#128;&#153;s not perfect

You will inevitably run into some frustrating issues with a Composer workflow that will cause you to question its wisdom. While there may be scenarios where it might make sense to abandon it for other options, you will bear the burden of managing the complexities of your projectâ&#128;&#153;s dependencies yourself. If you stick with Composer, the good news for you is that you are not alone, and these issues promise to improve over time because of that. Think of it as an investment in the long-term stability of your project.

[*Collage of JoAnn Falletta*](https://voiceofniagara.files.wordpress.com/2010/03/bpo-fallettacollage.jpg) by [Mark Dellas](https://www.mdellas.com/).

 Published in:

- [  Deployment   ](/topics/deployment)
- [  Drupal Development   ](/topics/drupal-development)
- [  System Administration   ](/topics/system-administration)

 

   You must have JavaScript enabled to use this form.

## Get in touch with us

### Tell us about your project. We'd love to hear from you!

 First Name  

 Last Name  

 Your email address  

 Your organization  

 How you heard about us  

 Tell us more 

     Leave this field blank