Skip to content

Trunk Based Development

Overview

Trunk based development as a philosophy asserts the value of a single source of shared truth, the ' Main Branch' or 'Trunk' previously referred to as the 'Master' branch. Throughout the duration of this document, we will use the term 'Main'.

Any changes made by the team must be committed to the main branch in order to 'exist'. Think of it this way- if you spend hours fixing a bug and don't commit to Main, it doesn't matter if you've come up with the most brilliant fix possible, because there is no way for the team to functionally draw from the work you've done. In fact, by hoarding your code changes, you may have created incompatibilities with whatever solution the team may have come up with in the interim. For further discussion on why we have moved away from Git Flow, please click here.

Again, we use the Main as a shared resource so we can appropriately respond to the new logic created any time someone merges their changes. Appropriately, the 'value' of a branch shifts as well; the onus is now on committing to main as frequently as possible so that the whole team can share their successes together. This means we don't wait for perfection, shared, incremental progress is the goal.

Practice/Application

We practice trunk based development overall, with most teams using short-lived feature branches that flow through pull-request style code-review. This means that from a Engineer's perspective, we basically use GitHub Flow with the addition of explicitly naming your branches feature/*, bugfix/*, etc. and a few changes to address process flaws with GitHub Flow (see below). For the purpose providing better consistency of branch naming and to make deployment definitions easier to write and integrate, in addition to GitHub Flow, we also adopt some conventions from the classic Git Flow as a way of keeping processes consistent. For more information on why we use trunk based development instead of Github/Git Flow, see the " Alternatives" section at the bottom of this document.

Requirements

  • Git tags are used to signify release versions
  • No commits directly on release branches
  • Hotfixes to release branches are cherry-picked from Main

Releases and Cherry-picking1

If you are fixing bugs on the release branch and merging them down to the trunk you are doing it wrong. There is a chance you might forget to merge it down, and then there is going to be a regression at the next release moment (fresh branch cut from the trunk). Then egg on face, and recriminations.

Bugs should be reproduced and fixed on the trunk, and then cherry-picked to the release branch. A build should happen from that, a second confirmation that the issue has been remediated, and that deployment should go live (perhaps a point release). If you can not reproduce bugs on the trunk ( truly rare), then you have permission to go ahead and reproduce it on the release branch, fix it there, and merge back.

Priorities

  • Feature branches are short-lived; ie deleted once merged to Main
  • Trunk/Main is always release ready TBD 2

How to Make a Code Change

How to develop a new feature
  1. Engineer fetches latest code

    git fetch --all
    

  2. Engineer creates a feature branch of the latest of the trunk

    1
    2
    3
    git checkout main
    BRANCH="$TYPE/$TICKET_NUMBER-$SHORT_DESCRIPTION"
    git checkout -b "$BRANCH"
    

  3. The Engineers adds commits to the branch, running CI on each commit

    1
    2
    3
    git add $FILE_TO_ADD
    git commit -m "$COMMIT_DESCRIPTION"
    git push --set-upstream origin "$BRANCH"
    

  4. The Engineer creates a Pull Request to get their changes reviewed,

    • CI Success required prior to integration
    • At least one pull request approval,
      • two on larger or more complicated projects
_This step is dependent on the code repository tool used by the project._
  1. Once PR complete, code is merged to Trunk
    _This step is dependent on the code repository tool used by the project._
    
How to create a bug fix
  1. Reproduce the bug on trunk/main

  2. Engineer creates a feature branch of the latest of the trunk

    1
    2
    3
    git checkout main
    BRANCH="bugfix/$TICKET_NUMBER-$SHORT_DESCRIPTION"
    git checkout -b "$BRANCH"
    

  3. Create a commit to trunk/main that implements a test for the bug, and fixes the bug.

    • If no test was written that reproduces the bug, and no test previously found the bug (because it passed CI before getting merged in the first place), CI for these changes do not test that the bug was fixes unless a new test that covers the bug
      1
      2
      3
      git add $FILE_TO_ADD
      git commit -m "$COMMIT_DESCRIPTION"
      git push --set-upstream origin "$BRANCH"
      
  4. The Engineer creates a Pull Request to get their changes reviewed,

    • CI Success required prior to integration
    • At least one pull request approval,
      • two on larger or more complicated projects
    
    
    
    
    
    
  5. Once PR complete, code is merged to Trunk

    
    
    
    
    
    

