8 New Steps for Fixing Other People's Code

Way back in mid-2007, when Rails 1.2 was the new hotness and GitHub was still a
year away from crawling out of the primordial internet soup, prolific open
source contributor Dr Nic wrote an article
titled “8 steps for fixing other people’s
code”.
It offers excellent general advice, but the workflow details are clearly a
product of their time: RubyForge, SVN, and .patch files feature heavily.

Here in the fantastical future world of 2012, while we still don’t have
hoverboards or household nuclear
fusion, we do have some great tools that make fixing other people’s code a lot
easier. Join me as we rewrite “8 steps” for the modern age!

Open source projects are usually far from perfect. In many cases they started as
something the author threw together to solve a problem, then released into the
wild just in case it could be useful to someone else. Open source contributions
come from regular people – just like you! – who start using the project and
run into a bug that annoys them, or get an idea for a feature that would make
their life easier.

The experience that prompted me to write this post started with finding a bug in
Chronic, a date-parsing library. The issue
is described in the pull request
I submitted to fix it (pull requests are the final step in this process; we’ll
get to them in a bit).

In recent years git and GitHub
have emerged as the de facto standard, if not for open source projects in
general, then at least for open source Ruby projects. Lucky you! In Dr Nic’s
time it was all SVN, uphill both ways.

If your target project has a web site, it will probably link to the official
GitHub repository. Failing that, you can search
GitHub for the name of the project. This may also
turn up other people’s forks of the project – if you’re not sure which one is
the original, just pick one and follow the “forked from” links.

Once you’ve found the repository, scroll down to the README and scan it for “How
to Contribute” directions. Some projects have a particular process they want
contributors to follow, and it might be different from the steps outlined here.
Chronic’s instructions are
typical and straightforward.

Forking a project creates your own personal copy of the repository that you’re
free to modify however you want. It’s integral to the GitHub workflow, and is
therefore stupidly easy: Just hit the Fork button.

Copy the Read+Write link from your newly-forked repo and clone it down:

Many projects use automated tests to ensure that bugs stay fixed and new
features don’t introduce new bugs. Running a project’s tests is also a useful
sanity check, since no tests should fail on an unmodified clone of the
master branch.

Before doing this, you’ll need to install the project’s dependencies – if it
has any, it should include a Gemfile. Use gem install bundler to install
Bundler if you haven’t already, then simply bundle install.

In most Ruby projects, running “rake” with no arguments will execute the full
test suite. Ensure this doesn’t give you any failures before proceeding.

Come up with a short, descriptive name for a branch that will contain your
changes. Here’s my Chronic branch:

git checkout -b handle-sm-sd-with-time

Now you’re ready to start coding! Assuming the project has tests, you can do
some TDD and start by
writing a failing test. Poke around the existing test suite – it likely
involves Minitest or
Rspec – then write one or more new tests that would
succeed if your feature existed or your bug was fixed.

Re-run the test suite to ensure your tests fail (and you didn’t somehow break
any other tests). Then, write the code to make them pass! Follow the style of
the existing code as much as possible, and resist the temptation to “clean up”
or reformat things that aren’t relevant to your change.

Many libraries have Rake tasks applicable to this step. rake console is a
common task that opens an IRB shell with your in-development copy of the library
loaded into it. If the library is made available as a gem, there will be a task
that builds the gem (e.g. rake build), and you’ll want to make sure this still
works after you change things.

Note that while the changes I made to
Chronic were in multiple
commits, you should really put both the tests and the code that passes them into
a single commit. Any given commit should pass all the tests that exist within
it. You can still make multiple commits locally – just squash
them
later using git rebase -i origin/master.

If you’ve added a new feature or changed how something works, and the project’s
documentation lives in the repository (this is usually true for at least the
README file), update it to be consistent with your changes. Documentation never
gets enough love, and the owner will probably appreciate your thoughtfulness.

Pull down any changes made to the original repository since you started working,
and replay your commits onto the updated code. This ensures that your changes
can still be successfully applied to the latest version. It sounds complicated,
but the commands are quite simple:

Here’s the other half of the fork-based GitHub workflow. After making changes in
your fork, you request that the original author pull those changes into
their repository.

First push the branch containing your commits up to GitHub…

git push -u

…then hit the Pull Request button on your forked repository.

Set the “head branch” on the right side of the pull request to your new branch.

You’ll be prompted to give your pull request a title (e.g. “Add support for
XYZ”) and detailed description. Fill these in, and hit the Send pull request
button!

Now comes the hardest part: Waiting. The owners of the repository will be
notified of your pull request, and at some point – hopefully – will get around
to reviewing it. They might suggest some changes: Make them in your branch and
git push, and the pull request will automatically update with your new
commits. If your changes look good, they’ll be officially merged into the
project. Success!

After you’ve done this once for a given project, the process is simplified: When
you’re ready to contribute again, git pull upstream master so you’re working
with the latest code, and start again from step 4.

While the GitHub web interface is more accessible if you’re not used to the
workflow yet, it’s actually possible to do all this without leaving your
terminal. Check out the hub tool, and more
specifically the commands dealing with forks and pull
requests. In fact, my Chronic pull
request was originally a bug report that I later “converted” using hub.

Remember these steps are more like
guidelines than actual rules. Every
project is different, and the owners of the one you’re working on might prefer
you do things a bit (or a lot) differently. Be receptive to the feedback you get
on your pull requests, even if it seems nitpicky. You’ll be hailed as a model
open source citizen in no time!