Published on

GitFlow on a banking app: the setup we actually used

Authors
  • avatar
    Name
    Phat Tran
    Twitter

GitFlow has a reputation for ceremony, and most of it is deserved. We ran it anyway on FE Online 2.0, because on a banking app you have to know at any moment exactly what is in production and what is about to be. This post is the setup we used, commands included. The feol you will see in every branch name is the project's ticket prefix.

Branch naming

We fixed the naming convention before anything else, and it did more for us than any diagram. When a branch carries its ticket ID and status, nobody has to open the PR to figure out what it is for.

# Feature
# [WIP] is optional: add it when the ticket is not finalized but you need an early PR for review or tracking.
# A module tag like [CASA] can sit in front of the feature name when it helps.
Feature Branches: feature/feol-{ticket_id}_[{ticket_status}]_[WIP]_{feature_name}

Example: feature/feol-1_[in_review]_[CASA]_add_casa_journey

# Hotfix
# `feol-{ticket_id}` and `[ticket_status]` are optional: add them when there is a ticket.
Hotfix Branches: hotfix/feol-{ticket_id}_[ticket_status]_{hotfix_name}

Example: hotfix/wording_cta_feedback
         hotfix/feol-1_[in_review]_wrong_design_home

# Release
Release Branches: release/vx.y.z

Example: release/v1.4.0

Yes, the names get long. A branch name is typed once and read many times, usually by someone trying to work out what changed and why, so we optimized for the reader.

Branch strategy

Three branches matter. master mirrors production and is the source of truth. develop is where approved features pile up for integration testing. Feature branches like feature/feol-... are cut from master.

That last part is not textbook GitFlow. The textbook cuts features from develop. We cut them from master and treated develop as a merge target only, never a base, because at any given moment develop holds a mix of features still in testing, and none of that should leak into your branch. You will see this rule again below. In the team doc it was written in caps.

Features and releases

Implementing Features and Handling Releases

The feature flow

  1. Cut a feature branch from master:
git fetch && git checkout master && git reset --hard origin/master
git checkout -b "feature/feol-xxx-Implement-feature-A"
  1. Commit to it until the feature is done:
git commit -am "feat: :zap: add transfer confirmation screen"
  1. Open two pull requests for review: one to master, one to develop.

  2. If the review rejects it, go back to step 2. If it is approved, merge the feature into develop. On conflict, resolve it on develop locally: check out develop, merge the feature branch into it, and ask the people who touched the conflicting files instead of guessing. Then push.

git checkout develop && git fetch && git reset --hard origin/develop
git merge origin/feature/feol-xxx-Implement-feature-A
... resolve conflict ...
git commit -am "fix: :zap: resolve conflict"
git push origin develop

REMEMBER: never pull develop into feature/feol-xxx-Implement-feature-A. If you do, everything currently sitting on develop rides along into your feature and, eventually, into a release.

  1. Once the feature passes UAT, open a pull request to the next release branch and put the release milestone on it.

One thing worth spelling out: the PR to master never gets merged by hand. It exists to review the change against master and to hold the conversation. It merges automatically when the release PR that carries the feature is merged.

Releases

A release is a branch, and the branch goes through UAT before it gets anywhere near production.

  1. Cut release/vx.y.z from master:
git fetch && git checkout master && git reset --hard origin/master && git checkout -b "release/vx.y.z"
git request-pull origin/master ./ | grep -i feol-

The second line prints what the release will ship, filtered down to lines carrying the feol- ticket prefix. Because feature branch names contain their ticket IDs, the merge commits do too, and the output makes a decent checklist to hand to QC.

  1. Deploy release/vx.y.z to UAT.

  2. QC verifies every ticket on the release.

  3. If something fails, cut a hotfix/fix-... branch from release/vx.y.z, fix it, open a PR back into the release branch, and send the build through QC again.

  4. When QC signs off, update CHANGELOG.md, bump the version, and tag:

git commit -am "chore: bump version"
git tag -a v$(cat VERSION) -m "Release v$(cat VERSION)"
git push origin --tags
  1. Merge release/vx.y.z into master and develop, then deploy to production.

Hotfixes

Production hotfixes skip the queue. Branch straight off master, fix, get it reviewed, then merge into both master and develop so the two do not drift apart. Same naming convention as everything else.


Looking back, two things carried most of the weight. The naming convention meant any branch, PR, or release could be traced to its ticket without asking anyone. And keeping develop strictly downstream of features made releases boring, in the good way: master always matched production, and the release branch always matched UAT. Nobody had to guess what was shipping.