ENV Branching with Git | Branching Models & Git-Flow Strategies
Back to Mingle

ENV Branching with Git

Posted in Insights — January 29, 2015

Another Successful Branching Model

When developing applications with multiple features, team members and deadlines, a version control system with robust branching is key. This is why we switched to Git from SVN over four years ago. After taking a simplistic approach we quickly realized our branching model needed another look to allow for development of concurrent features.

When asking most people what the ideal branching strategy is I get pointed to A successful Git branching model, also known as git-flow. While tried and true, there’s one element that didn’t line up with the majority of our applications: releases. More on that later.

Without releases, this model becomes overly complex. Instead of adopting unnecessary complexity, our team built off of our simplistic approach, adding steps and methodologies as needed. Each addition to the process was discussed, used in the wild for a couple weeks and rediscussed, ultimately keeping it or cutting it. We stumbled at times, but eventually we came up with a strategy that’s flexible enough for our client demands, yet more simple than most models. We’ve dubbed it ENV Branching.

The Basics

ENV Branching is based on the environments the application deploys to. For us these are typically:

  • production: the live site
  • stage: for staging features to the client
  • development: for staging features internally

Each environment has a dedicated ENV branch: master, stage and dev. At any given time, those branches will resemble exactly what’s on their corresponding environment. All commits are done on a feature branch and merged into the environment you wish to deploy. With continuous integration, pushing an ENV branch to the remote triggers an automatic deploy. When a feature is ready for production a pull request is created to merge the feature branch into master. Approval and merge of the pull request triggers an automatic deploy to production.

The Rules

To maintain the integrity of the branches and ensure we can deploy features independently we follow these rules:

  1. NEVER commit directly to an ENV branch (master, stage or dev)
  2. NEVER merge one ENV branch into another
  3. Feature branches should be autonomously deployable

A Simple Example

  1. create your feature branch
    # grab the latest from origin/master
    git pull --rebase origin master
    # create your feature branch
    git checkout -b 123-wine-validations
  2. commit your changes
    # stage and commit your changes to feature branch
    git add -A
    git commit -m "refs #123 - adding validations to wine model"
  3. deploy changes to dev for internal review
    # rebase from origin/master and resolve any conflicts
    git pull --rebase origin master
    # switch to the dev branch
    git checkout dev
    # rebase from origin/dev
    # (which could contain merged branches from other team members)
    git pull --rebase origin dev
    # merge your branch
    git merge 123-wine-validations
    # push changes to origin/dev (which triggers a deploy)
    git push origin dev
  4. deploy changes to stage for client review
    # checkout your feature branch and rebase from master
    git checkout 123-wine-validations
    git pull --rebase origin master
    # follow the previous step only using the stage branch
    git checkout stage
    git pull --rebase origin stage
    git merge 123-wine-validations
    git push origin stage
  5. deploy changes to production
    # checkout your feature branch and rebase from master
    git checkout 123-wine-validations
    git pull --rebase origin master
    # push your branch to origin
    git push origin 123-wine-validations
    # open a Pull Request to merge feature branch into master
    # or merge it to master manually

Why We Like It

Simplicity. In the same vein as the programming methodology, we like to Keep It Simple, Stupid. No matter how solid your branching model is, if people misuse it due to complexity it’s not working.

Even though it’s simple, it maintains the power of more complex models. ENV branching allows us to develop and stage concurrent features without blocking each other or waiting for a release to deploy.

The cherry on top is that it works perfectly with our continuous integration system. Since we have a branch for each of our environments, we can trigger tests and a deploy when one of the ENV branches are pushed.

When Not to Use It

If your application has structured releases that need to be tagged and individually maintained, by all means use a model that includes actual release branches.

There were a couple times we tried to gang up features into faux-releases that were merged and deployed simultaneously. Using this model in this way can cause conflicts. However, it’s easy enough to create a release branch to base the feature branches off of as long as it’s not a regular practice.

Are Releases Needed?

Most of the work we do are client websites and single serving web applications. There is only one stable version that needs to be maintained and releases become a method to structure client communication and roadmap features.

