Skip to content

Composer: managing dependencies in PHP projects

Every serious PHP project uses third-party code. Composer ensures that exactly the same versions run on the development machine, in testing and on the server — provided it is used correctly.

  • Who it is forDevelopers, technically interested readers
  • LevelIntermediate
  • Reading timeabout 7 minutes
  • Last updated16 October 2026

What you will learn

  • What Composer does and what it expressly does not do
  • The difference between composer.json and composer.lock
  • Why install and update are completely different commands
  • What to bear in mind in operation

Two files, two jobs

`composer.json` describes what you want: which packages and within what range. `composer.lock` records what came out of it: each package with exactly one version and a checksum.

JSONcomposer.json, excerpt
{ "require": { "php": ">=8.2", "monolog/monolog": "^3.5" }, "require-dev": { "phpunit/phpunit": "^11.0" } }
The ^ character allows updates up to the next major version: ^3.5 accepts 3.6 and 3.9, but not 4.0. For versions below 1.0 it is stricter: ^0.3 allows 0.3.x, but not 0.4.: ^3.5 accepts 3.6 and 3.9, but not 4.0.

For an application, both files belong in version control — the lock file too, especially that one. For libraries that others include as a package, it is often left out, because it has no effect for their users. It is the reason a colleague and the server get the same state as you.

You only confuse install and update once

  • `composer install` reads the lock file and installs exactly the versions recorded there. This is the command for colleagues, for the test environment and for the server.
  • `composer update` looks for new versions within the permitted ranges, installs them and rewrites the lock file. This is a deliberate decision that someone has to review.

A `composer update` on the server is an operation whose result nobody has seen beforehand. The same applies to `composer update` shortly before a release.

An example from practice

When the installation and the lock file diverge

A development tree in which `composer install` was never run after the lock file changed carries different package versions from those the project intends. The result is a pattern of errors that misleads: tests fail in bulk in an area nobody has touched.

That is why an automatic check is worthwhile, one that runs with every test run and reports exactly this — a comparison of the installed packages against the lock file. A cluster of errors in an area nobody has touched is a typical sign of deviating package versions and should therefore be checked against the lock file first; other causes are not ruled out by this.

What matters in operation

  • On the server, `composer install` belongs in the deployment process, not `update`. The `--no-dev` switch leaves out development packages. That is right on a production system — but only if the application really needs none of them there. `composer dump-autoload -o` generates a classmap and speeds up start-up; with `-a` (classmap-authoritative) only this list is used, and a class that is not in it is no longer found. A class whose path does not match the mapping is then missing. `composer audit` reports known security vulnerabilities in the installed packages. The command belongs in regular maintenance.

Common misconceptions

Yes, you do. Without it, everyone involved gets some state within the permitted range, and "it works on my machine" becomes the rule.

No. It only checks whether the required ones are present, and aborts otherwise. They are installed by the system.

Updating is important, but as a separate, reviewed step — not on the side during deployment.

Further sources

About the author

Fabian Lücker is managing partner of PHP-Schmiede KG in Rheine and has worked in software development since 2007. He is responsible for development and design and writes here about decisions that arise in real projects.