Skip to content

Instantly share code, notes, and snippets.

@JAMSUPREME
Last active February 1, 2019 21:10
Show Gist options
  • Save JAMSUPREME/2f4733e041bc13ff3f24caac7beca7fc to your computer and use it in GitHub Desktop.
Save JAMSUPREME/2f4733e041bc13ff3f24caac7beca7fc to your computer and use it in GitHub Desktop.
Gem and app version mgmt

Overview

The purpose of this gist is to explain some recommendations for managing versions for gems and applications. Specifically, the concepts will be the following:

  • How do we version? What are the conventions for branches and releases?
  • When do we tag? Can I tag if it is not a release?
  • When do we branch, and how long should that branch live?
  • What is upstream and what is downstream?

By the time we reach the conclusion hopefully you've discovered the answers to all of these questions!

Terms

  • Version - An explicit version of a product. For example, v1.0.0, v1.0.1-beta, v2.1.765321
  • Semver - Semantic versioning. Please read http://semver.org/ - it's a quick and useful read
  • Tag - A pointer to a git commit. Its name (e.g. v1.0.0) adds semantics that help us know what that commit represents.
  • Release - A semantic tag. It just means "This tag is a release of our product". It is possible that you have tags that are not releases. Someone might want to tag a particular commit as cloud-1.0.0 to denote some commit in which you began a bunch of cloud features, and you don't really know when or where it will come back into a release.
  • Branch - A branch is simply a living repository of code. It could start from a tag, SHA, or another branch. Master is the primary, always-alive branch.
  • Upstream - Bleeding edge
  • Downstream - Safe and stable
  • Communication - The act of talking to other developers when you are both working on the same product. 😏

