DEV Community

Software Solutions
Software Solutions

Posted on

The Practical Git Workflow We Use for Client Projects

Managing source control for internal products is relatively straightforward. But when delivering client projects with fixed timelines, strict acceptance criteria, and multiple stakeholders, bad Git habits can quickly lead to merge hell, broken deployments, or lost features.

Over years of building custom web applications and SaaS platforms, we’ve refined a pragmatic hybrid Git workflow that combines the structure of Git Flow with the speed of Trunk-Based Development.

Here is the exact Git workflow and branching strategy we use to keep client codebases clean, stable, and production-ready at all times.


1. The Core Branch Architecture

We keep our long-lived branches down to three primary tiers:

[main]         ----> Production-ready code (Live Client Environment)
│
[staging]      ----> Client QA / UAT Environment (Client Preview)
│
[feature/*]    ----> Active Developer Tasks & Feature Builds

Enter fullscreen mode Exit fullscreen mode
  • main: Represents code currently running in production. Direct commits are strictly blocked.
  • staging: Serves as the user acceptance testing (UAT) environment where clients review and approve completed features.
  • feature/*: Short-lived branches created for specific user stories, bug fixes, or enhancements.

2. Naming Conventions That Keep Work Clear

To maintain clarity across team members and project management boards, feature branches follow strict naming patterns:

  • Feature: feature/ticket-number-brief-description (e.g., feature/PROJ-102-stripe-webhook)
  • Bug Fix: bugfix/ticket-number-brief-description (e.g., bugfix/PROJ-204-cart-total-tax)
  • Hotfix: hotfix/v1.2.1-critical-auth-patch

Clear naming makes it instantly obvious what code is being altered, which task board ticket it belongs to, and who owns it.


3. The Feature Development Lifecycle

Step 1: Branch Off staging

All new work stems from the latest staging code:

git checkout staging
git pull origin staging
git checkout -b feature/PROJ-102-stripe-webhook

Enter fullscreen mode Exit fullscreen mode

Step 2: Local Commits & Clean Atomic History

We encourage frequent, atomic commits locally. Instead of generic commit messages like "updated code", developers follow Conventional Commits:

git commit -m "feat(billing): implement stripe webhook signature verification"
git commit -m "test(billing): add unit test cases for invoice payment success"

Enter fullscreen mode Exit fullscreen mode

Step 3: Rebase Before Submitting a PR

Before opening a Pull Request (PR), developers must rebase their feature branch onto the latest staging to catch conflicts locally before CI/CD runs:

git fetch origin
git rebase origin/staging

Enter fullscreen mode Exit fullscreen mode
  1. Pull Requests & Code Review Guardrails

No developer pushes directly to staging or main. All changes must pass through a Pull Request with automated and manual checks:

    • Automated CI Checks: Linting, static analysis (e.g., PHPStan/ESLint), and automated unit tests must pass.
    • Peer Review: At least one senior developer must approve the logic, performance impact, and security posture.
    • Squash and Merge: When merging into staging, we use Squash Merging to maintain a clean, readable linear history.
# Clean history in staging after squash merge:
* a91f3c2 feat(billing): implement stripe webhooks (#102)
* 4b22e1a feat(auth): add OAuth2 provider support (#98)

Enter fullscreen mode Exit fullscreen mode
  1. Client Review & Promoting to Production

Once features land in staging, our automated pipeline deploys the build to the staging server for client testing.

Handling Client Approval:

  • Approved Features: Included in the upcoming release tag.
  • Rejected/Revised Features: Addressed on a new feature/ branch or patch without affecting the main release line.

Promoting to main (Production Release)

When the release milestone is approved by the client:

  1. A Pull Request is created from staging to main.
  2. A Semantic Version tag is applied (e.g., v1.4.0).
  3. Automated deployment triggers to the client's production environment.
# Tagging the release on main
git checkout main
git pull origin main
git tag -a v1.4.0 -m "Release v1.4.0: Stripe integration and OAuth updates"
git push origin v1.4.0

Enter fullscreen mode Exit fullscreen mode

6. Emergency Production Hotfixes

When a critical bug appears in production, we skip staging temporarily to patch main directly:

  1. Cut a hotfix/* branch directly from main.
  2. Apply the emergency fix and test locally.
  3. PR into main and deploy immediately.
  4. Crucial Step: Backport/cherry-pick the fix back into staging so the bug doesn't resurface in future releases!
# Backporting hotfix to staging
git checkout staging
git cherry-pick <hotfix-commit-hash>
git push origin staging
Enter fullscreen mode Exit fullscreen mode

Developer Takeaways

  1. Protect Main Branches: Always enforce Branch Protection rules on main and staging.
  2. Keep Feature Branches Short: Branches living longer than 3–4 days invite high merge friction.
  3. Squash PRs: Keep your global history readable with one commit per feature or bugfix.
  4. Automate Everything: Rely on GitHub Actions or GitLab CI to run tests and linters on every PR.

Need Help Scaling Your Software Development Pipeline?

Building robust, scalable applications requires standardizing both your code quality and your team's Git workflows.

👉 Partner with Software Solutions for modern web application development, enterprise software architecture, automated CI/CD pipelines, and dedicated software engineering tailored to your business needs.

Top comments (0)