The Perfect WordPress Development Workflow

Last Updated October 29, 2016

This week, I’ve had to step back into the dark ages and develop a site on the server. Ugh. I hate it. It feels clunky. I’m paranoid I’m going to overwrite the wrong file. If I mess something up, I’m scrambling.

I’ve developed (what I consider) a pretty perfect workflow. I’ll break down my workflow, step by step, giving you the tools you need to set things up, but first, I want to give you a high level, 10,000 foot view of what this workflow looks like.

The Five Minute WordPress Install


My Local Environment



MAMP is a great tool for turning your computer into a server so that you can develop locally. If you’re doing “simple stuff”, like writing HTML and CSS, you might not need a server, but anytime you start referencing remote files (like TypeKit) or working with PHP and databases (ahem, WordPress), you’ll need a local server.

If you’re using a Mac, PHP and MySQL come pre-installed. But, you still have to go through motions of configuring everything. MAMP handles all that for you. It’s a simple install. Plus, if you purchase the pro version of MAMP, then you can create unique URLs for each of the sites you’re working on. This site, for example, is

I’ve written an in depth post on working using MAMP that should help you get started.



Yeoman is a tool for setting up coding environments quickly. Most hardcore developers probably associate it with scaffolding a large, web applications. But, Wesley Todd released a generator called YeoPress that can help you get a WordPress install up and running quickly.

I like it because (1) it sets up my project, but (2) it always installs the most recent version of WordPress.

I’ve written a post about working with Yeoman.


I use composer to install WordPress plugins. Composer isn’t that complicated. It’s a package manager. I tell it which WordPress plugins I want to use and it goes and grabs everything. Before, I was keeping local copies of the plugins on my computer and then copying and pasting. This works, except I have to make sure those plugins stay up to date. Composer does that for me, plus if I’m collaborating with someone else on a project, it will lock both of us into the same version of the plugin, ensuring our code matches.

I’ve also written a post about how I use Composer..



Gulp is a task runner. It handles things like:

  • Converting a folder of SVGs into a sprite
  • Concatenating all my JavaScript files together into a single file.
  • Minifying my JavaScript file
  • Compressing and optimizing all my image files
  • Compiling my Scss
  • Handling BrowserSync
  • Cleaning up folders
  • Pretty awesome, right? I thought so.



Remember how I mentioned composer is a package manager? Well, Bower is a package manager too. Composer handles PHP packages (a.k.a. your plugins). Bower handles your frontend packages. — All CSS grids and frameworks. JavaScript libraries. I tell Bower which packages I want to use and it goes and grabs all the files for me. I can tell it to grab the most recent version or a specific version of code. Updating everything is as simple as running one line of code.


Some people have foregone Bower for npm. If you’re running gulp (or grunt), you’re running npm. Pulling that functionality into Node, since it has that capability, is one less tool. — However, personally, I like keeping the front-end and backend separate. For now, I’ll be sticking to Bower.

Now what? How do you get the site from your computer to the server?

Initial Server Setup

.git and GitHub / BitBucket

You may or may not have heard of git, but chances are even if you haven’t heard of it, you’ve heard of GitHub. GitHub is a site where people share and store their code online. It sounds simplistic, but the beauty is in workflow and collaboration.

Git is a version control system (VCS) that allows you to work on code with other developers simultaneously, without the fear of overwriting each other’s work. It also allows you to have an unlimited number of “undos” — so you can can revert to any point of your code, at any time.

If you’re working with other people, GitHub is just a place to store your code, providing everyone access. If you’ve heard of Bitbucket() or Beanstalk, they’re comparable services.

What gets included in the repository?

There are a couple of different ways that you can set up your repository:

  1. Just the WordPress theme
  2. The whole sha-bang — WordPress core, theme, plugins, etc. (minus uploads)

Personally, I prefer to include everything. I’ll talk about it in a minute, but basically, the entire repository gets pushed to the server, so any files that I want on the server, should be included in the repository. — including my wp-config.php file.

