Stanford runs an instance of the OpenEdX code base that a team of engineers actively develops. We maintain our own fork at and we try to keep our fork close to edX master by merging monthly from edX’s main edx-platform repository ( We prefer to land code through pull requests (PRs) to edX’s main repository, but sometimes that isn’t possible, as we periodically build custom Stanford-specific features. Through iteration, we’ve adopted a clearly defined release process that accommodates workflows for both merging from the main edX repository and keeping our own separate master branch. Since we are running a website with a large number of users, we think that stability of releases is paramount. This means that we try very hard to keep bad code out of the release branch, and the release process facilitates that.

As we submit PRs to edX as well as to ourselves, we need to have access to edX’s repositories from our development environments, so most of the developers at Stanford run git environments with at least two remotes (or sometimes more with personal remotes and those of colleagues): origin, the Stanford code repository; and upstream, the edX code repository, from which we can check out the master branch to make PRs to edX. We also use this upstream remote to merge code changes from edX during our release process.

This is where I saw the need for a diagram that explains the release process. I am a visual person, and drawing something out is how I understand things. Here is my diagram to help describe our development and release workflow:

It’s especially useful because our developers take turns being responsible for releases in our rotating “Release Master” system. The diagram keeps our release cycle consistent between releases and minimizes mistakes.

Here is a release plan using the diagram as a guide:

1. First we create an rc branch off of our master branch, taking any development that has been merged into master since the last release.

1a. If we want to merge the upstream release branch into the rc branch, we do that here.

At this point we have a release candidate that we can test and, once we are confident it won’t break anything, then install onto our production machines.

2. Now we merge branch rc into branch release (this should be a simple fast-forward; if it isn’t, the previous release had an issue!).

3. Now the release branch (and the merged upstream code) can be merged back into the master branch to keep both branches in sync with each other.

Notice that feature branches are created off of master and merged back in to be released in the regular release cycle. Hotfixes, on the other hand, are branched off of release, merged back into release, immediately pushed to production, and then merged back to master to maintain synchronicity. Also notice that release changes and upstream merges don’t make it into master unless they are deemed safe after testing, which insulates master from bugs as well.

And that’s it! We use this branching model for every forked repository we maintain, which keeps everything consistent and predictable.