Composer without the myths: keeping PHP dependencies sane
Most dependency pain is self-inflicted
Composer is not complicated. What gets teams into trouble is a handful of habits that feel harmless until the day they are not. This is the routine I use on every project.
The lock file belongs in git
composer.lock is not a build artefact. It is the record of exactly which versions your application was tested against. Commit it, and never let a server generate it. If two developers run different versions of the same library, you do not have a reproducible build, you have a coin toss.
Install on servers, update on your machine
This is the rule that prevents most surprises.
# on the server, in deployment
composer install --no-dev --optimize-autoloader
# on your machine, when you mean to move versions
composer update
composer install reads the lock file and gives you precisely what is in it. composer update goes looking for newer versions and rewrites the lock file. Running update on a server means every deployment is a small unplanned upgrade.
Read the constraints you write
The caret and the tilde look similar and behave differently.
^1.4means any version from 1.4 up to, but not including, 2.0. Breaking changes are allowed to reach you only when you cross the major boundary.~1.4means 1.4 and patch releases of 1.4 only. Tighter, and often what you want for a library you do not want to think about.1.4.*means patches only, which is the tightest of the three.
Most of the time ^ is correct. The mistake is not choosing. Leaving a wildcard wherever you were too busy to decide is how you end up with a support ticket on a Tuesday.
Update in small steps, on purpose
Updating twenty packages at once and then debugging is the slow way. Update one group, run your tests, commit. If something breaks, you know exactly which group did it. The weekly small update is far cheaper than the annual emergency.
Pin your platform
If production runs PHP 8.3, put that in the composer file. Otherwise your machine, which runs something newer, will happily resolve a package that production cannot install.
"config": {
"platform": {
"php": "8.3.0"
}
}
Run the audit
composer audit checks your dependencies against known vulnerability databases and tells you which ones have advisories. It is one command, it takes seconds and almost nobody runs it. Put it in your routine, or in CI, and you find out about a vulnerable package from a report instead of from an incident.
The boring conclusion
Dependencies should be dull. Commit the lock file, install on servers, update deliberately in small batches, pin your PHP version and run the audit once a month. That is the whole routine. Everything else is preference.
Need a hand with a WordPress site that keeps breaking? See what I do and how I work.