Except that's not the best way to do it.

Here's a better way.

1. Test first. If the tests don't pass with your own changes, you've stuffed up. Make sure you didn't stuff up before you try to share. Besides, you've been doing this as you made the changes, right?

2. Push. If you're not up-to-date, it won't work. If it works, start a new bit of work.

3. If you're not up-to-date, pull.

4. Review what's changed and see if that impacts what you're doing. Make changes as appropriate. Then go back to step 1.

Pulling without reviewing what changed increases the risk of a conflict. Not a merge conflict, but a _conceptual_ conflict. Maybe there's a change in behaviour you weren't expecting. Maybe there's now a better way to do things. Maybe there's some duplication you should consolidate.

Not testing before pulling means that when you get a test failure you don't know if it's your stuff or because of a conceptual conflict.

And because push is fail-safe (it won't work if you're not up-to-date), there's no reason not to just do that if your tests are passing,.

https://mastodon.cloud/@jasongorman/116232201879519354

Jason Gorman (@[email protected])

Attached: 1 image It's another Back To Basics Sunday. When we're doing Continuous Integration, that means we're pushing our changes directly to the trunk. To have confidence, we need to: 1. Make sure we're up-to-date with trunk 2. Make sure the tests are passing 3. Make sure it's safe to push Pull -> Test -> Push

mastodon.cloud

The failure mode here is when your _full test suite_ takes longer than your commit cycle.

This gives you a set of conflicting desires:

* you want a comprehensive test suite. This takes time, so you face pressure to cut back, or do slower tests on another branch.
* ideally, you want small commits, done frequently, so that the changes are understandable. (See step 4 in the previous post)
* depending on your org, you may be wanting more developers to work on the same code base. Between this and the previous point, you get bigger commits and increase the chance that things break.

The most common way to deal with this is to break things into modules. It's not a bad idea. But it means you're no longer "continuously integrated". Because you can't force the clients of your modules to update immediately.

The other way that this breaks is that the _full test suite_, if it's comprehensive enough to be a strong safety net, is going to be slow enough that blocking your development work becomes a time waste. Particularly if you've got a good code base where making changes rarely breaks the tests you wouldn't run in your more focused development mode,

At some point, you're going to want to offload that full-test-suite. Maybe you do this with feature branches - push your change to a branch, have it test in the background and merge when it passes. Of course, you're no longer doing trunk-based-development. But you've not sacrificed anything in the way of "Continuous Integration" - that not-ready-for-trunk code wouldn't have been pushed to trunk anyway.

Spoiler: the code on your machine wasn't integrated anyway. Moving it off your machine to another machine doesn't make it any less integrated. As long as you close feature branches quickly (e.g. when the tests pass), you're still just as "Continuously Integrated" as people doing trunk-based-development; all you're doing is making the "make the tests pass" bit async.

Yes, it does mean that if your tests _don't_ pass, you end up interrupting the next piece of work you started. So? Worst case is that you need to restart that piece entirely – but that's no worse than if had waited for the tests.

But if your commit cycle gets slower than your test speed, you'll end up with more frequent merge blocks. Or maybe you've got contention for resources in your full test suite, so you can't run a number of them in parallel.

This ends up with you needing to batch changes. Good news - you can stick with pure trunk-based-development. Bad news: when the full test suite breaks, there's going to be multiple sets of changes that could be the culprit.

And, of course, it means your trunk code _isn't_ integrated. Because there's a gap between the head of the trunk and the last point where tests passed.

The point about all of this is that being continuously integrated is a spectrum, not a binary state. You're never 100% integrated as long as there is a single keystroke different between the code on your machine and the deployed software (because CI is about getting the software deployed, not merely committed).

The point of CI is to _reduce_ the time it takes from when you have a code change ready to getting it in use, not eliminate it. There's lots of ways to do that, and trunk-based-development is neither a necessary nor sufficient process. It's one good technique amongst many,

Just don't go for long-lived feature branches.