Our Git Workflow

As happy git users, we love learning how other people use our favorite version control system. So, we thought we’d add to the discussion and share how we use git and other tools to drive our engineering work. We’ve been improving our git workflow for a few years, and our current solution is optimized around:

  • Easing work parallelization
  • Communicating who’s working on what
  • Allowing us to prioritize tasks
  • Allowing external stakeholders to view task progress
  • Preventing us from deploying un-QA’d code
  • Creating a clear path for engineers to take their tasks through the pipeline of development, testing, QA, all the way to production without blocking on work from others

We’ve been pretty satisfied with our setup – check it out, and let us know what you think!

Task Management

For task management, we use JIRA, both for its flexibility in configuration and because using different components of the Atlassian ecosystem together gives us some nice benefits (more on that below). We’ve found JIRA to be helpful not just for engineers but for designers, account managers, and anyone else involved with shipping a certain code change. (Relatedly, sometimes a JIRA task involves no code but could be something like “draft announcement to tell clients about feature.”) We try to make JIRA tasks as small as possible, using its many layers of subdividing (projects, epics, stories, subtasks, etc.) to get down to the point where a given task can be done pretty quickly. This keeps our code changes small and incremental, and allows us to parallelize our work across many people much more effectively.


We use feature branches for each JIRA task, so once we start work on a task, we create a branch for it. Since the JIRA tasks are small at this stage, we tend to have many short-lived branches that encapsulate small but concrete changes. This is great for us, because it allows us to have nice, clean code reviews (once per branch), as well as reducing the chances of a single change ballooning into a blocker for all the work we’re trying to complete.

Recent develop-branch history

Recent develop-branch history

We like to have fun with our branch naming – some recent examples:

  • feature/SAM-6-elementary-my-dear-watson
  • feature/SEL-144-call-the-children-in-for-dinner
  • feature/OPS-753-no-file-left-behind


Now comes the fun part: writing code! (along with another fun part: writing tests!). A big benefit to our local development has been the use of overcommit, which Automagically™ runs a battery of linters and static analysis checks on code before we commit or push it. In addition to finding security vulnerabilities and other serious issues, these checks enforce a uniform code style, so our code reviews don’t waste time with lots of nitpicky preferences. Thankfully, this also keeps these comments out of code review, unlike other automated tools.

Rewriting History

Once we’re done coding, we use git-rebase to squash our development commits into a single commit that mirrors the content of the original JIRA story. Some engineering teams don’t feel comfortable with rewriting history as part of their daily workflow, but we’ve found that it works really nicely to compress our work into small, meaningful chunks that are rebased on the latest shippable code. Even Linus Torvalds, Linux’s benevolent-dictator-for-life and creator of git, has chimed in to recommend squashing and rebasing before a PR as a nice way to keep your work clean and clear for others.

Speaking of pull requests, all of our code is reviewed by at least one engineer. This is great both for quality, as well as keeping everyone up to date on the changes that are being made. It makes our final product a joint effort, and thus promotes and develops shared responsibility and ownership across the team.

Code review in action

Code review in action

The Atlassian Walled Garden

The nice thing about using Bitbucket, JIRA, and the (soon-to-be-deprecated) Bamboo is that these Atlassian services all talk to one another nicely. For instance:

  • When a pull request is created, the status of the corresponding JIRA task updates to “code review” (powered by conventions in how we name git branches).
  • When tests pass or fail, an indicator appears on both the JIRA task and the Pull Request.
  • When a PR is merged, the JIRA status updates automatically again, to “QA,” and the code is pushed to a test environment.

The authoring engineer then verifies that the code works in the test environment (we take responsibility for the code we write) and updates the JIRA status to “Reviewed,” which allows our production deploy script to pick it up and deploy the change. We deploy daily, and so once the code is reviewed, it generally gets deployed the next day.

We’re always tweaking and improving our processes and technologies, so we’d love to know what you think of our workflow. Even better, we’d really love if you joined us in making these types of decisions with the goal of radically improving student outcomes – we’re hiring!

Related Posts
A way to change a foreign key reference with zero downtime
Toward a Swankier Rails Console
Support Engineer: Experiments & Learnings
Eliminating Nondeterministic (“Flaky”) Tests in Ruby and RSpec