What’s Beeminder?

I’m a huge fan of Beeminder. It’s a goal-tracking app that turns a goal like “write more code for project X” into a metric-driven habit like “make 5 commits per week to project X,” which in turn implies an up-and-to-the-right graph of commits over time. The twist is that committing to a specific rate of progress means making a contract that you will achieve that progress. On the graph for your goal, if your datapoints ever lag behind the upward linear trend to which you’ve committed, you have violated your contract, so you pay money to Beeminder. (At this point, depending on personality type, most people experience either curiosity at the possibilities for technology-mediated behavior change, or bemusement coupled with a firm conviction that this is not for them. If you fall into the former category, read on.)

My "run twice a week" goal
My "run twice a week" goal

I like Beeminder for a lot of reasons: its recognition that many “goals” are actually best modeled as “habits,” its emphasis on regularity (“it’s been N days since you last went on a run”), and of course its function as a commitment device.1 My faith in its ability to improve my behavior comes not only from my past 4 months of use, but also from the fact that its features include those of many (less sophisticated) habit-change tools I have built for myself.2

Automating goal data

By default, a Beeminder goal is just a self-defined metric (pages read, calls made, tasks completed) plus self-reported data on that metric. This is what most of my goals look like, and as long as the metric doesn’t leave much room for post-hoc interpretation, self-reporting mostly works.

The best Beeminder goals, however, are those whose data tracking is automated: in other words, doing the pledged task is equivalent to reporting it to Beeminder. For example, data for my “run regularly” goal comes from RunKeeper, so I log data to Beeminder simply by tracking my run as I normally would. I would like this sort of data pipeline for my git commits: make a commit, generate a Beeminder datapoint.

Unfortunately, even though Beeminder includes GitHub among its third-party integrations, most of my projects aren’t hosted on GitHub. The next best thing? A script which uses the Beeminder API to submit a datapoint whenever I make a new commit to my local git repository.3

A post-commit hook

Every git repository can be customized with hooks, scripts which are triggered when certain events happen, such as committing or pushing. A common use is to add a pre-commit hook which validates the contents of a commit (by running a linter or style checker) before it is created. For my use case, the post-commit hook, which is run after a commit is finalized, lets me add submit-to-Beeminder behavior at the moment of divine creation:

$ git commit -m "Refactor animation loop"
Post this commit ("mtr: Refactor animation loop") to Beeminder goal "art"? [y/n] y
Posting to Beeminder...
{"timestamp":1422739132,"value":1.0,"comment":"mtr: Refactor animation loop",
  "canonical":"31 1 \"mtr: Refactor animation loop\"","daystamp":"20150131"}
[master 3f68571] Refactor animation loop
 1 file changed, 70 insertions(+), 30 deletions(-)

The code powering this hook is an absurdly simple Bash script which makes an HTTP request to the Beeminder API. It boils down to:

curl https://www.beeminder.com/api/v1/users/$USERNAME/goals/$GOAL/datapoints.json \
  --data "auth_token=$AUTH_TOKEN" \
  --data "value=1" \
  --data-urlencode "comment=$(git log -1 --pretty=%B)"

My final version wraps this curl command in a prompt and adds some abstraction to support using the code from multiple git repos.

Get the code!

Clone my git-to-beeminder repo from GitHub and follow the “Quick start” instructions in the README.

For each repo whose commits you want to count towards a Beeminder goal, you should add a .git/hooks/post-commit file that defines your Beeminder options (username, goal name, API key) via environment variables and then calls the git-to-beeminder.sh script.

One optional configuration value, BEEMINDER_MESSAGE_PREFIX, allows you to specify a string which will be prepended to the comment for your Beeminder datapoint. I use this to count multiple git repos towards the same goal, while still being able to trace the provenance of a given datapoint:

2015-01-31 1 "mtr: Refactor animation loop"
2015-01-22 1 "sfstreetquiz: Add more neighborhoods"
2015-01-22 1 "sfstreetquiz: Remove compass, add street name axis, better start transition"
2015-01-18 1 "mtr: Slowly increase transparency and width of lines as they expand"

Beeminder is rad

In my quest to improve my own behavior through technology, I’ve often gotten carried away setting up syncing infrastructure, architecting data models, designing visualizations, and dreaming up new interventions. The most liberating thing about Beeminder has been that they do the work of storing, presenting, and creating accountability for my goal data. That frees me up to focus on the more exciting work of deciding on goals, testing out (and automating) their metrics, and achieving them.

  1. One of my favorite posts on procrastination and commitment devices, which also happens to be an introduction to the motivation for Beeminder, is “How To Do What You Want: Akrasia and Self-Binding”

  2. In my Quantified Self talk on running, I alluded to a system which sends email alerts to my loved ones when too many days have passed since I last logged a running or stretching event. That system, and a couple half-finished “habit dashboards,” will be released after my death as soon as my biographer reaches my shitty-old-code folder. 

  3. I’m setting aside the debate of whether any quantitative measure of code should be used as a metric for achievement. In any case, a prompt before submitting to Beeminder allows you to judge whether a commit should be “promoted” to a datapoint that counts towards your goal.