Skip to main content
Back to Blog
Engineering

Git Workflows That Don't Make You Want to Quit

October 25, 20259 min read
GitVersion ControlWorkflowDevOpsBest Practices

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.

Want to see this in action?

Check out the projects and case studies behind these articles.