Skip to main content

Our git workflow

In my current gig in ABC Active Memory (https://activememory.com/) we have put together a git workflow which works really well for us, so I decided I would write about it here so that others could benefit.

The things you need to have in place though are Continuous Deployment (http://en.wikipedia.org/wiki/Continuous_delivery), Test Driven Development (http://en.wikipedia.org/wiki/Test-driven_development), Agile (http://en.wikipedia.org/wiki/Agile_software_development) and the desire to keep releasing small independent incremental features without any one of them blocking each other.

Branches

So the key is branches (obviously). We have 2 main branches which are shared by everyone, and every new feature takes place in a feature branch. 

Our 2 shared branches are 
  • acceptance
  • master

Feature branch

When we start a new feature, we do all the work in our feature branch which we cut from master. Our feature branches are named by ticket number (i.e. 575-add-invoice-to-order) so that they can reference our JIRA tickets (though any ticketing system will do) and we can keep track of them.

If you have been working on a feature branch for a long time, it is worth occasionally rebasing or merging from integration to keep it fresh. 

Acceptance

When we think the feature is ready for the business owner to inspect, we merge it into acceptance and once this build is green, we deploy it to the acceptance server for the business owner to look at and (hopefully) accept. If for some reason the business owner needs some changes, we do them in our feature branch and then re-integrate into acceptance and then redeploy. Rinse and repeat until accepted.

Periodically, we will wipe the acceptance branch (when there are no pending stories in it) and re-cut it from master to weed out any stories that never get accepted (due to changing business needs).

Integration

Once the feature has been accepted, we merge it from our feature branch into master. Never merge acceptance into master as it may have other unaccepted stories in it.

Once master is green, deploy it to the staging server to test the actual build. Once the feature is checked on staging, we deploy to our production servers and do another feature check. Master should be deployed as soon as it is ready (i.e. don't let it hang around).

No downtime releases

We strive for no downtime releases most of the time, but occasionally we will need some scheduled downtime. If the feature one is working on does need downtime, we normally hold off on merging it into master until about an hour before deployment and we make sure everyone knows about it (it's quite rare though). We even deploy migrations without downtime provided we are only adding fields or tables (renames do require downtime unfortunately).

Advantages

So what advantages do we get from this workflow? Well, mainly, none of us are blocking each other. All of us can release without having to worry about the state of our coworkers' stories/features. Some features take longer to get accepted than others and it gives the business owners more time to examine a feature before accepting it.

Downsides

The downsides are minimal once you get used to the flow, but there is a little bit of complexity to take on when you first get on board. There is a lot of merging going on and sometimes you might have to fix merge errors twice (once in acceptance and once in master).

The other downside is if your feature does actually have dependencies on another feature then you need to wait for it to get integrated before you start working. It is possible, if you can't wait, to cut from the other feature branch instead of master, but it is not a risk free approach (especially if the first feature gets cut or rejected or delayed).

This flow also works best if all your developers are full stack or vertical, rather than horizontal. It is the case in our team that some of us are more geared towards the front end and some of us are geared towards the back end. We normally get around this either by sharing the feature branch or else by doing code reviews.

Summary

In summary, our git workflow is the following
  1. Feature branches for all new features
  2. Merge feature branch into acceptance and deploy on acceptance server for the business owner to accept
  3. Merge feature branch into master and make sure build is green.
  4. Deploy on staging and manually check
  5. Deploy to production and manually check

Additionally

  1. Hotfixes are done on master deployed ASAP
  2. acceptance is periodically deleted and re-cut from master



Comments

Popular posts from this blog

Master of my domain

Hi All, I just got myself a new domain ( http://www.skuunk.com ). The reason is that Blogspot.com is offering cheap domain via GoDaddy.com and I thought after having this nickname for nigh on 10 years it was time to buy the domain before someone else did (also I read somewhere that using blogspot.com in your domain is the equivalent of an aol.com or hotmail.com email address...shudder...). Of course I forgot that I would have to re-register my blog everywhere (which is taking ages) not to mention set up all my stats stuff again. *sigh*. It's a blogger's life... In any case, don't forget to bookmark the new address and to vote me up on Technorati !

Elixir - destructuring, function overloading and pattern matching

Why am I covering 3 Elixir topics at once? Well, perhaps it is to show you how the three are used together. Individually, any of these 3 are interesting, but combined, they provide you with a means of essentially getting rid of conditionals and spaghetti logic. Consider the following function. def greet_beatle(person) do case person.first_name do "John" -> "Hello John." "Paul" -> "Good day Paul." "George" -> "Georgie boy, how you doing?" "Ringo" -> "What a drummer!" _-> "You are not a Beatle, #{person.first_name}" end end Yeah, it basically works, but there is a big old case statement in there. If you wanted to do something more as well depending on the person, you could easily end up with some spaghetti logic. Let's see how we can simplify this a little. def greet_beatle(%{first_name: first_name}) do case first_name d

Speeding up RSpec

So today I have been looking into getting our enormous battery of tests to run faster. I have yet to find anything that works for Cucumber, but I did find an interesting way to speed up RSpec which is detailed here. https://makandracards.com/makandra/950-speed-up-rspec-by-deferring-garbage-collection Basically, it seems that by not collecting garbage too frequently, you can make your tests run much faster (at the expense of memory management of course). We observed a 30% reduction in the time it takes to run an RSpec test suite. I did try to implement this on Cucumber, however because we need to store much more in memory to set up and tear down our objects, it meant that I kept running out of memory when I wasn't using the default Garbage Collection and the tests took even longer (so, buyer beware). I suppose if you had a small set of features though you might see some benefit.