Skip to main content
Engineering

Git Workflows That Don't Make You Want to Quit

October 25, 20259 min read
GitVersion ControlWorkflowDevOpsBest Practices
Share:

Git Workflows That Don't Make You Want to Quit

At Home Depot, we used GitFlow. Feature branches, develop branches, release branches, hotfix branches. Our branch graph looked like a subway map. Merging a feature required a PhD in conflict resolution.

Now I use trunk-based development. One branch. Ship from main. My deploy frequency went from weekly to daily.

Why Most Git Workflows Are Over-Complicated

GitFlow was designed for software that ships quarterly on physical media. If your deployment process involves burning a CD, you need release branches.

If you deploy by merging to main and Vercel/GitHub Actions handles the rest, you don't need 90% of GitFlow.

What I Actually Do

```bash

1. Create a branch for the change

git checkout -b fix/stripe-webhook-idempotency

2. Make small, focused commits

git commit -m "add webhook event log table" git commit -m "check for duplicate events before processing" git commit -m "add test for duplicate webhook delivery"

3. Push and create a PR (even solo — for the diff view)

git push -u origin fix/stripe-webhook-idempotency gh pr create --title "Fix: Stripe webhook idempotency" --body "..."

4. Self-review the PR diff (24 hours later)

5. Merge to main

6. Auto-deploy to production

Delete the branch — it served its purpose

git branch -d fix/stripe-webhook-idempotency ```

That's it. No develop branch. No staging branch. No release branches.

The Commit Message Convention

I use conventional commits, but keep it simple:

``` feat: add Stripe webhook idempotency check fix: prevent duplicate payment processing docs: add ADR for database choice chore: update dependencies refactor: extract billing logic to lib/billing.ts test: add regression test for double-charge bug ```

The prefix tells you what KIND of change without reading the diff. `feat` is new functionality. `fix` is a bug fix. `chore` is maintenance. That's all you need.

When I Use Feature Flags Instead of Branches

Long-lived branches are where productivity goes to die. If a feature takes more than 3 days, I don't keep it in a branch — I merge it to main behind a feature flag:

```typescript function DashboardPage() { const showNewAnalytics = process.env.NEXT_PUBLIC_FF_ANALYTICS === 'true';

return (

{showNewAnalytics && }
); } ```

This means:

  • Main is always deployable
  • Incomplete features don't block other work
  • I can demo the feature by flipping a flag
  • No merge conflicts from a 2-week-old branch

The Solo Developer Advantage

Most Git workflow advice is for teams of 5-50 people coordinating parallel work streams. If you're solo, you don't have coordination problems. Your workflow should be as simple as possible:

  1. Branch for every change (even small ones — for the PR record)
  2. Keep branches short-lived (1-3 days max)
  3. Merge to main = deploy to production
  4. Feature flags for anything that takes more than 3 days

No develop branch. No staging branch. No release manager. Just main, branches, and deploys.

The Non-Negotiable: Never Push Directly to Main

Even solo, I never push directly to main. Every change goes through a branch and a PR. Why?

  • PR history is searchable. "When did we add Stripe?" → search PRs for "Stripe"
  • The diff view catches mistakes. I've caught bugs in my own PR diffs that I missed in the editor
  • It's a forcing function for small changes. If a PR has 40 files, it's too big. Break it up.

The 30 seconds it takes to create a branch pays for itself in clarity, traceability, and quality.

More in Engineering

Want to see this in action?

Check out the projects and case studies behind these articles.