From the development department: Migrating to Composer at Mollie
At Mollie, we recently switched to Composer to manage our applications’ dependencies. Composer is an application-level package manager for the PHP programming language that provides a standard format for managing dependencies of PHP software and required libraries. Using Composer is considered a best-practice in PHP application development.
Before our switch to Composer, we managed dependencies by creating local copies of their repositories and including them in our application using Git submodules. Disadvantages of this approach were that it took a lot of effort to add a new dependency and that it was very hard to update the dependencies. In practice, this resulted in our developers creating their own solutions instead of using high quality open source packages and us quickly getting behind on whatever package we did manage to add.
We decided to follow Composer best practices. The required dependencies are configured in the file
composer.json, which is managed by the Composer binary. The actual versions of dependencies installed in the application are stored in the
composer.lock file, which is for that reason under version control. This way, when someone runs
composer install, the installed dependency’s versions are always predictable. The dependencies themselves are stored in
vendor/ and are not under version control. This is the recommended way to use Composer.
During deploys, after copying the application code to the web server, each web server will run Composer to install the non development dependencies. Composer uses a local cache so if the dependencies are unchanged as compared since the previous deploy, no network traffic is initiated.
For local development and during integration similar dependency installing steps are in place.
We’ve configured a GitHub OAuth token on our servers, so that we don’t run into rate limits when building or deploying our application.
Most of the migration went rather smooth. Changes consisted mostly of removing existing autoloaders, require and include statements, and removing git submodules. We created some
Make targets to help developers smoothly transition from Git submodules to Composer installed dependencies: git does not remove existing submodule folders if you remove the submodule from VCS.
Not all our dependencies followed semantic versioning equally well. We encountered issues with changed behaviour in Smarty when merging compiled templates. This required us to pick a Smarty version a few minor versions down from the latest version. All in all, changing Smarty versions using Composer was a piece of cake.
We also ran into trouble with several Zend Framework 1 components. ZF1 is a dependency we picked a long time ago and has not aged so well. Separate packages for each component are available via Composer. However, some did not declare all of their dependencies properly. Another issue was that some had calls to
Zend_Loader to load extra classes, which doesn’t work. Our solution was to force Composer to load these extra classes using
class_exists(), so that the
Zend_Loader calls could be skipped. We’ve opened some pull requests with ZF1 to resolve these issues.
A final issue that we encountered was that, with the default Composer settings, new packages would be added at the bottom of the list in the
composer.json file. This would cause lots of merge conflicts when different teams would add dependencies in their branches. We enabled the sort-packages setting and this worked much better.
Composer downloads code from the internet and adds it to our applications, which execute the code. Generally, downloading code from the internet and running it is considered a bad idea™, so we had to come up with some measures to mitigate this risk. The biggest risk is that the package is replaced by a different (evil) package, during transit or by replacing the package on Packagist.
In its default configuration, Composer only uses the HTTPS protocol and (finally) with the launch of PHP 5.6 all HTTPS streams verify the certificates by default, so the risk of mitm replacement of the packages is low. Composer uses a lock file to lock a dependency to a certain version, so no unexpected updates of packages will be installed. The team behind Composer has improved Composer’s security a lot over the years and with the release of v1.0.0 three months ago we now feel confident that Composer won’t do anything unexpected in this regard.
Another risk is the presence of vulnerabilities or security issues in packages. To protect against this risk, we’ve added roave/security-advisories as a dependency. This package does not contain any code but marks itself incompatible with known vulnerable packages. Furthermore, we have integrated the SensioLabs Security Advisories Checker (which could conveniently be installed through Composer) so that during every build in our CI process all dependencies are checked against known vulnerabilities.
Our installed dependencies are easier to keep up to date, reducing security risks. Developers are happy with the convenience of Composer over Git submodules. Thanks to the SensioLabs Security Advisories Checker, we have good insight on the known vulnerabilities in our dependencies. Our deployment process has been improved because we have replaced slow
git pull operations with fast Composer installs, cutting deploy time. Already several high quality open source packages have been added to our applications, reducing development effort for new features.
Overall, we have improved security, faster deploys and higher velocity when developing new software thanks to Composer.