logo

Netmaking

22 71 95 97

Yngve Høiseth

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:

// composer.json
{
    ...
    "require": {
        ...
        "your-namespace/your-library": "dev-master"
    },
    ...
}

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:

cp composer.json composer-testing.json && cp composer.lock composer-testing.lock

Now, we’re ready to change your composer-testing.json to require your library’s testing branch:

// composer-testing.json
{
    ...
    "require": {
        ...
        "your-namespace/your-library": "dev-testing"
    },
    ...
}

Notice how it now says dev-testing rather than dev-master.

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:

COMPOSER=composer-testing.json composer update your-namespace/your-library

Assuming that your library’s testing branch contains one commit that your library’s master branch lacks, the output should look approximately like this:

Loading composer repositories with package information
Updating dependencies (including require-dev)                                                       
  - Updating your-namespace/your-library (dev-master a06d6e2 => dev-testing 73dba82)
    Checking out 73dba825423b30c39825e9d2575ee3a74494af75

Writing lock file
…

Using Capistrano

If you use Capistrano to automate your deployments, you can use the following line to set the appropriate environment in your testing deployment script:

# deployment/stages/testing.rb
...
set :default_env, { COMPOSER: "composer-testing.json" }

Summary

By using Composer’s COMPOSER environment variable, you have a robust way of varying your dependencies between your different environments, such as testing and production.

Yngve Høiseth

Yngve Høiseth

Yngve har vært innom både befalsskole og bank før han landet hos oss. I tillegg til webutvikling er Yngve opptatt av hvordan ting kan gjøres bedre, på alle mulige måter, noe som er bra for både oss og våre kunder. Yngve har også en ullgenser.