Skip Navigation

Why I prefer trunk-based development - Trisha Gee

trishagee.com /2023/05/29/why-i-prefer-trunk-based-development/
22
TechNews @radiation.party irradiated @radiation.party
BOT
[HN] I prefer trunk-based development
22 comments
  • My problem with trunk based development is I feel like people treat it as the solution to a problem that is fundamentally a developer culture problem.

    You need to commit small changes, frequently, which requires you to only change small sections of the code and make incremental changes, something which can be a difficult habit to get used to.

    This is really the main benefit of trunk based development, and it's something you can get with feature branches as well, you just have to make sure everyone on your team starts reducing scope of their features and merging in smaller and smaller features sets.

    There's nothing inherit in the trunk based development model that stops someone from sitting on changes for a month, never pulling, and then trying to pull and ending up with a bunch of conflicts anyways. So it really feels like "yeah use trunk based development" boils down to "integrate continuously" which can be done with a branching model.

  • I disagree with a lot of the premises presented. Not that I think trunk based itself is good/bad or anything, but some of the arguments don't convince me.

    (All emphasis is mine, I didn't copy the author's because I'm lazy and on mobile lol.)

    1. Speed and Efficiency

    [...] This is literally "Continuous Integration (CI)", as originally suggested by the practices of Extreme Programming. While these days we tend to mean "run your build and tests on a team server every time you commit" when we say CI, what CI really meant was actually integrate your code regularly. Code living on separate branches is, by definition, not integrated. [...]

    I don't disagree with anything here, I only quote it because arguments where each side is using different definitions are pointless. The author is using a different definition of CI than what most developers would use so I wanted to make sure it is mentioned.

    2. Greater Code Stability

    [...] we don't want big merges from long-lived branches - the longer we leave it to commit our changes, the higher the chances our commit will clash with someone else's changes. By frequently pulling in the other developers' changes, and frequently pushing small changes of working code, we know the codebase is stable and working.

    The author sort of seems to use a different version of the term codebase in the emphasis but that just seems to be an extension of their view on what CI means. I would say my codebase is stable if the main branch is stable. They maybe seem to include branching developers' unmerged code as part of the codebase but I'm unsure.

    Of course, this assumption of "stable and working" is easier to check if we have a CI server that's running the build and tests for each of these commits. We also have to stop making commits if the build breaks at any time and focus on fixing that build. Continuously pushing small, frequent commits when the build is already broken isn't going to do anyone any favours.

    [...]

    4. Improved Continuous Integration and Delivery (CI/CD) Practices

    [...]

    In a trunk-based model, continuous integration becomes more straightforward because your code is committed frequently to trunk, and that's the branch your CI environment is running the build and tests on. Any failures there are seen and addressed promptly, reducing the risk of nasty failures. It's usually easy to track down which changes caused the problem. If the issue can't be fixed immediately, you can back the specific changes that caused it.

    [...]

    This is the part is disagree with the most. It is trivial nowadays to not allow a branch to merge unless a build passes and it is also trivial to have that be a build of the would-be merged code as opposed to the code on the branch. Now to be fair, I believe you could also do this with a trunk approach. You could have some hook which rejects commits, but that might be too slow. Instead you could do something like have a main and stable branch and push to main but pull from stable to deploy. Then have a process that checks each commit of main for the build passing and advances stable branch forward one commit.

    In short, I disagree that in a branching environment it is difficult to keep the build passing. (I agree that CI is more difficult with their definition of CI though.) The build failing should never be a surprise with either approach (apart from crazy weird edge cases that would effect both approaches). If it is inconvenient (or impossible) to run the build locally that should be addressed and I believe the author would agree with me (mentioning tests they seem to encourage unit testing), but they're seeming to say it is impossible so maybe they have a point I misunderstand.

    Continuous delivery also thrives in a trunk-based development environment. Successful continuous delivery hinges on the ability to have a codebase that is always in a deployable state. The trunk-based development approach ensures this by promoting frequent commits, frequent integrations, and tests on all of these integrations. The small number of changes being introduced at any one time makes the software easier to deploy and test.

    I see no reason why the trunk based approach encourages tests and the branch based approach discourages them. I see no reason why someone trying to ignore their responsibilities of writing tests is more likely to do it for big changes than small changes because they're still making more small changes. So if they're only writing like 50% of the tests they should I don't see why it wouldn't manifest as them skipping tests on half of their commits.

    In contrast, implementing effective CI/CD can be more complex and time-consuming with the branching model. While it's tempting to think "Well, I run my build and all my tests on my branch", you're not actually integrating every time you commit. It's at merge (or rebase) time that you start to see any integration issues. All those tests you were running on your branch "in CI" were not testing any kind of integration at all.

    Merging and testing code from different branches can introduce delays and potential errors, which takes away some of the benefits of having a build pipeline in the first place.

    I spoke on this above, but it is easy to have your CI server build a potential merge rather than the branch itself.

    Like I said at the start, I'm not saying anything about trunk based development as a whole, I just disagree with some of the arguments they claim make trunk based superior.

  • For me, history documents changes and intention. Trunk based development seems inherently incompatible to that.

    Many small commits intertwining different concerns makes it practically impossible to grasp changes by concerns or change sets. Once an issue inevitably occurs you may be able to work on the current state, but you can't check what was changed together and why. If it's not obvious from the code base you won't be able to quickly assess the context.

    The idea or thesis that many changes would lead to more stability (always deployable) is baffling to me. Changes are what introduces new issues. Mixing and intertwining concerns and people unaware of other changes in close time proximity increases risk.

    More commits also don't mean CI is more stable. A merge request as a gate to CI success is what does. A failed build on trunk already failed and blocks others.

    While I develop a solution I specifically don't want it on main. It's a messy process, that may be partially reverted. Once I found my approach I clean up / define history and explain the approach and changes.

    I want to commit the messy process too, for reference of my changes and approaches, and to have stuff I split off and integrate later. None of that belongs into main before I'm confident in the approach and changes. Because it's specifically not stable and should not be deployed.

  • The recommendation to make small, focused changes and commit frequently are good practices for using git as well. Any changes a developer has locally are effectively a branch, so I don't see much of a difference between the two approaches, at least in the context of this article.

  • Regarding section 1, won't you still get the conflicts when pushing to remote (or pulling from it)?

  • This type of workflow is natively supported by https://github.com/facebook/sapling, https://github.com/jiju-git and https://github.com/arxanas/git-branchless. Both these tools can interact with normal git repositories.

    The idea is to divide commits between public commits that are untouchable and under-dev-commits that can be amended, split, merged, reorderdered, and so on. One can play freely with under-dev-commits, because every modification done on them is locally registered, and one can navigate in a tree of undo.

22 comments