This page looks best with JavaScript enabled

Trunk-Based Development: Push to master, do it often

 ·   ·  ☕ 6 min read

What it is and why bother.

If we think of software development as a means to satisfying the needs of a user(s), we’ll notice some “gaps” in the way those needs are conveyed:

  • What the user actually wants != what he thinks he wants.
  • What the user thinks he wants != what the business thinks he wants.
  • What the business requests != what ends up as a “requirement” for the dev team.
  • Requirements given to the dev team != what is actually shipped to production.

In an ideal world, the user would peek over our shoulders while we code, understand what we are writing, and let us know if we are on the right track.

Since this is a bit unrealistic, we would want to ensure our efforts make sense by giving the user the latest version of our software, as soon and frequently as possible.
This would inform us of our mistakes quickly and establish a short feedback loop between development and production.

TBD is one of the approaches we might use to achieve this goal.

What even is TBD?

Trunk Based Development is a practice in which branches are generally avoided and all (most) of the development is done in one main “Trunk” branch, also used for deploys, QA and the likes.
You don’t need to strictly avoid all branches, but no long-lived branches should exist.

It follows that, for this to be viable, you would want your commits to be small and really frequent, and your code to be thoroughly tested and always releasable.

Simply put, you should aspire to constantly commit excellent, tested code straight to production.

What’s wrong with branches?

Nothing inherently wrong, but we often forget the trade-offs of using them.

Merge Hell

In big projects with multiple dev teams working on shared code, Merge Hell is a very real issue.

Nobody wants to spend half a work-day resolving merge conflicts, much less pay for someone’s salary to do so.

The feeling you get after resolving merge conflicts for 2 hours, only to find out more conflicting code was pushed in the meantime is… not nice.
It’s just not a productive use of time.

Information hiding

If you are working on a branch created more than a day ago, you only know how your code behaves with yesterday’s project.
Today’s project might not behave the same, and you might need to re-think what you are doing.

Wouldn’t you want to know if that’s the case as soon as possible?

Moreover, while your work is not in the main branch, you are hiding information from the rest of the team.
Nobody knows what your code looks like, how it behaves or how to work with it.
Why hide that information?

Value adding speed

Instead of asking what’s wrong with branches, one might ask: Why do we need them (especially long-lived ones) anyway?

We are clearly able to push directly to master when a hotfix is needed.
Even if the repo settings prevent this, given an important enough issue, we create a branch on the fly that lives for less than an hour and goes straight to production, no fluff involved.

This is done as a way to very quickly update the software currently in production, in order to add value by squashing a bug or fixing a glaring issue.
Wouldn’t we want the same speed when releasing new features or updating our UI?

Why do we reserve this luxury only for emergency situations?

Elephants in the room

CI/CD

One might argue that these pitfalls are avoided by practicing CI, no need to do TBD.
The thing is, you are probably not really doing CI if you aren’t also doing TBD.

CI is an ambiguous concept, as it relies on what one considers “continuous”.
Teams coming from a monthly release cycle might consider weekly integrations to be “continuous”, while one might argue that daily integrations are the bare minimum for CI.

CI/CD is also quite often mistaken with “having a pipeline”, which is indeed part of the deal, but not the whole thing.

If we want to deliver software continuously (CD), we need to integrate our work continuously (CI).
This indeed requires a well-thought-out pipeline to production, but it also requires thorough testing and practicing TBD.

The fundamental assumption of CI is that there’s only one interesting version, the current one.
-src-

If the only interesting version is the current one, why waste effort, time and resources in other versions?
When practicing TBD, CI will naturally follow (one would otherwise have to be very “brave”).

PRs

Does this mean we need to ditch PRs completely?
As mentioned before, branches are not the devil, PRs have their place.

TBD is a way of approaching development, not a strict dogma.
We’ll see that these principles can be followed even in a PR centric workflow.
So long as they are used in a specific and well reasoned way.

Still, you might want to reconsider why exactly we use and (in theory) “need” PRs, since they have some clear downsides:

  • Reviews are a pain
    PR reviews are often treated like chores: rarely does one enjoy doing them and more often than not, they are done as an afterthought in some spare time.

  • Context change
    They require constant context changing (if one is to prioritize them).
    This is not productive and should be avoided as much as possible.

  • Slow pace
    Reviewing PRs is a slow process (if done with care), and even if done with the best of intentions and effort, it still imposes a significant slow down to the rate at which we ship value to the user(s).
    After all, perfectly good code might be sitting in a PR right now just waiting for someone to review it while it could be adding value to the project.

  • Better alternatives
    In most situations, live code reviews and pair or mob programming sessions are a much faster way of ensuring code quality, with much less chance of overlooking mistakes or introducing bugs.

Gatekeeping

More often than not, PRs are used as a form of gatekeeping.
Surprisingly, this can make sense in some scenario.

  • Many juniors, one senior
    Although this isn’t ideal, PRs are a nice way of managing these situations ensuring code quality standards are met (and bugs avoided).

  • Open Source Software projects
    There are usually only a handful of maintainers with a complete picture of the codebase and sometimes hundreds of occasional contributors.
    It just makes sense for them to inspect the code before merging it and PRs are the best way to do so in that context.

Other than these situations, one struggles to find a reason for one team member to veto the work of another.

Trust

Some might feel uncomfortable with the idea of “just letting anyone push to master”.
Besides from that not being the point and for the sake of argument: why?

What would you expect to happen?
Would you expect your teammates to knowingly introduce bugs?
Are you worried that they aren’t “good enough”? “Professional enough”?

As mentioned before, it’s reasonable to worry about these things in some scenarios (mainly OSS projects), but other than that, these concerns are either unreasonable or need to be tackled well before thinking about introducing TBD in your team’s workflow.

A team can’t work efficiently and effectively if their members don’t fully trust each other.
If that isn’t the case, you are probably better off leaving TBD for further down the line, as there are likely more pressing issues to take care of.

Support the author with