Derex.dev

Branching Strategies

I wear two hats. Some days, I’m slinging code, trying to make features sing. Other days, I’m the last line of defense—the one who catches the bugs you thought you squashed. And let me tell you, your branching strategy? It’s making my life a living hell.

I’ve seen it all. Merge conflicts that look like ancient hieroglyphs. Bugs that mysteriously appear after a “simple” merge. Teams spiraling into chaos because their branching strategy is more complicated than it needs to be.

Here’s the thing: your branching strategy isn’t just a technical choice, it’s a predictor of pain. When I see a messy strategy, I know I’m in for a rough ride. As a developer, I dread the merging circus. As QA, I brace myself for the bug hunt.

Environment Branches and Git Flow

Let’s start with the worst offenders.

Environment Branches. You know the ones: dev, staging, master, and whatever else someone dreamed up. Each one represents a different environment, and from a QA standpoint? It’s a nightmare.

  • Multiple Deployments: I’m testing the same feature across three environments, each with its own quirks. As a developer, it means debugging problems that only happen on “that one server.”

  • Merge Mayhem: Keeping all those branches in sync is like herding cats. As both a dev and QA, I lose days to this nonsense.

  • Configuration Drift: No two environments stay the same for long. Sooner or later, something only works “on my machine”—and good luck figuring out why.

  • Debugging Hell: When a bug shows up, where do you even start? Is it the code? The environment? Or some unholy combination of the two? Cue the finger-pointing.

And then there’s Git Flow. Someone probably told you it’s “best practice.” But if you’ve ever tried navigating its labyrinth of develop, feature, release, and hotfix branches, you know it’s anything but.

As a dev, I feel like I’m spending more time moving branches around than writing code. As QA, I’m constantly asking, “Wait, which branch am I testing again?” It’s a maze. And somewhere in that maze is the bug you missed.

The Light at the End of the Tunnel: Trunk-Based Development and Feature Branches

There’s a better way. Actually, there are two.

Trunk-Based Development: Less Pain, More Flow

Trunk-Based Development (TBD) sounds scary if you’re used to elaborate branch hierarchies. But once you embrace it, life gets simpler:

  • Faster Feedback Loops: Bugs show up early. As a dev, I can fix problems while the code is still fresh in my mind. As QA, I’m not playing catch-up weeks later.

  • Reduced Merge Conflicts: Frequent, small merges keep things clean. No more terrifying mega-merges.

  • Confidence in Deployments: With solid automated tests, I’m not sweating every release. As QA, I’m catching real issues—not environmental weirdness.

Yes, it requires discipline. But you know what’s worse? Spending three days resolving a merge conflict because half the team went rogue.

Feature Flags: Merge Early, Test Safely

Feature flags are the secret sauce. They let you merge incomplete work without exposing it to users. This is a win-win for everyone:

  • Safe Testing: QA can validate unfinished features in production without breaking things.

  • Incremental Delivery: Devs merge early and often, which means fewer surprises later.

If you’re not using feature flags, you’re basically flying without a parachute.

Feature Branching (GitHub Flow): A Pragmatic Middle Ground

If TBD feels too radical, lightweight feature branching works just fine. Here’s why:

  • Clear Code Reviews: QA gets involved early, so bugs don’t sneak through.

  • Smaller Changes: Less code per merge means fewer headaches for everyone.

  • Faster Iterations: Quick merges keep us moving forward, not fighting the process.

It’s simple. It works. And it won’t drive your team insane.

A Hybrid Approach: Feature Independence with Release Tags

Okay, here’s a trick I’ve been experimenting with—a hybrid approach that balances feature isolation with integration testing. It’s saved my sanity more than once.

  1. Stable “Develop” with Release Tags: Treat develop as the integration branch. Whenever it’s stable, tag it (e.g., develop-stable-1.0).

  2. Feature Branches: Devs work in separate branches and merge into develop.

  3. Independent Feature Testing: Need to test a feature in isolation? Compare it against the develop-stable-1.0 tag—no interference from other work.

  4. Integration Testing: Once a feature is merged, we test how it plays with others.

  5. Production Testing: Same strategy applies to staging (e.g., staging-release-1.0), letting us validate final releases while still isolating new features.

Workflow
A branching strategy workflow

This gives us the best of both worlds:

  • Isolation: Test features independently without muddying the waters.
  • Integration: Catch conflicts before they hit production.
  • Stability: Tags provide a known-good baseline to compare against.

A little extra work upfront saves a ton of pain later.

The Dev/QA Plea: Keep It Simple, Stay Sane

I get it. You want control. But complexity isn’t control—it’s chaos.

If you care about your team’s sanity, do everyone a favor:

  • Ditch environment branches. Seriously.
  • Avoid Git Flow. It’s too much.
  • Embrace simplicity. Trunk-Based Development or lightweight feature branching keeps things manageable.

And if you need feature isolation? Use tags. It’s not rocket science, it just works.

At the end of the day, we’re all on the same team. We’re all trying to ship good software without losing our minds. So, let’s stop making it harder than it needs to be.

Did I make a mistake? Please consider Send Email With Subject