How to Release the Latest Code

How to release from trunk
  1. Create a git-tag with the release version on the latest commit to release
    1
    2
    3
    4
    5
    git fetch --all
    git checkout main
    TAG="$RELEASE_VERSION"
    git tag "$TAG" "$COMMIT_HASH"
    git push origin "$TAG"
    
How to patch a release without an existing release branch
  1. Engineer fetches latest code

    git fetch --all
    

  2. Engineer creates a feature branch of the latest of the trunk

    1
    2
    3
    git checkout main
    BRANCH="bugfix/$TICKET_NUMBER-$SHORT_DESCRIPTION"
    git checkout -b "$BRANCH"
    

  3. Engineer adds a commit that fixes the bug in production

    1
    2
    3
    4
    git add $FILE_TO_ADD
    git commit -m "$COMMIT_DESCRIPTION"
    COMMIT_ID=$(git log --format="%h" -n 1)
    git push --set-upstream origin "$BRANCH"
    

  4. The Engineer creates a Pull Request to get their changes reviewed,

    • CI Success required prior to integration
    • At least one pull request approval,
      • two on larger or more complicated projects
_This step is dependent on the code repository tool used by the project._
  1. Once PR complete, code is merged to Trunk

    _This step is dependent on the code repository tool used by the project._
    

  2. Engineer creates a release branch from the latest deployed tag

    1
    2
    3
    PREV_TAG="$PREV_RELEASE_VERSION" # will be version "$MAJOR.$MINOR.0"
    RELEASE_BRANCH="$MAJOR.$MINOR.x" # e.g. "1.2.0" and "1.2.x"
    git checkout -b "$RELEASE_BRANCH" "$PREV_TAG"
    

  3. Engineer cherry-picks bugfix commit from Trunk to the release branch

    git cherry-pick $COMMIT_ID
    git push --set-upstream origin "$RELEASE_BRANCH"
    

  4. Engineer applies new git tag to latest commit on release branch

    1
    2
    3
    4
    git checkout "$RELEASE_BRANCH"
    TAG="$RELEASE_VERSION" # e.g. "1.2.1"
    git tag "$TAG" "$COMMIT_HASH"
    git push origin "$TAG"
    

How to patch a release with an existing release branch
  1. Engineer fetches latest code

    git fetch --all
    

  2. Engineer creates a feature branch of the latest of the trunk

    1
    2
    3
    git checkout main
    BRANCH="bugfix/$TICKET_NUMBER-$SHORT_DESCRIPTION"
    git checkout -b "$BRANCH"
    

  3. Engineer adds a commit that fixes the bug in production

    1
    2
    3
    4
    git add $FILE_TO_ADD
    git commit -m "$COMMIT_DESCRIPTION"
    COMMIT_ID=$(git log --format="%h" -n 1)
    git push --set-upstream origin "$BRANCH"
    

  4. The Engineer creates a Pull Request to get their changes reviewed,

    • CI Success required prior to integration
    • At least one pull request approval,
      • two on larger or more complicated projects
_This step is dependent on the code repository tool used by the project._
  1. Once PR complete, code is merged to Trunk

    _This step is dependent on the code repository tool used by the project._
    

  2. Engineer checks out the relevant release branch

    git checkout "$RELEASE_BRANCH"
    

  3. Engineer cherry-picks bugfix commit from Trunk to the release branch

    git cherry-pick $COMMIT_ID
    git push --set-upstream origin "$RELEASE_BRANCH"
    

  4. Engineer applies new git tag to latest commit on release branch

    1
    2
    3
    4
    git checkout "$RELEASE_BRANCH"
    TAG="$RELEASE_VERSION" # e.g. "1.2.2"
    git tag "$TAG" "$COMMIT_HASH"
    git push origin "$TAG"
    

TBD 3