We thought faux-releases would aid in our development process, when in reality they were a restriction. Certain features would hold up the entire release while in refinement rounds with the client and other features would go stale. At the same time the client would be anxious to start the next round of features or have a promotion coming up with a hard deadline.

ENV branching gives us the agility to develop a wide range of features simultaneously without being tied to a release schedule. Features are deployed as they are completed, which keeps us our applications shipping and our developers building.

James Kurczodyna is the Director of Application Technology at FINE.


  1. Excellent write-up. This is the model that I’ve been looking at as well; your description is clear and concise so I’ll use it as a starting point for the team. Like you we deploy to dev, stage, integration, and production environments and I wanted to use a branch-per-environment approach but wasn’t sure about how that working with the overall flow. Your post gives a great place to get started.

    Posted by Ron Cordell, May 12, 2015 | Reply

  2. For me, that’s a strange pattern.
    The logic of splitting branch per environment let introduct implicitly that code can change between environment.
    In a devops mode, that’s not what we want. I just get into a customer that get this way of releasing. They are lost because they are making some big merge between env branches after test validation…
    We always want to deploy continuously on dev platform, then test, if test is successful put a tag on the validated hash then deploy tag of the app to the next platform (e.g.staging).

    Posted by Odd model, June 8, 2015 | Reply

    • Yes codebases in the ENV branches will diverge, but that’s required for a release-less system. Unit and acceptance tests happen on the feature branch and it’s the feature branch that’s merged into ENV branches. In your example you mentioned merging ENV branches into each other which hints at a release based system. This shouldn’t be done in this model.

      We worked in systems with multiple levels of acceptance on entire codebases before it’s moved into the next env, but they’re slow. Small changes take weeks to deploy and features are held up by releases. We maintain well over 50 apps with this strategy and is fast, agile and affective.

      Posted by james, June 10, 2015 | Reply

  3. We’ve been using a similar model (dev, stage1, stage2 and master) for the past 4 months and found that it slowly falls apart as the number of merge conflicts increases. In these 4 months there have been ~1200 commits, and merge conflicts were quite common.

    When branches are merged in a different order across the environment branches, the merge conflicts are resolved in a different order too and mistakes are inevitably made at some point. We had to trash our dev branch once and now discovered that one of the staging branches is also broken because of an incorrect merge.

    In theory it sounded great, but in practice it’s hard to maintain correctly.

    Posted by Stijn, January 13, 2016 | Reply

    • It can be hard comparing branching models without looking at the specific implementations, project and team dynamics. I can’t speak to the strategy you had issues with but our team of 8 has been using it for almost 2 years, across over 50 projects and rarely run into issues.

      If you’re running into constant conflicts, I’d assume you team is working on different features that affect the same files without deploying frequently. If that is the case, then I would definitely look into a release sprints along with a released based system, like git-flow.

      Posted by james, March 8, 2016 | Reply

  4. Great post. I like the flexibility this approach allows.

    How do you handle merge conflicts (say in a Pull Request into Dev) with this strategy?

    The usual way to do this would be to merge DEV into the feature branch, resolve the conflicts and the re-push the feature branch (to refresh the PR). But this ends up “polluting” your feature branch with other commits from DEV (the ones you resolved conflicts with) so that you end up taking them along when you merge the feature branch into QA?

    Posted by Mayowa, October 4, 2016 | Reply

    • Thanks! We mainly do PRs into master and freely merge and deploy dev/stage. To handle conflicts with dev we’ll checkout and rebase the dev branch locally then merge the feature branch into dev handling any conflicts in a merge commit. This will leave your feature branch clean.

      Posted by james, January 18, 2017 | Reply

  5. We use the same strategy with SVN for many years without problems: feature and fixing branches, environment branches, trunk is the production one. I don’t see why one should switch to GIT for using this method.

    Posted by Gábor, January 6, 2017 | Reply

    • True, both SVN and Git can achieve the same workflow. For us it’s a matter of preference. We find managing branches in Git a lot easier, which in turn encourages a consistent use of the workflow. When we used SVN, devs would end up avoiding branching when they could.

      Posted by james, January 18, 2017 | Reply

  6. This branching strategy seems odd to me, and perhaps I am missing something. It seems to me that as you merge to each environment you are rebaseing the master branch into the feature branch each time. Doesn’t that mean it’s possible to get untested features in production? When are merges to the master branch performed? Is production merged to master at some point?

    Posted by Ryan, January 18, 2017 | Reply

    • Rebasing your feature branch with master is a way to make sure your feature branch is up to date without updating master. Your feature branch is then merged into dev/stage for testing before a PR into master is opened. We don’t use a production branch, master == production.

      Hope that provides some clarity 🙂

      Posted by james, January 18, 2017 | Reply

      • Excellent writeup, James, as others have said. This is more-or-less the direction I’d like to set for my small team here.

        However, if we preferred to have squash merges in our environment branches (and our Git server can even enforce that), would that work okay with the rebase-from-master-before-merging plan? I can imagine it might function just fine but potentially squash several people’s changes into one commit, reducing the usefulness of the commit record…

        Posted by Eric Wallace, August 20, 2019 | Reply

  7. I am following this model to a certain success. I am currently a single-man operation, so I am trying to keep the history linear. While it works for master (when I merge feature branches, they fast-forward), staging and dev generally creates a merge commit, causing a rainbow graph. Any ideas how to keep it clean on every branch?

    Posted by s, April 20, 2017 | Reply

  8. This strategy looked really good fit for our needs, as we’ve been searching for a good continious delivery Git flow. I’ve been working to implement it to be used from our Release Manager, but found out there are some strange things. First of all, it seems like you work with your feature branches only locally – you never push to any commits or the branch itself to the origin. This looks odd to me, as you’re not keeping any copy of the branch and changes into the Git origin until you’ve finally moved the feature (of course with the branch itself) to the production ENV. Also, does every developer deploy it’s stuff on the corresponding branch? So the whole continious delivery process you’re having is not conducted by one guy, rather than each and every guy into the development team moves the stuff he/she is working on to the right place every time.

    Posted by Georgi, July 13, 2017 | Reply

  9. I am looking at using a model like this as we have the same situation in terms of environments that code must be promoted through. It’s been a little over 2 years since your original post. Are you still using this model? Have you made any changes? Do you have any lessons learned?

    Posted by JimK, August 21, 2017 | Reply

  10. Hello

    I am new to using GIT with multiple team members and having multiple servers dedicated to different phases and branches.
    So there are three servers : dev, acceptance and prod.
    There is a code repository (same on each server) and we are planning to put it under version control. But I am a bit lost in the concepts involving various servers. Can you guide me how to achieve it?
    Create a git project on Gitlab (thats what the company has decided to use), with three branches : dev, acc and prod. Then on each of the servers, initialize the git repo? Please guide here how to proceed.

    Posted by Nupur, September 22, 2017 | Reply

  11. If i have different environment config files in master, dev branches. When i create a feature-A branch from master then i merge to dev branch to test. It merged all files including env config file which i didn’t touch at all. How to ignore environment files and only merge those files i have changed?

    Posted by Chok Wee Ching, January 15, 2018 | Reply

  12. Well for me I have quite similar approach but instead of using your rule ‘NEVER merge one ENV branch into another’ We do flow in forward direction. For example, we create branches from master for feature and bug fixes and when we think that the feature is ready to be publish, first we merge to master, then to staging (for testing) and then production branch. We keep master staging and production at the same level. The reason for following that flow is, if we keep production, staging and master at the same level and if we all keep our local master up to date with remote master, it becomes very easy to merge new feature to master. Also let suppose if some new person come, he or she can start working right away by cloning the repo and branching from master, instead of worrying about which branch to use.

    Posted by TH, April 18, 2018 | Reply

  13. Maybe I’m missing a point, but how are the hotfix changes propagated to the other branches, so that the bugfix is also available in dev?

    Posted by Felix, June 19, 2018 | Reply

  14. in this configuration you cannot test more that one feature in one env at a time.

    Posted by Frank Romero, August 2, 2018 | Reply

  15. What do rollbacks look like in this model?

    Posted by Jason Martin, June 6, 2019 | Reply

Leave a Reply

Your email address will not be published. Required fields are marked *