Now, I know some people may just scoffed, shook their heads, and disagreed with me. That’s fine. In programming, there’s usually more than one way to do something. I include my wp-config.php file in the repository. Why is this potentially bad advice? Well, it means that the database information is available to anyone that has access to the repository. — This isn’t a bad thing, as long as you’re aware of what you’re making available. All of my project repositories are private, so it’s not a problem. Also, another word of caution, if you include your wp-config.php in your repository, it’s a private repo, no problem. But, then one day, you decide you want to make the project open source, so you remove the wp-config.php file and make the repo public. That repo’s history is always available. Someone, could potentially go back and in time and still look at your wp-config.php file. You’ll either need to find a way to remove tracking on the wp-config.php or change the credentials so that information is no longer applicable.

Now that we have some context…

You did notice I’m including the WordPress core within my git repository? Normally, I would say, “that’s bad practice,” you’re including code that you don’t manage. –Well, remember how I talked about setting the site up with Yeoman? Yeoman actually sets up WordPress as a submodule. You can think of it as a repository inside a repository. It’s referencing the official repository. — Same concept for using composer to manage all my plugins. So, in theory, all the code in my repository is mine.

GitHub isn’t just for teams

Even though I typically code projects by myself, I still use .git. The “unlimited undos” has saved my butt on several occasions. But, even more than that, I have it set up so that anytime I “push” code to GitHub, my revised files automatically get pushed to the server, too.

In the “olden” days, I would make an edit to one or two files locally. Then, I would pull up Transmit (my FTP client of choice), try to remember which files were changed, drag those files over to the server, and wait for them to upload. Now, that process is seamless. Save, commit, push. Done.

How does that set up happen?

Well, I use Forge to set up the server on Digital Ocean (my hosting platform of choice). I can connect my GitHub repository to Forge and it does all the heavy lifting for me, watching my code and pushing updates.

I’ve written about that too.


There are a couple of Snaffus when working with Forge and Digital Ocean. — Digital Ocean is so cost effective! You have complete control over the server, but it also means you have to set everything up yourself. It also means when something goes wrong, there’s not a support team to handle your requests (like MediaTemple, SiteGround, BlueHost, etc). With that said, I’ve never run into an issue that a quick Google didn’t solve.

Out of the box, Digital Ocean doesn’t handle email. I’ve found Google Apps to be the easiest (for both me and my clients) for client email. Any messages I want the server to send out I run through MailGun. Also, very easy to set up.

What if I can’t use Digital Ocean and Forge?

It’s OK. I still work on client sites that bring their own hosting solutions. So, Digital Ocean and Forge are a no go. — No big. For those projects, I use a service called DeployBot.

The great thing about DeployBot is that it can use normal, Plain Jane FTP — meaning you can use it on just about every project under the sun. You connect DeployBot to your repository, give it your FTP credentials, and it handles the rest. You can set up multiple environments and multiple servers, which is great if you have staging and production servers.

Notifications through Slack

Regardless of whether I’m using Forge or Laravel, in both cases, I connect these services to Slack and receive notifications when these processes are done posting code. As I’m working it’s nice receiving these notices and knowing that everything went through correctly. Nobody sees these messages, except for me.


If you’re not familiar with Slack, it’s a messaging application that makes it easy to quickly communicate with other people. The beauty of Slack is that you can create bots or hook into other services that allow you to run tasks or perform notifications.

Handling the Database

This is all fine and dandy, but with WordPress, we’re still working with a database. How do you handle that?

There’s a great plugin called WP Migrate DB Pro, a long name, but a great product. Each year, I pay for the developer version because it’s a plugin I include on all the sites I develop. From this plugin, I can:

  • Export a database
  • Replace my database with another
  • Replace another database with mine
  • This makes it really easy to sync.

With the developer license, you also have access to the Media Files Add on, which allows you to sync Media Files (uploads directory). — Remember, we’re not including that folder in our repository.

The Conversation

  • Ricky

    Excellent write up! Are you still using Bower in this workflow, or have you moved on to Yarn or Webpack?

    • Amy Dutton

      @disqus_NWxXRxW5gL:disqus Thanks! I’m still using Bower. I know I’ll probably receive some judgement for that 😉 But, I have several reasons: (1) I like the idea of keeping my frontend packages separate from my node modules. (2) With WordPress, I’m not running complicated JavaScript code. If there’s anything page specific, I just conditionally enqueue that file through functions.php

      • Ricky

        Good to know. I’ve yet to make the transition myself in some of my projects. Also, no judgement here. As you say in many of your posts, there’s usually no one correct method in web development.

        • Amy Dutton

          No one correct way, but there are plenty of opinionated ways. ?