By Greg Nowak. Last updated 2026-07-07.
Composer is not just a developer convenience in a Drupal project. It is part of the delivery system. It decides what gets built, which versions the team shares, and whether staging, production, and local environments can be explained without guesswork.
For business owners, operations leads, and agency teams, that matters because dependency drift is rarely visible until it becomes a missed release window, a fragile handover, or an emergency update that cannot be reproduced. The aim is not to make Composer complicated. The aim is to make it boring, reviewable, and dependable.
Use a safer Composer install habit
If you maintain servers, CI images, or developer onboarding notes, avoid treating root access as the normal Composer workflow. Composer can execute third-party code through plugins and scripts during commands such as install and update, so it should usually run as the project user, not as superuser.
For a user-level install, put Composer somewhere on the user PATH and verify the installer before running it. The checksum changes over time, so do not paste an old hard-coded hash into permanent documentation.
mkdir -p "$HOME/.local/bin"
EXPECTED_CHECKSUM="$(php -r 'copy("https://composer.github.io/installer.sig", "php://stdout");')"
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")"
if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then
echo "Invalid Composer installer checksum" >&2
rm composer-setup.php
exit 1
fi
php composer-setup.php --install-dir="$HOME/.local/bin" --filename=composer
php -r "unlink('composer-setup.php');"If you are evaluating untrusted dependencies, use a container or another isolated environment. Passing --no-plugins --no-scripts can reduce execution during an install or update, but it is not a substitute for isolation when the code itself is not trusted.
Start Drupal projects from the right template
The old drupal-composer/drupal-project:8.x-dev approach should not be the default for modern Drupal 10 or 11 work. New projects should start with Drupal’s recommended Composer template:
composer create-project drupal/recommended-project my_site_nameThat structure keeps composer.json, composer.lock, and vendor outside the public web root, while placing web-accessible files in /web. This is a practical security and operations detail: it makes hosting configuration clearer and reduces the chance that private project files are exposed.
If you need to rename the web root or adjust installer paths before dependencies are installed, start with --no-install, make the intended change in composer.json, then install.
composer create-project --no-install drupal/recommended-project my_site_name
cd my_site_name
composer install| Situation | Composer habit | Why it helps |
|---|---|---|
| New Drupal build | Use drupal/recommended-project |
Keeps public files in /web and dependencies outside the document root. |
| Adding modules | Use composer require |
Updates constraints and the lock file in one reviewable change. |
| Upgrade planning | Run composer outdated and composer audit |
Shows update pressure, advisories, abandoned packages, and policy issues early. |
| Blocked update | Use composer why and composer why-not |
Identifies the package or platform constraint causing the conflict. |
| Drush management | Keep Drush in the project | Prevents global tool versions from drifting away from the site they manage. |
Check versions before adding packages
A small amount of checking before require saves a lot of cleanup later. Drupal modules often have stable releases, development branches, and pre-release versions available at the same time. Before adding a module to a production project, inspect what Composer can actually resolve.
composer show drupal/video_filter --available
composer show drupal/video_filter --allThen add packages through Composer rather than by manually editing files or copying code into the repository.
composer require drupal/pathauto drupal/metatag
composer require --dev drupal/develThat keeps composer.lock honest. It also gives reviewers a clear dependency change instead of a vague bundle of downloaded files.
Make update checks routine
Composer has enough built-in reporting to support a simple operational rhythm. These commands are useful before release planning, before a PHP upgrade, and before a handover to another team.
composer validate
composer outdated
composer outdated 'drupal/*'
composer auditvalidate catches bad or stale Composer state before it reaches CI. outdated shows which installed packages have newer versions available. audit checks installed packages against security advisories and dependency policies, including abandoned or flagged packages. None of these commands fixes the project by itself, but they turn unknown risk into a list the team can plan around.
Debug conflicts instead of guessing
When Composer refuses an update, resist the temptation to loosen random constraints until the command passes. Start by asking Composer why the current package is installed and what blocks the version you want.
composer why -t drupal/file_mdm_exif
composer why-not drush/drush 14.0.0
composer prohibits php 8.3why shows dependency paths. why-not and prohibits show which packages block a target version or platform requirement. This is especially useful when planning PHP upgrades, where the blocker may be a transitive library rather than the Drupal module everyone is looking at.
Keep Drush tied to the project
The older habit of deleting vendor/drush by hand is not a maintainable fix. Current Drush guidance is straightforward: Drupal sites should be Composer-managed, and Drush should be a dependency of the project.
composer require drush/drush
vendor/bin/drush statusUse the major version that matches the Drupal site. As of the current Drush install documentation, Drush 13 is the supported and recommended line for Drupal 10.2+, while Drush 14 is the supported and recommended line for Drupal 11.3+. Pinning the major version is sensible for teams managing several sites, because it avoids surprise tool changes during a release cycle.
composer require drush/drush:^13
# or, for a compatible Drupal 11.3+ project:
composer require drush/drush:^14Composer discipline is not about ceremony. It is about having a build that another developer, agency, or hosting partner can reproduce without private knowledge. If your Drupal estate needs cleaner dependency management, safer upgrades, or a calmer release process, Greg can help put a practical workflow around it.
Related on GrN.dk
- Copy and Paste Images from the Clipboard in Drupal
- Drupal's June security bundle exposes fragile Composer update habits
- Sending Mail with Drupal: Reliable Email Setup for Business Sites
Need help with this kind of work?
Get help with Drupal delivery Get in touch with Greg.