Feature Branching Is NOT a Strategy
If your branches outlive the day — you have a backlog of merge conflicts.
👋 Hello, this is Valentina with the free edition of the Optivem Journal. I help Engineering Leaders & Senior Software Developers apply TDD in Legacy Code.
Every ticket gets a branch.
Nobody decided this. There was no meeting, no architecture review, no trade-off weighed. It’s just the reflex — the default Git workflow everyone inherited the day they learned git checkout -b. New ticket, new branch. Open it, work on it for as long as the work takes, merge it when it’s done.
And it is quietly the reason “we do CI” is a lie.
A branch that lives for a week is not CI. It’s a private fork.
And a private fork is the opposite of continuous integration — you are NOT integrating continuously. You are integrating at the end.
A Long-Lived Branch Is a Private Fork
Here is what a feature branch actually is: a copy of the codebase that diverges from everyone else’s copy, a little more, every single day it stays open.
On day one, the difference is small — your branch and main differ by your few lines.
By day five, main has changed — three other developers merged their own week-long branches. Your feature branch has changed too, and you only find out how far apart they are when you try to merge.
The conflicts aren’t just textual. Two developers refactored the same function for different reasons. A method you are calling got deleted. An interface was modified.
The merge you keep deferring is getting more expensive every day you don’t do it.
This is the trap: merging hurts, so the team merges less often, so branches live longer, so the next merge hurts more. “I’ll keep my work isolated until it’s solid” — is the exact thing causing the pain.
Trunk-Based Development
Trunk-based development is the decision to stop deferring.
It’s not “no branches.” That’s the strawman people use to dismiss it. It’s no long-lived branches
✔ Everyone integrates to main at least once a day.
✔ Branches are fine — as long as they live hours, not weeks.
✔ The trunk is the single shared integration point. There is no “at the end” — only now.
❌ No branch that survives the sprint.
❌ No “I’ll merge it when the feature is done.”
The whole mechanism is small batches. Integrate a little, constantly, and changes never get big enough to become a conflict.
The merge that hurt when you did it once a week stops hurting when you do it three times a day — not because the work got easier, but because each piece is small enough that there’s nothing to collide with.
You’re not avoiding the painful thing. You’re doing it so often it stops being painful.
“But how do we ship half-finished work?”
This is the real objection. If I merge to main every day, and my feature takes two weeks, won’t I be pushing broken, half-built code into the shared trunk?
No — because you hide unfinished work in main, not from main.
With Trunk-based development you integrate incomplete work safely:
Feature flags — the code is merged and deployed, but dark. It doesn’t run until you enable it.
Branch by abstraction — add an abstraction layer (like an interface) in front of old code, and swap the implementation behind it incrementally, with main green the entire time.
Keystone interface — build the feature, but don’t expose it to users yet. “Turn on” the feature in the UI at the end.
You integrate code that isn’t finished without integrating code that’s broken. The feature is incomplete; the trunk is always releasable. This makes sense once you stop thinking “merged” = “done”.
The team that says “we can’t do trunk-based, our features are too big to finish in a day” has it backwards.
The features don’t have to be finished in a day. The integrations do.
How Long Do Your Branches Live?
You don’t need to think too hard to know which camp you’re in. Open your repo’s branch list and find the oldest open branch. Look at its age.
If it’s measured in hours, you’re integrating. If it’s measured in days or sprints, you’re feature branching — and every one of those branches is a deferred merge that gets harder over time, no matter how green the build looks today.
Trunk-based development isn’t “no branches.” It’s no long-lived ones.
The thing that breaks CI was never branching itself — it’s how long the branch lived before it was merged into main. Shrink the branch lifetime, and the merge pain you’ve organised your whole workflow around avoiding simply stops existing.
👉 Next week, I’m running a live workshop where we walk through a working e-shop example with a pipeline architecture, so you can see how it works in practice.
No rebuild hacks. No “it passed CI but broke anyway” surprises.


Trunk-Based Development == merge incomplete work, not broken work.