10ish Factor WordPress for Continuous Engineering
I am currently working on an interesting project: a publishing platform for one of our biggest clients.
As I discussed it before, we divided our team into three –smaller– focused sub-teams: Content Creation, Content Pipeline, and Content Delivery.
Content Creation happens on WordPress. WordPress is exceptional as a publishing platform; widely used in the Media Industry. Authors are very familiar with it.
As with any product we develop, we try to follow good engineering practices. This is our 10-ish factor WordPress setup.
We are adopting the 12 Factor App methodology and approaching WordPress as just another microservice in our final product.
The Code Base
We are using Git for version control (Hosted on GitHub). Our Workflow goes like this:
- We use feature branches. Each story gets its branch. When the code is ready, we create a Pull Request (PR) to have changes merged to develop.
- The team peer-reviews the PR. We suggest changes if needed.
- Before changes get merged, CI/CD runs Code Sniffing to make sure the codebase sticks to WordPress VIP Coding Standards because our clients deploy to VIP Go. It also runs our tests. If everything passes, you can merge your PR.
- When the branch merges back to develop it gets auto-deployed to our development environment on AWS.
- We delete the branch to keep things clean.
Dependencies
We use composer to manage dependencies. Everything gets tracked in our composer.json file, including the WordPress Core and all external plugins. Anything that is not built by the team.
This helps us maintain our codebase leaner and makes sure we all have the same packages installed. Making sure all environments match with our local environments.
We are using WP headlessly, so we do not have themes. If we were, external themes would also be a dependency in composer.
We use WP Packagist. It exposes WP’s plugins and themes as a composer repository.
Configuration
We use Vance Lucas’ dotenv package to get configuration from environment variables.
Everything is generated from environment variables:
- Database configuration.
- WordPress settings.
- AWS settings.
- Folders.
- Endpoints of other services.
- Product keys.
Sharing secrets can become cumbersome quickly as .env files are not under version control. Variables tend to get out of sync easily with time.
To solve this issue, we are currently sharing these secrets between the team and our different deployments with the help of Torus. A centralized, secured, secret sharing platform. I will share our experience in another post.
We have used Vault before for our Bots Platform. We are giving Torus a chance to see if it offers a better Developer Experience.
Backing services
Everything is a remote resource. AWS Aurora hosts our Database. Images live on S3 with the help of Human Made’s Tachyon. All posts are also synced to an external store and can be imported back into WordPress if needed.
Our client might have the final product hosted on VIP Go. When that happens, infrastructure will get abstracted from us.
Build, release, run
The CI/CD pipelines take care of everything.
- Sync our Repo
- Generate static assets with gulp
- Link shared assets
- Make an Atomic Release (that we can easily rollback)
- Install dependencies with composer
- Set our environment from variables
- Update permissions
- Get the word out on slack ??
The only way to propagate changes to any of our environments are Pull Requests.
We also have Ansible Playbooks that build our EC instances. I highly recommend Roots’ Trellis. It gives you a full-blown LEMP stack for WordPress with Vagrant for local development and one command deployment to staging/production.
Processes
This one comes free with PHP. The principle states that your application is one stateless process (in this case WordPress). In PHP each request is handled by a different process.
Depending on your setup, you might want to use Memcache or Redis to handle sessions. Always keep in mind that the next request for a given user might go through another server.
Dev and Production Parity
At this point, everything has been taken care of for us.
With this setup, you can leverage Vagrant or Docker to run your WordPress instance. We use both at the company so the team can use either. Use which ever tickles your fancy.
No more “Works on my machine.”
Logs
We don’t do anything in particular here. We have not had any issues that required us to go thru the logs thus far. I guess everything got caught locally before hitting our servers.
However, depending on the setup your logs can end up on CloudWatch, ElasticSearch or other external resources, check out Monolog for that.
Admin Processes
We use Bash scripts, Phing and the WP CLI heavily to do administrative tasks.
The idea is that all management related tasks (like DB Migrations, Admin scripts or processes) run from the CLI, so all steps are reproduced exactly on all environments.
What about the missing factors?
The missing factors come free with the PHP stack. Port binding is done by Nginx (or Apache’s httpd). They interface with PHP through the FastCGI Process Manager (FPM). Concurrency and Disposability are too abstracted away by the FPM.
In Dockerized environments, you can go as far as having nginx containers load-balance several FPM instances in different containers.
More resources
- The kind folks from 10up have an amazing Engineering Best Practicesguide for WP development.
- WP Packagist has most common WP’s plugins and themes as a composer repository, ready to be used.
- Check out Roots’ Bedrock for a starting point to organize your codebase.
- Root’s theme starter Sage is an excellent starting point to develop a WordPress theme.
- Follow WordPress’ Coding Standards to get a consistent codebase.
- Check out Shifter for a Serverless WordPress as a Service that scales.
- Finally, head to the REST API Handbook and start using WordPress as a headless CMS. Integrating WordPress with other projects has never been simpler.
Special thanks to Rafa Salazar, Fernando Alvarez and David Nú?ez for their help making this article better.
?
I published the original version of this article on Medium.