Skip to main content
Deployment Workflow

End-to-End Deployment Workflow Explained

Modern software development requires a clean, predictable, and secure workflow from the moment code is written on a developer’s machine until it reaches production. Whether you work with Drupal, Node.js, Java, Laravel, or any modern framework, the deployment lifecycle remains structurally similar.

This guide explains each environment, how to maintain a robust branching strategy, how to version releases with tags, and how to implement safe rollback mechanisms. By the end, you’ll have a complete blueprint of how code should flow from Local → Development → QA → UAT → Production.

1. Understanding the Different Environments

Let’s break down the purpose and responsibilities of each environment.

1.1 Local Development Environment

Used by: Developers
Purpose:

  • Writing code

  • Running local servers

  • Testing logic before committing

  • Running unit tests

Tools used: Docker, Lando, DDEV, XAMPP, WAMP, Local PHP servers, npm, composer, etc.

Good practices:

  • Local should mimic production as close as possible (Docker-based setups are ideal).

  • Developers must regularly pull the latest code from the repository to avoid conflicts.

1.2 Development (DEV) Environment

Used by: Developers & Tech Leads
Purpose:

  • Integrate code from multiple developers

  • Fix merge issues

  • Test modules/features

  • Validate backend integration

  • Early functional testing

Good practices:

  • Auto-deploy from feature branches or develop branch

  • Turn ON detailed logging

  • Allow debugging tools

1.3 QA Environment

Used by: QA team
Purpose:

  • Full system testing

  • Regression testing

  • Bug verification and bug logging

  • Automated test runs (if available)

Good practices:

  • Deploy code only from a stable branch (typically release/x.x.x)

  • Database should be refreshed periodically from production (after sanitization)

1.4 UAT (User Acceptance Testing) Environment

Used by: Business teams, Product owners, Clients
Purpose:

  • Accept or reject the release candidate

  • Validate that features match requirements

  • Final approval before production

Good practices:

  • UAT should ONLY accept code already tested in QA

1.5 Production Environment

Used by: End users / Public
Purpose:

  • Live platform where real users interact

  • Must be stable, secure, monitored

Good practices:

  • Deployment ONLY from approved release tags

  • Zero downtime deployments

  • Monitoring & alerting (NewRelic, Prometheus, ELK, CloudWatch)

  • Automated backups

  • No direct developer access

  • No experimental code

2. Recommended Git Branching Strategy

A clear branching model ensures that teams avoid conflicts, parallel development stays clean, and releases become predictable. The most widely used models are Gitflow, Trunk-based development, and custom hybrid models.

Below is a simplified and effective Gitflow-based strategy.

2.1 Main branches
1. main or master
  • Always contains production-ready code

  • Only accepts code via pull requests from release/* branches

  • Each commit/version in this branch should be tagged

2. develop
  • The integration branch for developers

  • Contains code ready for QA

  • Accepts merges from multiple feature branches

2.2 Supporting branches
1. Feature branches

Naming: feature/feature-name
Used for:

  • New features

  • Enhancements

  • Major refactoring

Merged into → develop

2. Hotfix branches

Naming: hotfix/issue-123
Purpose:

  • Fix critical production issues quickly

Merged into → main and develop

2. Hotfix branches

Naming: hotfix/issue-123
Purpose:

  • Fix critical production issues quickly

Merged into → main and develop

3. Release branches

Naming: release/1.2.0
Purpose:

  • Prepare code for QA/UAT

  • Stabilize before production

  • Freeze new features

Merged into → main (with tag) and develop.

3. Code Flow: Local → Dev → QA → UAT → Prod

Below is the recommended step-by-step flow.

Step 1: Developer works locally
  1. Create branch from develop:

git checkout develop
git pull
git checkout -b feature/user-login
  1. Write code

  2. Commit changes

  3. Push branch

  4. Create Pull Request (PR) into develop

Step 2: DEV environment deployment
  • Once PR is merged, develop branch is auto-deployed to DEV environment

  • Integration and smoke testing happens

  • If issues → fix in feature branch → merge again

Step 3: Create a release branch for QA

When DEV is stable:

git checkout develop
git checkout -b release/1.0.0
git push
  • QA team tests release

  • Bugs → fixed in release/1.0.0 and merged

  • No new features allowed

Step 4: Deploy to UAT

When QA approves:

  • Same release branch is deployed to UAT

  • Business/Client validates features

  • If approved → mark as “Go Live”

Step 5: Merge into main & tagging

After approval:

git checkout main
git merge release/1.0.0
git tag -a v1.0.0 -m "Production Release 1.0.0"
git push origin main --tags
  • Tagging ensures we can always roll back to this exact version

Step 6: Deploy to production

The tagged version (v1.0.0) is deployed using:

  • Jenkins

  • GitHub Actions

  • GitLab CI

  • Bitbucket Pipelines

  • Acquia / Pantheon pipelines

  • Custom deployment script

Production must ALWAYS be deployed using tags, not branches.

4. Versioning & Tags

Semantic versioning is recommended:

MAJOR.MINOR.PATCH

Examples:

  • 1.0.0 – first production release

  • 1.1.0 – new features

  • 1.1.1 – minor bugfixes

  • 2.0.0 – breaking changes

Every production release must have a tag so rollback becomes easy.

5. Rollback Mechanisms

Something goes wrong after a release? Rollback is your safety net.

5.1 Rollback using Git tags (fastest & safest)
git checkout main
git fetch --tags
git checkout tags/v0.9.0
# deploy this tag
5.2 Database rollback (Drupal / CMS platforms)

Often needed if:

  • Updates applied

  • Schema changes

  • Config changes

Recommended mechanisms:

  • Automated nightly DB backups

  • Pre-deployment DB snapshot

  • Snapshot-based rollback (AWS RDS, MySQL dumps)

5.3 Infrastructure rollback

If using containers:

  • Keep previous Docker images

  • Rolling back is as simple as

    docker pull myapp:v0.9.0
    docker run myapp:v0.9.0

    If using Kubernetes:

    • Use kubectl rollout undo

     

                                                          STEP BY STEP REPRESENTATION

Local
 
Feature Branch
 
Pull Request
 
CI pass? (Yes → Merge to Develop, No → Fix in Feature)
 
Develop (Integration)
 
DEV Environment (Integration Testing)
 
Release Branch
 
QA Testing
 
QA pass? (No → Fix in Release, Yes → UAT)
 
UAT (Client Approval)
 
Merge to Main → Tag Version
 
Production Deploy
 
Prod issue? Create Hotfix from main → CI → Merge&Tag → Deploy → Sync to develop