Managing WordPress Projects with Version Control and Composer - Part 1 cover image

Managing WordPress Projects with Version Control and Composer - Part 1

Posted on August 9, 2019

For most freelancers and agencies, version control is a staple of good programming practices. Still, some may not be aware of the benefits of version control, so here's a simple list:

  1. It allows checkpoints in your code through "commits".
  2. It offers a stable and predictable way of collaborating.
  3. It isolates features and detours through the use of "branches".

However, using version control on a client's website can be tedious, fragmented, and time-consuming. For the majority of WordPress projects, the level of version control (VC) usually starts and ends with the project theme (if VC is used at all).

The Partially-Version-Controlled Problem

Partially tracking a web project introduces a whole lot of problems, however:

  1. Plugins and themes are disconnected from a central source.
  2. There is no reliable way to sync a local development server and a production server, introducing possible bugs through false assumptions.
  3. Deployments and updates have to be managed through separate interfaces.
  4. It becomes harder to accurately introduce Continuous Integration and Deployment to the project.

A big reason I've heard as to why a projects don't get fully tracked is that developers don't want to setup multiple repositories for all the assets. Another reason is that if the entire project is version controlled, it makes the repository heavy and bloated, and updating plugins usually means commits with hundreds of file changes.

To be fair, these are valid reasons to avoid version controlling an entire project. It can get confusing really fast, especially if you have to create several repositories for each dependency.

Confusing Math

How to Version Control Entire WordPress Projects

Thankfully, there is a system for streamlining an entire WordPress project in a central repository. How is this possible? Let's look at a couple concepts, below.

For those interested in a big-picture view of what a repository like this might look like, I created a boilerplate for you to use on GitHub. Just fork it and make it your own!

Composer to the Rescue

Remember those two reasons for not tracking an entire project? Let's recap:

  1. Don't want to create new repositories for each dependency (i.e. plugins and parent themes).
  2. Version tracking the entire WordPress project would create a repository nightmare.

Well, thankfully Composer solves both of these in one fell swoop. By tracking dependencies in a config file (usually labeled composer.json), developers can install any number of packages in a single command:

composer install

This let's you set a rule in your .gitignore file to ignore all those plugins and parent themes, while still being able to track them from a single file!.

When a developer wants to work on the project locally (or you want to set it up on a new server), all one needs to do is run the install command from Composer. To give you an idea, adding a plugin to your Composer file looks like this:

    "repositories": [
            "type": "composer",
            "url": ""
    "require": {
        "wpackagist-plugin/akismet": "4.1.2"

The first block tells Composer to search WordPress Packagist for the dependencies, which is a Composer clone of the WordPress plugin repository.

The second block tells Composer to install the Akismet plugin at version 4.1.2. For a more in-depth article on using Composer with WordPress, check out this Smashing Magazine article.

Defining a Project Root

For all of this to work properly, however, you need to set up a project root. Since we're tracking plugins and themes (and Must-Use plugins, which I'll talk about in the next post), our repository will track the entire contents of the wp-content folder.

In our .gitignore file, we'll make sure to ignore dynamic folders and things not easily tracked via Composer, like:


From there, we'll need to tell Composer where to install our dependencies. Here's how I define that from the boilerplate:

    "extra": {
        "installer-paths": {
            "mu-plugins/{$name}/": [
            "plugins/{$name}/": [
            "themes/{$name}/": [

This tells Composer to install mu-plugins, plugins, and themes in their respective directories.

Keep in mind that these are for WordPress specific Composer packages, and WP Packagist specifically defines each plugin with a repository type, such as a plugin.

So, for clarity, the package "wpackagist-plugin/akismet" would be defined as having a type of wordpress-plugin. This is done manually via the Composer package author, and isn't something you'll need to know unless you want to develop your own packages.

Still, it's helpful to know why/how it all works together.


In this project template, we are doing several things:

  1. Tracking the entire WordPress project in a single repository.
  2. Tracking dependencies like plugins and parent themes via Composer.
  3. Ignoring dynamic files and folders.
  4. Tracking only the things we need (i.e. the project's active custom theme).

Next, I'll explain the benefits of wrapping all the project logic (that shouldn't live in the theme), in a central Must-Use plugin. If you want a head-start, you can see the default template here.

Also, much of this I learned from people much smarter than me. To give credit where credit is due, make sure to follow Aaron Holbrook and Philip Newcomer, as both are amazingly good programmers.

Start the Conversation on Twitter

Filed Under: