Contributing to open-source software is harder than I thought!
Published Nov 9, 2019
I took part in Hacktoberfest this year! I successfully opened 4 pull requests on public repos in October (and will be getting that sweet participation T-shirt!), so I wanted to take a bit to reflect on the experience.
I found out about the event through an announcement from Gusto’s internal open source group. I then received emails from GitHub, DigitalOcean, and a few other services I use in my developer-related life. (I am the target audience for this event, after all! 😄) But the work announcement actually got me motivated. It came with a two hour hackathon every Friday for people to hang out and contribute together. That was a cool way to meet other people who are interested in the social aspect of software, especially since I’m relatively new at Gusto.
Finding a project
I’m not actively involved in any public software development, so the first thing I had to do was find a project to contribute to. This was actually much harder than I thought it would be.
Anyone who’s talked to me about computing knows that I get frustrated with the tools we use to do it. I’ve customized most interface components of my Linux desktop environment, I’ll argue about which argument parsing library is easiest to use, and don’t even get me started on keyboards! So I thought I would just scratch my own itches for a month and get a free T-shirt out of it. (Spoiler: I’m somehow only itchier.)
First, I tried to go to all the software libraries and frameworks I’ve depended on in the last year. The problem with that is that most things I was frustrated with were dependencies I used in my previous work at MemSQL. The dependent code itself is still closed-source, and I’ve been away from it long enough that I don’t actually remember the things I wanted to change.
Next, I went through my heavily-customized desktop environment fishing for something I could upstream. My dotfiles include almost every config-related change I’ve made since I got my second computer. It’s even got some stale files from when I tried any window manager that took less than an hour to set up. It turns out that I’ve worked around most of the issues I experienced over the years, either by playing with configuration or simply using different tools. Sometimes I think I’ve changed the way I use computers to fit the tools I happened to have at the time, and the tools and I are evolving together. (Neo)Vim and i3 definitely feel that way. Contributing to either of those projects isn’t easy, though. Getting a C development environment set up on my work laptop wasn’t something I wanted to spend a month on, especially since I haven’t written C for a loooong time. That applies to a lot of my desktop environment, actually, and “rewrite it in Rust” doesn’t get you any pull request points! 😛 On a real note, the features I want to add are usually out-of-scope for the project or explicitly against its philosophy. That would be PR spam even if I did manage to wrangle the code to do what I want.
The first pull request
Since leaning on my past and present wasn’t useful, I decided to look toward the future! The part of Gusto’s codebase that I work on is the backend and admin interface of a Rails app, so I’ve been learning both Ruby and Rails while I get ramped up on all the domain knowledge too. We use Rubocop to enforce a consistent style, and what better way to learn about a language and its conventions than poking around the linter?
I scrolled through the issues looking for something well-defined and narrowly-scoped, and there was a report of the linter making things worse! (To summarize, trying to get the default settings to accept some weird code would push code in an even weirder direction.) The issue also laid out a plan for fixing it: flip the default setting the other way. So I did exactly what it said and got my first pull request: rubocop-hq/rubocop#7424. It shipped in Rubocop 0.75.1!
But that was the end of easy linter issues I could use. Everything else (new cops, options on existing cops, etc.) required opinions on what Ruby should or shouldn’t look like, which I still don’t have.
The rest of the pull requests
At the next Gusto Hacktoberfest session, one of the organizers reminded me that the group had created a list of projects that needed some love. On that list was Gusto/committer, which makes it easier to use git pre-commit hooks for linters and formatters. One of my teammates helped me set it up during my onboarding, and it really helps with commit friction when our preferred IDE (RubyMine) has different auto-formatting than Rubocop likes. The project list suggested some improvements, and someone already did the add-a-license PR, so next up was adding a linter to the project. It’s linters all the way down!
When adding a linter to a project in any language, I’ve had the best experience when I can pin the version in the project’s development dependencies. That makes it possible to enforce it consistently, both for local development and continuous integration. Committer is written in Go, but the last time it saw active development was before the Go dependency management wars. It uses godep for dependency management, which, in addition to being deprecated, doesn’t have a way to vendor “unused” dependencies. Godep recommends switching to dep, which I’ve had good experience with, so I let myself start on a potential yak shave and followed along. It went well because dep specifically implemented an “import” from other dependency management tools. I think that’s standard among third-party dependency managers now (but still appreciated!). That became my second pull request: Gusto/committer#10.
Now I could actually add the linter! I’d used gometalinter in the past, but it’s deprecated too. That has everyone moving to its recommendation of golangci‑lint. (Go in 2019 sometimes feels like JS did in 2017, where putting it down for a few months makes you rethink everything you know.) So I installed it and tried running it, but it somehow failed to execute due to an import error that I’ve since lost to the terminal scrollback ether. I saw that the release had been done a few days prior, so I added a dependency constraint for the previous version. That worked! We now have a vendored linter!
When I went to open an issue on golangci-lint for the import error, I learned that it specifically doesn’t support being vendored! That’s very strange to me, given how much the Go community loves vendoring, but I figured it wasn’t worth creating issue spam. ¯\_(ツ)_/¯
Getting back to Committer, it’s bad form to commit with linter errors (especially if they’re your fault!), so I couldn’t open the PR just yet. There were some valid warnings about dead code, so now we have less code. There were other warnings from errcheck about unchecked errors, but they were about a Println variant, so it’s smarter to ignore them. After trying to format an errcheck ignore file every way I could think of, I eventually found an issue describing the same confusion with a comment that solved it. Since I was the third person to be confused by this, I figured I might as well be the one to document it. So I did, and got my third pull request: kisielk/errcheck#173
Okay, now that my yak shave had forked multiple times, I was finally able to run the linter with no complaints. And that got me my fourth pull request: Gusto/committer#11
In the background of this experience, I’d been making notes about how to get started working on Committer. I’m used to doing this for any project I join, mostly because I have a terrible memory and get distracted easily. As a side-effect, these notes can be useful for anyone in the future (especially me!), and it can reduce confusion around order- or state-dependent setup steps. I cleaned them up a bit and used that as my fifth pull request: Gusto/committer#12
I think it’s pretty cool that I got all four PRs I needed as part of a yak shave (I consider the Rubocop one the bonus PR now). In my mind, that’s exactly the way FLOSS development is supposed to go: you try to do something, you find an issue, you fix it yourself, and then you send it upstream for everyone else who comes after you. I had a lot of fun, and I’ll definitely participate in Hacktoberfest again!
I realize that I shouldn’t have waited the full week for my PRs to get reviewed, but I honestly forgot I was waiting for them until the last day that the Gusto OSS group met (which I missed due to a real work deadline). I’ll follow up on them internally though, since I can probably find the maintainer on Slack! 😆 Maybe I’ll have to take over as the maintainer, but that sounds pretty interesting, actually. It’s adjacent to another project that someone stuck in my mind a while ago: directory-dependent git pre-commit hooks.
I know that the game this time was to get PRs. That’s a great way to generate new contributor interest and experience in established projects, but I sometimes feel like sometimes FLOSS needs something different. There were some projects that needed issue triage way more than code or documentation. Some projects needed someone to take over as a maintainer, and sustainable maintenance is a topic I’ve seen discussed a lot lately. There’s no way to compare these things against a PR count (or judge their effectiveness within a week, really), so Hacktoberfest definitely isn’t the place for those. But maybe we could do something at another time of year, like Spring Cleaning for issue triage or Maintenance Madness where we put all the forks of an ancient repo in a bracket and crowdfund the most popular one for a year. Google seems to have claimed Summer of Code, but winter’s still open!
In the process of checking on my PRs, I found one that I didn’t recognize: graysky2/pulseaudio-ctl#38. This is a three-year-old PR that I made to a tool I don’t even use anymore! It got a thumbs up and a comment from someone else, so I hope the maintainer gets around to it at some point. Too bad this couldn’t count toward my four this year! 😂