What is trunk-based development (TBD)?
Trunk-based development (TBD) is a software development and version control practice that encourages developers to commit their code changes to the main development line, or trunk, as often as possible. This practice is meant to reduce the complexity of merging code from multiple branches and help teams stay up to date with the latest version of the codebase. With TBD, developers commit their changes to the trunk and then use feature flags or other techniques to control when the changes are available in production. This practice makes it easier for teams to quickly deploy new features and bug fixes and helps them maintain a stable codebase.

What are the advantages of trunk-based development?
Improved Collaboration: Trunk-based development encourages collaboration between developers, since they are all committing to the same branch. This makes it easier to review and discuss changes and identify potential conflicts before they become an issue.
Easier Merging: Trunk-based development makes it easier to merge code from multiple branches, since all changes are committed to the same branch. This reduces the complexity of merging code, which helps teams stay up to date with the latest version of the codebase.
Faster Releases: Trunk-based development makes it easier for teams to quickly deploy new features and bug fixes. With feature flags, teams can control when changes are available in production, which helps them maintain a stable codebase.
Reduced Risk: By committing changes to the trunk frequently, teams can reduce the risk of introducing bugs and conflicts. This helps teams stay up to date with the latest version of the codebase, which can help reduce the risk of introducing bugs and conflicts.
Non-trunk integration anti-pattern
Some people will not believe that a single integration branch can work. They may be scared of the strategy and want to make some manual intgregration outside of trunk for "extra testing." They may want to merge some feature branches together in a separate integration. This is an antipattern because the integration they are testing is irrelevant - it's missing things from trunk, it follows a different order of integration, it may have things that trunk will never have. It's often not transparent, automatic and reproducible. When a bunch of feature branches are merged together outside of trunk, the acceptance testing done on this integration is irrelevant! If everything looks good, it doesn't matter because the trunk branch will be different - different order of merges, different resolution of conflicts, different combinations of changes, more changes than trunk, fewer changes than trunk. The separate integration will inevitably be ahead and behind trunk. So, any testing done there is wasted. If a separate integration looks good in the non-trunk integration branch it doesn't matter because this is not a version that could arrive in production. Similarly, if the separate integration looks bad, it's not worth doing a root-cause analysis and figuring out why it looks bad because it's not the real integration version.
The best way to ensure that the trunk branch is stable is to have a robust integration process. This includes regularly merging in changes from other branches, resolving conflicts, and running tests to make sure that the code is functioning as expected. Additionally, having a clear set of coding standards and guidelines can help ensure that changes are being made in a consistent and reliable way. Finally, having a code review process in place can help to identify and address any issues before they can cause problems in the trunk branch.
All eyes on trunk
Manual stakeholder testing is a valuable resource. The more eyes on trunk, the better the quality of the software. Manual stakeholder testing shouldn't be wasted on irrelevant versions that have diverged from trunk.
Stakeholders should be encouraged to review the code in trunk regularly, and any changes should be tested thoroughly before being merged. Additionally, automated tests should be set up to run regularly on trunk to ensure that any new changes are working as expected. This can help to catch any bugs or issues before they can cause problems.
Simple Testing Principles for Trunk-based development
Let's delineate two types of testing.
- testing that can prevent a merge to trunk
- testing that can not prevent a merge to trunk
Testing that can prevent a merge to trunk must be done on the individual feature branch, integrating all changes from trunk into the feature branch. If this version is accepted, it becomes the new trunk version.
Testing that can not prevent a merge can test the entire integration - the real release candidate that may go to production, not some other integration that is different from the code that will go to production.
Testing that can prevent a merge must be done on the individual changeset in question. Testing that can not prevent a merge should be done against the exact integration that is headed for prod - not some irrelevant manual integration.
Testing a non-trunk integation
Why is testing a non-trunk integration a waste of time?
Testing a non-trunk integration is a waste of time because it does not accurately reflect the code that will be released to production. The code in the non-trunk integration may contain changes that have not been tested or accepted and may not be the same as the code that will be released to production. Additionally, the non-trunk integration may be missing changes that are in the trunk, which may cause issues when the code is released to production. Therefore, it is best to test the exact code that will be released to production, which is the trunk version.
Examples of what could be wrong with a non-trunk manual integration:
- Missing features or bug fixes from the trunk version that are not included in the non-trunk version
- Code that has not been tested or accepted and could contain bugs or errors
- Incorrect or outdated libraries or tools that could cause compatibility issues
- Unintended changes to the code that could cause unexpected behavior
- Different order of merges with different manual conflict resolution
Not only could testing a non-trunk version lead to a false positive (finding bugs that won't exist in trunk). But, it could lead to a false sense of security (not finding bugs that will exist in trunk).
Testing resources are valuable and limited, particularly valuable stakeholder time spent manual testing. This manual testing should be maximized - either individual changesets are tested (very close to master, next version candidate) and accepted/rejected before merging. Or the real integration is tested after merge to get an accurate picture of the real version of the real product that is going to production.
Conclusion
Overall, testing that can prevent a merge should be done on the individual changeset in question, while testing that can not prevent a merge should be done against the exact integration that is headed for production. This ensures that any issues are addressed before the code is released and that the code is ready for production. Additionally, it is important to remember that both types of testing are valuable and should be used in conjunction with each other.

