Early in my career, like many developers, I edited live WordPress sites directly. A change to a theme file over FTP, a quick save, refresh the browser and hope. Most of the time it worked. Occasionally a missing semicolon took down a client’s site while I scrambled to find the error.
Those moments teach you quickly that a proper workflow isn’t a luxury. It’s what lets you make changes confidently, undo mistakes in seconds and collaborate with others without overwriting each other’s work. It’s also what clients are paying for when they hire a professional, whether they realise it or not.
This guide explains the workflow I use for WordPress projects: local development, staging, Git and safe deployments. You can adopt it gradually. Even the first step makes a real difference.
The three environments
A professional setup usually has three versions of the site:
- Local, running on your own computer, where you write and test code.
- Staging, a private copy on a server that closely matches production, where changes are reviewed and tested.
- Production, the live site that visitors use.
Code flows in one direction: local to staging to production. Content, which editors create on the live site, flows the other way when you need fresh data for testing.
Keeping this direction straight is the most important principle in WordPress workflows. Breaking it is how content gets accidentally overwritten.
Setting up local development
Local development means running WordPress on your own machine. You can work offline, make changes instantly and break things without affecting anyone.
There are several options:
- Desktop apps designed for WordPress that create local sites in a few clicks.
- Docker-based setups, which give you more control and closely match server environments.
- WordPress’s own tools, such as wp-env, for plugin and theme development.
Whichever you choose, try to match your production environment, especially the PHP version and database version. A plugin that works on your laptop’s newer PHP version might fail on a server running an older one.
What belongs in Git
Git is a version control system. It records every change to your code, who made it and why. You can compare versions, undo changes and work on features in separate branches.
The first question with WordPress is what to put in the repository. A WordPress site contains several kinds of files, and they don’t all belong in Git.
Usually commit:
- Your custom theme or child theme.
- Your custom plugins and must-use plugins.
- Configuration files that don’t contain secrets.
Usually don’t commit:
- WordPress core files, which can be installed and updated separately.
- The uploads folder, which contains media created by editors.
- wp-config.php with real credentials.
- Cache files, logs and backups.
Third-party plugins are a judgment call. Some teams commit them so the repository represents the complete site. Others manage them with Composer, a PHP dependency manager, which lists plugins and their versions in a file. Either works, as long as the team is consistent.
A typical .gitignore for a repository rooted at wp-content looks something like this:
Adjust it to match how your project is structured.
Keep secrets out of the repository
Database passwords, API keys and security salts must never be committed to Git. Once a secret is in a repository’s history, it’s there permanently unless you take special steps to remove it. If the repository is ever shared or leaked, those secrets are exposed.
Instead, keep secrets in environment-specific configuration. A common pattern is to load values from environment variables or a file excluded from Git:
The WP_ENVIRONMENT_TYPE constant is useful. You can set it to local, development, staging or production, and plugins and your own code can behave differently in each. For example, you can disable outgoing emails or payment gateways on staging.
A simple branching approach
You don’t need a complex branching strategy for most WordPress projects. A simple approach works well:
- The main branch represents what’s on production.
- Create a branch for each feature or fix.
- Merge into a staging branch, or deploy the feature branch to staging, for testing.
- Once approved, merge into main and deploy to production.
Write clear commit messages explaining what changed and why. Six months later, “fix” tells you nothing. “Fix checkout button overlap on small screens” tells you exactly what happened.
Staging done right
A staging site should be as close to production as possible: same PHP version, same server configuration, same plugins and a recent copy of the content.
A few rules I follow:
- Protect it. Use password protection so search engines and the public can’t see it. A noindex setting alone isn’t enough, since it can be accidentally copied to production.
- Disable outgoing emails. You don’t want customers receiving test order emails from staging. Use a plugin or configuration that captures emails instead of sending them.
- Disable live payments and integrations. Switch payment gateways to test mode and disconnect live integrations like CRM syncing.
- Refresh content regularly. Copy the production database to staging when you need realistic data, then replace URLs with WP-CLI.
Deploying code
Deployment is moving tested code from the repository to the server. There are a few common approaches, from simple to sophisticated.
Manual deployment with Git on the server
The server has a copy of the repository, and you pull changes:
It’s simple, but it requires server access and someone has to remember to do it.
Rsync-based deployment
Code is built locally or in a pipeline and copied to the server with rsync, transferring only changed files. This works well when your theme has a build step for CSS and JavaScript, because you can deploy the built files without putting build tools on the server.
Automated deployment with CI/CD
A continuous integration service, such as GitHub Actions, runs automatically when you push to a branch. It can install dependencies, build assets, run tests and deploy to the server. A simplified example:
In a real setup, you’d store the server’s SSH key as a secret and add steps to clear caches after deployment. Once set up, deployments become a merge and a few minutes’ wait, with a full record of what was deployed and when.
The database problem
Code is easy to version and deploy. The database isn’t. WordPress stores content, settings and much configuration in the database, and editors are changing it constantly on production.
This leads to the golden rule: never push a staging or local database to production on a live site. You’ll overwrite orders, posts, comments and user changes made since you copied it. I’ve helped clients recover from exactly this mistake more than once.
Instead:
- Move code through Git and deployments.
- Make settings changes on production directly, carefully, or through code where possible.
- For structural changes, such as new custom post types or options, write code that creates them, so they deploy with the rest of your changes.
- For content migrations, use scripts or WP-CLI commands that can be run on production safely.
Updating plugins and core in this workflow
Updates follow the same path as your code:
- Update plugins and core on local or staging.
- Test key functionality.
- Deploy, or apply the same updates on production.
If you manage plugins through Composer or commit them to Git, updates become commits you can review and roll back. If not, at least test updates on staging before applying them to production.
Rolling back
One of the biggest benefits of this workflow is easy rollback. If a deployment causes problems, you can revert the commit and redeploy, or check out the previous version on the server. Combined with database backups taken before each deployment, you can recover from most mistakes in minutes.
Frequently asked questions
Is this workflow overkill for a small site?
Not entirely. Even small sites benefit from Git and a staging site, especially if they have custom code. You can skip automated deployment and keep things simple. The point is never editing live code directly.
Can editors use this workflow?
Editors keep working on the production site as normal. The workflow is for code: themes, plugins and configuration. Content stays in the live database.
What about page builder layouts stored in the database?
That’s the trickiest area. Layouts created in page builders or the Site Editor live in the database, not files. For important templates, export them to files where possible, so they can be versioned. Otherwise, make layout changes on production carefully, with backups.
Do I need Composer?
No, but it’s helpful for larger projects. It makes plugin versions explicit and reproducible. For small projects, committing custom code and managing third-party plugins on each environment is fine.
Final thoughts
A good WordPress workflow comes down to a few principles: develop locally, test on staging, track every code change in Git, keep secrets out of the repository, deploy in a repeatable way and never push old databases over live content. You don’t have to adopt it all at once. Start with Git and a staging site, then add automation as your projects grow. Your future self, and your clients, will thank you the first time a change needs to be undone.




