Semantic Versioning (SemVer.org) allows you to communicate changes to your software to users through your version numbers. But how do you use it with GitFlow, pull requests or continuous delivery? What about easily creating alpha/beta packages? Do you need a nightly/CI feed? Don’t know how all these bits fit together? You want to see this talk then and get a holistic view of what it means to Semantically Version your software.
I will show you what SemVer is, the problems it solves, new ways it can be used (customer facing apps/sites?). I will cover the GitFlow versioning strategy, and the much simpler pull request model. When to use each, and how you can take advantage of conventions in these workflows to make Semantic Versioning an integral and easy part of your workflow from writing to releasing and maintaining your software!
The talk will also include many of the things that Simon Cropp, Andreas Öhlund and I have learnt over the past 7 months while building the Open Source GitVersion command line tool by showing practical examples from multiple other open source projects, including nServiceBus and TestStack.White.
Not a .NET guy? No worries, everything in this talk is about SemVer, Git and releasing software irrespective of platform or language.
39. • Supporting old releases is built into
the workflow
• Very structured which needs to be
followed
• Releases require rebuild
• Encourages Waterfall
• Simple
• Mainline development
• Supporting old releases is
difficult
Which branching strategy?
GitHubFlow GitFlow
About 8 months ago I was releasing new versions of a few open source projects I help maintain, like TestStack.White, shouldly and Markpad. I was finding each project had a different way of versioning and creating the release notes, so I started playing around with different ideas for TeamCity configurations, build scripts, using version.txt files to try and simplify versioning and allow me to just release a new version with a click of a button in TeamCity. Around that time I had a few gists and blog posts discussing different ways I could think of solving the problem, but was not really happy with any of them.
Then Simon Cropp (who is responsible for Fody) pinged me with an idea that Particular who are known best for nServiceBus had been working on. They would use a git branching strategy called GitFlow to infer and manage their semantic version for all their projects.
This was pretty cool, I started playing with it, but found that GitFlow was too heavy for simple open source projects which use pull requests targeting master. I simply did not want to increase the barrier to entry on these project.
I talked to Simon and Andreas (another guy at Particular) about contributing support for GitHubFlow to their little tool. After discussing it for a while we found the initial requirements and the way we were going to use it differed. So we decided to split, having a separate project for each branching strategy allowing each project to come up with it’s own ideas and ways of doing things with the intention of bringing them back together later.
At the time I was thinking, this will be really simple, just a simple console app and I will have the problem solved in a few weekends. Why are things never that simple..
After about 5 months or so both projects had matured, we had learnt a HEAP about semantic versioning and how we could use branching to control that version and we started to merge the projects.
8 months after this all kicked off we are about to finally release version 1 of GitVersion.
This talk is not about the tool, but what I personally have learnt over the last 8 months building this projects.
Before getting started, I mentioned GitVersion just before. Much of this tool is about the conventions we have built into GitVersion, but all of this can be applied separately
Each part has a specific meaning, first explain the concept of the public API
Major – Breaking/incompatible API change
Minor – Add functionality in a backwards compatible manner
Patch – Fixes bugs in a backwards compatible manner
Pre-release Tag – a ‘.’ Separated tag denoting a pre-release version which may not satisfy the intended compatibility requirements a stable version would
Build metadata – Stuff which does not affect the actual semantic version, but can give additional information to the consumers of that package
Example: Web app + Facebook library + twitter library both rely on Json.net
Version lock – when dependency specification is too tight, two libraries rely on a single dependency. If we rely on exact versions then we can get into a situation where no dependency can be updated
Version promiscuity – when the dependency specification is too loose and libraries start assuming they can run against any version of a dependent library, so upgrading any dependency can introduce compatibility issues between your dependent librarys
Note: strong naming in .net causes version lock, and we need binding redirects to break the version lock.
When you have multiple releases in flight at once it becomes hard to tell what the version for each branch should be.
Some people have nightly or CI builds, each CI build creates *the same* semantic version. This breaks the CI/Nightly build systems because there will be multiple packages of the same version. How do we deal with that?
Customer facing extensions
Major – New features, if this is a paid product then major version bumps often involve users paying a fee
Minor – Feature enhancements
Patch – Bug fixes
Beta still has the same meaning, it is a pre-release of the version before the -
Lets say we have a bunch of features on our backlog, after adopting SemVer you start thinking about the impact of that feature BEFORE working on it.
For libraries:
- Is this feature going to break our public API, most of the time you know this up-front.
- If you are going to introduce a breaking change, does that change your priorities?
- Maybe we want to switch to stories which will introduce breaking API changes so they are all in a single breaking release rather than multiple breaking releases over time
- Can we think of a way to introduce this feature without breaking something
- Is the bug we are fixing a security flaw, or serious enough to patch previous versions? (depends if you are supporting old versions)
For products
- Is this a feature our customers would pay for?
- If it is, maybe we want to start on the path of a new release
- For patch releases, do we need to fix in previous releases and do multiple patch releases?
If you are maintaining multiple versions, a patch release has implications. If it exists in previous versions then you have a duty to fix it
This has impacts on the development time and other things, so if it is not critical it can be fixed as part of the next minor release
This leads right into how do we manage these decisions in source control ->
Who uses git here?
Who knows what ‘GitHubFlow is’?
What about using pull requests?
Anyone here understand GitFlow?
And out of those, how many are using GitFlow?
As a contributor, I do not have write access to the main repo
So I create my own fork which I can write to.
Pull Requests are simple a request to get your code pulled into the main repository which you do not have access to (in the open source model).
That is pretty much it for GitHubFlow, there is another step to get your local repository up to date with upstream once your pull request is merged but that is getting a bit out of scope
We now know how to use GitHub (Stash, BitBucket work the same)
There are currently two ways to bump the version number. The first is by using a NextVersion.txt file in your repository root. This allows you to bump the major/minor easily. Once the conventions built into GitVersion pass the version in the NextVersion.txt file, it will be ignored
Branch name conventions work just like GitFlow, when you want to work on a feature or breaking change off the mainline just work in a branch which is suffixed with -2.0.0 or whatever version you want. When that branch is merged the version in the branch name will take over when building the target branch
Other ideas: Special commit messages, like prefix ‘Breaking:’ to the commit to have the major version bumped. If you like this idea, jump online and +1 the issue.
If you are following GitFlow, you only commit bug fixes to release branches
Then we merge the bug fixes across to develop as they are made
What is the issue when SemVer is mixed into this?
When using GitFlow with semantic versioning this doesn’t work so well for major version bumps because we are trying to keep the breaking changes separated until they are ready to be released.
When a commit is made on develop, that is a non-breaking change by it’s nature (only minor changes are made on develop)
So it can be safely merged into the major release branch. We have taken a branching method which fits a more waterfall style company/process and are using it as a too for devs to manage their versions.
We learnt this.. Now that you know the workflows it is not that bad.
On top of this, we learnt how you would use GitFlow when using Semantic Versioning.
Now that you know each branching strategy, when would you choose each?
Another thing to note is ALWAYS start with GitHubFlow, when you need more only then think about switching. Using GitVersion.exe
Now we have covered branching strategies, what they are and when you should use each. This means we are ready to move onto continuous delivery.
What does continuous delivery have to do with Semantic Versioning anyways?
A lot, most versioning strategies require a commit or some manual work AFTER a release to start building the correct version. Lets look at a few other solutions
An important part of continuous delivery is the release pipeline, or the idea that you build packages which are ready to deploy to production.
To get to production they have to go through a number of steps, they could be manual testing, automated testing, sign off or anything else. The important part of this is that the first step of the chain creates the artifacts. And they are not re-built for different environments, the same binaries which got built and deployed into a testing environment can just be deployed into production.
This is the hard step with semantic versioning. Being able to:
- Not have to touch your build server configuration or create a commit as part of your workflow. It is exceptional circumstances which make you step in
- For example, both GitHubFlow and GitFlow support both increment the Minor after a tag
- No nice migration path between a simple and more complex branching strategy
- Making pre-release packages easy to create, not a special or manual process
- Guidance, every project has to solve these issues. I have seen HEAPS of different approaches and I have invented quite a few. This is a reusable approach which is really easy to setup and start using
- Make the problem about the SEMANTICS of the release, NOT the version number. The version number is an artifact of what type of release it is.
Example simple build chain
Another, showing off the artifacts from the first step being used in the second step
In conclusion semantic versioning is great. It solves dependency hell, allows you to communicate either breaking changes or new features to consumers of your library or software and encourages people to think before breaking their consumers.We have learnt the GitHubFlow and GitFlow branching strategies and how they relate to semantic versioning.And finally we have seen how you can use your branching strategy to assist you in achieving continuous delivery.There is no reason for you not to start using SemVer today, change the way you think about what different features will do to your system and how you will implement them and with GitVersion to help remove the hard manual work out is a no brainer.