Netmaking

Managing environments with Composer

Learn how to use Composer to manage dependencies in different environments.

Composer greatly simplifies handling dependencies for PHP applications. In this blog post, we’ll have a look at how to use Composer to manage your dependencies when your different environments call for different versions of the libraries you use.

When is this useful? Imagine that your application depends on a library that you’re developing. Your composer.json might look like this:

Every time you make a change to your library, you merge it into the library’s master branch. Then, in your app, you run composer update your-namespace/your-library, and you have the latest version of your library.

Your development process for a new feature may look like this:

Develop in your development environment (on a feature branch)

Merge your feature branch into master

Deploy to production

A more robust development process

But – as you may know – deploying directly from your development environment to your production environment means that more errors will go unnoticed. You’d like a testing environment – which is as similar as possible to your production environment, but with a separate database – as a step between your development and production environments.

Your new development process contains a few extra steps:

Develop in your development environment (on a feature branch)

Merge your feature branch into your testing branch

Deploy to testing

Someone else on your team and/or on your client’s team tests and approves of the change

Merge your feature branch into master

Deploy to production

This is a more robust process and will – if you’re like me – lead to less fear of failure on deployment to production.

Making changes to your library

But what if your new feature requires making changes to your library? You’d make a new feature branch in your library, and develop it there. But how can you deploy that feature to testing?

You don’t want to merge it into your library’s master branch before it’s ready to be deployed to production. (If you did this, and someone else on your team ran composer update and deployed to production, the new version of your library would end up in production before it was fully tested and approved.)

Another possibility is to – in your app’s testing branch – update the library’s reference in composer.lock to point to the exact commit that you want to use in your testing environment. But this reference may be overwritten, for example if this reference is changed manually at a later point in time, or master is merged into testing.

You then risk that your library’s new feature no longer exists in your testing environment. If this happens before your client and/or team member has tested the new feature, confusion and frustration will ensue.

Composer environment variables

We can avoid this problem using Composer’s environment variables. They let us specify different dependencies for different environments, meaning that your app will depend on a different version of your library depending on the environment.

In your library, you can – just like in your app – have a testing branch. You can merge your changes into this branch, and only when they’ve been tested and approved merge them into your library’s master branch – just like in the app itself.

If this is all a bit confusing at this point, don’t worry. Let me walk you through an example.

Step-by-step

We’ll start in your application’s root and make copies of your Composer-files. In your terminal:

Next, we’ll use the environment variable COMPOSER to let your package manager know that you want it to use a different set of files than default, allowing us to get the latest changes to your library’s testing branch: