Composer, Yarn, and other web development package managers create a .lock file. Many people assume it is a filesystem semaphore or meant to be temporary and end up removing it from commits. However, its purpose is to indicate exactly which packages and versions the project is currently built for and is meant to be committed to version control so other servers (development, staging, or production) get the same package versions. This benefits a few scenarios:
Developing the project together from the start
If a group of developers are working on the same project, it is best for them to work with the same versions of vendor packages. Once a developer clones from a repository, they can bootstrap the project by running composer install, which will install the exact versions which the composer.lock file lists.
A common misconception is that the developer should bootstrap with composer update. However, this will install the latest versions of packages which composer.json indicates in its version restraints. Sounds good in theory, but I’ll detail this further on.
Coming into a project that has existed for some time
In this scenario, the project was finished some months ago and receives (or not) regular updates from the existing developers. A new developer comes in to add some functionality. Without the .lock file, they will end up with updated packages which could break compatibility.
The new developer is not meant to be updating and solving any dependencies and compatibilities throughout the project, but to implement functionality to what’s already there. The task of installing updates is a separate maintenance task which can be quite the rabbit hole for this new developer, depending on how the existing system has been built.
Deploying to production
Composer update, or composer install without a .lock file causes Composer to resolve dependencies, which is a VAST use of system memory. In most production environments, this will exceed the memory limit. With the .lock file present, Composer needs to merely fetch the packages (and dependencies) listed in the .lock file and populate the vendor/ directory.
Without the .lock file, and without being able to run Composer, we would need to FTP, or SCP the vendor directory to production which is an unnecessary complication. Some deployment routines will have a post-processing command which will run composer install - this is broken if we need to FTP into the server as a separate step to pushing a release via git.
composer.json vs composer.lock
The composer.json isn't suitable for building the vendor directory reliably. What would happen is:
- The project is created, for example, with composer create-project symfony/web-skeleton. The latest stable Symfony is installed, and the entry in composer.json would be, say, ^3.4. The version installed (for sake of example) is specifically 3.4.2.
- Months down the line, another developer gets involved and runs composer install (without the lock file) or composer update. They end up with symphony 3.4.4.
You can tighten the constraints to force version 3.4.2 in this case, but that’s the exact purpose of the lock file. The ^3.4 constraint is sensible as it allows the developers to run composer update at a suitable time, and fix any deprecations and compatibility issues (which to be fair shouldn’t exist thanks to the caret (^) restriction, but this isn’t always the case) rather than being lumped with that problem when the goal is to release a feature by Friday afternoon.
A more complex example is that - restraining versions too tightly in composer.json means important updates to some libraries will never make it into the project, as composer update is being told not to install and resolve later versions.
In short, you want to use:
composer install on the development server to install any vendor packages and be on the exact same page as other developers on the project.
composer install [—no-dev] on the production server to install the exact packages which the development environment has been running (and tested on).
composer update on the development server to update any vendor packages, and fix any compatibility issues.
…and of course, commit the .lock files to version control.