Recommended practices

  • Move upstream if possible
    • If you have a new feature, you should implement it for the oldest version in which you are planning to implement it
    • If you have a bugfix, implement it in the oldest version and merge it upstream (into newer version) - This makes merges cleaner and you don't have to worry about purging new code after you've merged a feature into older code. cooler-header-tree
  • If not, move downstream (backport)
    • If you absolutely MUST start upstream, you will very likely have to deal with merge conflicts or do a complete rewrite of that feature when moving it downstream.
  • If it's in production, it is 1.0.0!!! (don't follow in NodeJS's footsteps)
  • Coordinate with others when working within a major version.
    • This is important since within a sprint someone might be working on a few bugs (v1.0.15) while someone else wants to add some features (v1.1.0) - In this case, the v1 branch should branch out into two child branches v1.1 and v1.0.15, and whoever fixes bugs will be responsible for pushing them upstream. (You'll note this is exactly the same way we handle major version branching)
    • In order to make sure that bundle update works as expected, you should update version.rb with a pre-release suffix for every commit until you are ready to tag that version, for example:
      • 1.0.15-pre1 (fixing bug 1)
      • 1.0.15-pre2 (fixing bug 2)
      • 1.0.15 (ready to tag)
      • Tag the commit as v1.0.15 (the version.rb omits the v)
      • This helps us avoid bloating versions when we know several bug fixes will go out in a single sprint!
    • The question might arise "What if there are two features?" - in which case I think both would occupy the same minor version. The difficulty arises if we decide that one feature has to be excluded, in which case I think we simply revert it (if the features overlap, it will have to be decided on a case-by-case basis)
    • TODO: img and link in cooler-header
  • Release notes should be concise and accurate
    • In my sample app the changes are trivial, but a real gem might have several corrections.
    • See examples for the following:

Gems

  • Always branch for a major version (prior to development)
  • Make releases/tags on the major version branch (prior to merging into master)
  • Master will always look like the latest major version tag (and after each major tag we should PR into master)
  • Bugs should be made as far downstream as possible, then pushed all the way upstream (tagging each), and after the final upstream merge you will tag, then PR into master.
    • Example: Better header patch
    • The sample above will show that this commit made its way into both major versions
    • The same concept applies to features. (e.g. Ruby 1 code probably works fine in Ruby 2.3, but not vice versa) - There are some scenarios in which you're stuck with a rewrite anyway so it doesn't matter which version you use as your base.

Apps

Apps have to be managed slightly differently (in order to be compliant with our contract)

Therefore, I think this would be an ideal approach:

  • We have master and sprint-x branches (sprint-1, sprint-2, etc.) - This could be date based if that sounds better (sprint-07-07-16)
  • We do all development on the sprint-x branch, and deploy to all non-prod environments from that branch. Once we are prod ready, we merge sprint-x into master and tag it with a version (v1.0.0)
  • If a hotfix has to go out before the next sprint, then a short-lived branch v1.0.1 should be made from v1.0.0 and patches immediately addressed. The patch should also be pushed upstream into sprint-2 (assuming prior sprint was sprint-1)
  • It would look something like this (see repo for more): All my merges into master were pretty simple, but in our case it might be a good idea to decide on a naming convention for PRs. app-branching

Note: Reena expressed some concern that we can't actually have branches alive even for a sprint. If that's the case, all new code may need to be toggled in some manner.

Toggle-heavy recommendations:

  • Use flip (both session and db-level, defaulting to false)
  • For new features, include the code and toggle them on/off - Then remove toggling code after it is stable
  • I'm open to any other ideas?

Demonstration

I made a little playground, and in it you can see why I recommend the above practices. The app itself is trivial, but the code isn't as important as how we view the versions, dependencies, and how the code is managed in every location.

  • Cool app (repo)
    • Uses the glorious gems listed below. Exists to give you a feel for how a client is impacted with gem modifications (and how the product version gets bumped accordingly)
  • Cool footer (repo)
    • Doesn't follow upstream/downstream recommendations
    • Doesn't keep major version branches alive (just short-lived branches that get tagged)
  • Cooler header (repo)
    • Follows upstream/downstream recommendations
    • Keeps major version branches alive

Other things

I intentionally avoided the following:

  • Our pipeline (needs improvement)
  • Gem hosting (Nexus probably needs more setup for hosting)

Conclusion

Let's address the points we tried to tackle and hopefully you've answered them all:

  • How do we version? What are the conventions for branches and releases?
    • Following semver (major, minor, patch, with -pre-release until it's ready for a tag)
    • Branches - Only major version (e.g. v1 or v2)
    • Releases - Semver, ideally without -pre-release suffix (e.g. v1.2.55)
  • When do we tag? Can I tag if it is not a release?
    • Whenever flagging a release or an important snapshot
    • Yes
  • When do we branch, and how long should that branch live?
    • Gems - Branch immediately when development begins. The branch should be alive as long as you are supporting that version
    • Apps - Branch immediately when development begins. The branch should be alive until the sprint ends.
  • What is upstream and what is downstream?
    • Upstream means "newer", and "downstream" means older. It's just a handy way to classify the direction your patch or feature is moving. My recommendation is that everything moves upstream, but this is just a rule of thumb.

v1.0.1 released!

We've released v1.0.1 - It includes a handful of small patches.

Patches

  • TIX-1001 - Fixed header text - (optional git SHA or PR)
  • TIX-1405 - Fixed buggy workflow - commit
  • TIX-807 - Fixed button color - (optional git SHA or PR)

v2.0.0 released!

We've released v2.0.0 - It includes huge internal refactors, upgrades several dependencies, and breaks any clients using our client-facing API. There are also a few new features and patches.

Backwards incompatibilities

  • The client-facing API has been renamed
  • Ruby version has been upgraded, as well as several gems. All of our old code will no longer work for our deployment target.
  • Huge refactors that make most of the codebase unrecognizable

Features

  • TIX-2328 - Image upload

Patches

  • TIX-2577 - Fixed strange gif
@JAMSUPREME
Copy link
Author

Header tree
cooler-header-tree

@JAMSUPREME
Copy link
Author

App branching
app-branching

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment