Posts from February 2017

RC week 7

This is week 7 of being at the Recurse Center.

Day 23

Arrived ~10:00, departed ~21:45, total time at RC 11h45m.

Didn't get a whole lot done today, mostly because we were all welcoming the Spring 1s - I went out to lunch with some of them and participated in Code Dojo in the afternoon (Aditya and I solved the challenge in bash, which was pretty interesting an also hilarious[1]). However, I did do some maintenance on I did, however, start a project to display activity in 455 Broadway in realtime which I'm pretty exited about.

[1]: Aditya asked me "so can you just cast a string to an integer in bash or...?" and when I said no he looked at me and asked how in the world we could complete this task - not realizing that the correct question is, of course, "does bash have a type system?" The answer to which is no, no it does not.

Day 24

Arrived ~14:30, departed ~1:10, total time at RC 10h40m.

Spent most of today working on - I got the basic API working, so you can POST to the server and it'll handle expiry properly. Also spent a lot of time blogging for the Iron Blogger meeting (in which I got sentenced to use vim for the week instead of Emacs for not writing a post a couple weeks ago).

Day 25

Arrived ~10:30, departed ~19:10, total time at RC 8h40m.

Spent the day catching up on various things, notably putting out a new beta that backported some regression fixes. In the evening I spent most of my time finishing up a mid-year college application form before videocalling with a friend in college.

Day 26

Arrived ~10:35, departed ~1:05, total time at RC 14h30m.

Spent some time working on server stuff today. Other than that though I didn't do much of anything.


Arrived ~16:20, departed ~1:45, total time at RC 9h25m. As always, Friday doesn't count as a day because RC is technically not in session.

Worked on generator-stratic today. I fixed some bugs and disabled some functionality that wasn't complete (and was non-trivial to complete). Published a beta 0 in the evening.

Executive summary

This week was... okay in terms of productivity. It started off relatively good, slipped a fair amount in the middle, and then somewhat came back at the end of the week. I did, however, check off some semi-longstanding issues on my todo list. So that's nice.

Total time at RC 55 hours 0 minutes; cumulative time 294 hours 25 minutes.

How I passed 2k GitHub contributions

So the other day I logged into GitHub and saw something crazy:

Screenshot of my GitHub contribution graph showing 2,054 contributions over the past year

How in the hell did I end up with that many contributions? Well, I think I know why - it happened because of a couple related, small habits I have. I thought I'd share them with everyone else as a sort of trivia - maybe you can adopt these habits too! (Although hopefully because you think they're good ideas, not because you just want to make yourself look cool on GitHub.)

Fix typos

If projects are freedom-respecting, that means that anyone is welcome to contribute if they're able to offer something valuable. How many times have you seen a typo in some docs? GitHub makes it super, super easy to fix these (just click the pencil icon in the upper-right of GitHub file views), and they're a fantastic way to contribute a little back to projects. Even if you aren't reading docs on GitHub, it often doesn't take that much time to find where they're hosted (which is frequently GitHub anyway). Really good docs will even have a link to their source right on the generated page.

So whenever I see a typo, I send a Pull Request. It's become an automatic response to seeing something that needs fixing, and is pretty routine for me nowadays.

Oftentimes there are changes that aren't as easy as a typo, but are still super easy to fix. Take this Homebrew PR - I authored it on a tablet and it took me under 3 minutes to make the suggested change[1]. That's not a lot of effort, and it'll help lots of people looking for the Homebrew change log. The next time you find yourself thinking "it kinda sucks that..." ask yourself - can I fix this myself? And if you can, go for it! Even if you screw up, I promise most projects will want to help you out instead of making fun of you[2] - as someone who runs several freedom-respecting projects, I know that getting new contributors is very, very valuable and because of that I'll do a lot to make contributing a good experience. I would encourage everyone to, as Mike McQuaid puts it in that Homebrew PR, "be the change you want to see in the world."

[1]: of course, that's partly because I'm pretty good at git and grok things like rebase -i. But even if that's not you and it would take you longer, it sounds like a great opportunity to improve your git skills to me!

[2]: I feel obligated to point out that not every project is like this - there are some projects that have a terrible culture (*coughcough*Linuxkernel*cough*). Screw them. They're not that common in my experience and it's their loss, anyway. Don't let them deter you from trying to improve things outside of their bubble of suck.

File issues

I just talked about making small changes whenever you see something you can improve with a couple minutes' time. But what if you're in a hurry, or it would take longer?

Filing issues is a super easy way to solve this. Get into the habit of filing issues for everything that irks you[3] - you're not allowed to say "such-and-such a project sucks because it's buggy!" when you haven't told the developers about the bug you're experiencing!

Filing issues also takes very little time and is super helpful to the developer. Probably a lot of the people who read this blog are software developers, so I'm betting you can understand how great it is to receive an issue that gives you lots of details that you can then use to fix a bug or a design problem. So why not give the gift of issues? Just make sure to search for duplicates! (This accounts for a significant amount of those 2,000 contributions on my GitHub profile. Over the course of my time on GitHub, I've filed 619 issues, 438 of which were on other people's projects. Of course, that's just on GitHub - I also do this frequently outside of GitHub.)

Again, every time you find yourself saying, "why doesn't it..." or "I wish it..." or "it's so annoying when...", let the developer know! You'll help them make their project better.

[3]: this is not to suggest that you file ridiculous issues that don't really say anything or just complain. But a focused bug report describing something concrete that's problematic is awesome.

Publish by default

This is the last and biggest habit I have that I think led to those 2k contributions: I publish everything by default, no matter what. It doesn't matter how bad or hacky or ugly I think a software project is, I just publish it. Because honestly, why not?

Keeping projects public doesn't get in anyone's way. It's not like someone will look at your GitHub and be annoyed that you give so much software to the world. And there's even a chance that your hacky script may actually be useful to someone. Even if I think a project is "bad" I'll still publish it because the reality is that no one is going to go around GitHub specifically trying to find people to make fun of.

Publishing projects also gives me incentive to clean them up - write a README, write tests, and keep a change log (if relevant). My standards are higher because I know all my work is all in the open - not because I'm afraid of other people telling me I'm doing it wrong, but because I want to create high quality code that has a better chance of being useful to the wider community.

In summary

All of these habits are related. The tl;dr is this: engage by default. Get used to being involved instead of shrugging off a problem and moving on. Put your work out there for other people to see. The open source/freedom-respcting software community belongs to everyone - and that means we can all contribute to improving it. We just have to start.

From static to Stratic - part 1

So a couple days ago I published generator-stratic@1.0.0-beta.1 to npm. Since Stratic is now officially in beta, I thought I'd write up a guide to converting a regular, static site to a Stratic-powered blog.

Each step in this blog post (part 1 of 2[?]) will take you closer to having a fully-functional blog, but because of Stratic's decoupled design, you can work through them at your own pace. Each step will leave you with a functional environment (i.e. nothing will be "broken" such that you can't work on your website anymore).

You can see the steps in this post in action at straticjs/static-to-stratic. Each commit corresponds to a step in this post.

Let's get started!

Initial setup

The site we'll be converting is currently pretty simple. It has an index.html and a projects.html. Each of these includes /css/main.css and /js/main.js. Also, they both have a navigation section and a footer that are duplicated across each page. Each time Alyssa P. Hacker - the website's owner - makes a change to these (for example to fix the copyright year in the footer), she has to change both HTML files. The best way for her to add a new page will be to copy an existing HTML file and then change it. This is a little unideal.

Alyssa tracks her website on GitHub (in the example repository mentioned above). Here are links for the index.html and the projects.html we'll be working with.

Here's a visual of the project layout:

% tree .
├── css
│   └── main.css
├── index.html
├── js
│   └── main.js
└── projects.html

2 directories, 4 files

When Alyssa needs to preview her website, she manually runs http-server ..

Since Alyssa uses GitHub she publishes her website on GitHub Pages, so her website is in the master branch of her git repository. (Here we're assuming that the repository is called or something, instead of static-to-stratic.)

In addition to adding blog support, we'll improve Alyssa's website by reducing duplication while still allowing her to publish to GitHub Pages.

Step 1 - adding gulp

Before we do anything else, we need to add a build system. Stratic is designed to work with [gulpjs][], so that's the one we'll be using.

Adding gulp is super easy. First, we need to create a package.json, so we do npm init:

% npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (static-to-stratic)
version: (1.0.0)
description: Personal website of Alyssa P. Hacker
entry point: (index.js)
test command:
git repository: (
author: Alyssa P. Hacker <>
license: (ISC) AGPL-3.0+
About to write to /Users/alex/Development/static-to-stratic/package.json:

  "name": "static-to-stratic",
  "version": "1.0.0",
  "description": "Personal website of Alyssa P. Hacker",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  "repository": {
    "type": "git",
    "url": "git+"
  "author": "Alyssa P. Hacker <>",
  "license": "AGPL-3.0+",
  "bugs": {
    "url": ""
  "homepage": ""

Is this ok? (yes) yes

A couple things to note here: in general, the defaults are fine to accept. We've provided a description and an author, but these are optional since this isn't actually going to be published on the npm registry. They're just kind of nice to have.

The same goes for the license, which in this case is the Affero GPL 3.0 or above - however, as the copyright holder you are of course free to choose whatever license you want. (Or no license, although I'd discourage you from doing that.)

Once we have a package.json, we can go ahead and install gulp and another module we'll need, ecstatic:

% npm install --save-dev gulp ecstatic

If you haven't used gulp previously, you'll also need to install gulp-cli:

% npm install -g gulp-cli

At this point, we'll need to move some files around. Now that we have a build system, we can organize our repository however we want instead of putting stuff exactly where we want it in production.

You can do this however you want. The organization that you'll find most projects using, though, is to put stuff in a src directory. Let's make that right now.

% mkdir src
% git mv *.html src
% git mv css src/styles
% git mv js src/scripts

Finally, create a file named gulpfile.js and put the following in it:

var gulp = require('gulp'),
    http = require('http'),
    ecstatic = require('ecstatic');

gulp.task('build:html', function() {

gulp.task('build:css', function() {

gulp.task('build:js', function() {

gulp.task('watch', ['build'], function() {'src/*.html', ['build:html']);'src/styles/*', ['build:css']);'src/scripts/*', ['build:js']);

gulp.task('serve', ['watch'], function() {
                ecstatic({ root: __dirname + '/dist' })

gulp.task('build', ['build:html', 'build:css', 'build:js']);

gulp.task('default', ['serve']);

This gives us a pretty good starting point. This gulpfile defines a couple tasks that simply copy source files into dist. The watch task watches for changes and rebuilds when they occur, and the serve task starts up a server, replacing Alyssa's usage of http-server. This provides exactly the same workflow as before: Alyssa runs one command and then she can look at her site on localhost:8080. You can use different task names if you want (for example, html instead of build:html, etc.), but these are what generator-stratic gives you.

However, there's one problem: Alyssa can't deploy her site anymore. If she pushed like this, visitors would have to visit e.g. instead of! That's no good.

In order to rectify this, we'll create a new git branch, src. src will contain the source files, and we'll put the final, built site in master, which is what's served by GitHub Pages. So:

% git checkout -b src
% git push --set-upstream origin src

Great. Now, we need to add something to put the built files (i.e. the contents of dist) in master. We'll use the gh-pages module for this. First install it and a dependency we'll need:

% npm install --save-dev gh-pages gulp-util

Next, make it available in the gulpfile by adding a line at the end of require() statements:

var gulp = require('gulp'),
    http = require('http'),
    ecstatic = require('ecstatic');

And finally, add a deploy task somewhere in the gulpfile:

gulp.task('deploy', ['build'], function(done) {
    ghpages.publish(path.join(__dirname, 'dist'), { logger: gutil.log, branch: 'master' }, done);

Now whenever Alyssa wants to deploy a new version of her website, she just runs gulp deploy and it'll be taken care of for her. (ProTip™: change the default branch to src on GitHub. That way visitors and new clones see the source files, not the build files generated by a program.)

The very last thing we need to do is add a .gitignore file since we're installing Node modules and have a build directory now. We'll just use GitHub's, adding a line for dist/ at the end:

% curl > .gitignore
% echo "\ndist/\n" >> .gitignore

Now we've got a functionally equivalent development setup based on gulp. Success!

Step 2: converting HTML to Pug

The next step is to convert the HTML to Pug. Pug (formerly known as Jade) is a language that compiles to HTML. It lets you do useful things like inherit from a common layout as well as looping over JavaScript variables. If you're not familiar with Pug, you should go take a look at its syntax now.

The easiest way to do this conversion is to get a program to do it for you. Here's the one I used way back when; you may be able to find a better one. The generated Pug will be valid but not the prettiest - you may want to wait to clean it up since we're going to do some work to reduce the duplication soon.

Once you've got the converted Pug, you should rename the relevant HTML file to have a .pug extention, then replace the contents with the Pug. Do this for each HTML file.

The last step here is to make gulp build the Pug. Install gulp-pug:

% npm install --save-dev gulp-pug

Then add pug = require('gulp-pug') to the end of the var declaration at the top of your gulpfile. Finally, change your html task to look like this:

gulp.task('build:html', function() {
        .pipe(pug({pretty: true}))

We'll also need to fix the watch task so it has:'src/*.pug', ['build:html']);

which will watch Pug files instead of HTML files.

That's it! Alyssa's site is now building with Pug instead of HTML.

Step 3: splitting out the layout

Pug's looping and flow control constructs will be very useful to us later on, but we can get some immediate productivity wins by splitting out the site layout so it's not duplicated across every Pug file.

There's one tricky thing about this: the navigation is mostly the same between pages, but not quite - the page the user is currently on shouldn't be a link. We'll solve this by using a block directive for each link. That way, we can override just what needs to be changed, while introducing no duplication.

You'll have to figure out exactly what parts of your personal layout make sense to be split out. In Alyssa's case, there are three main things that are mostly or fully duplicated across pages:

  1. The navigation bar
  2. The footer
  3. Invisible metadata and script/style includes

These are what we'll split out. First, we'll make a copy of index.pug and put it in src/includes/layout.pug. (Again, you can organize your files however you want - but in projects generated by generator-stratic, utility Pug files go in src/includes.) Next, edit out the page-specific content and replace them with block directives. Finally, edit each navigation bar item so it has its own block directive, leaving the old code as the default for the block directive.

Here's what this looks like when we do this to Alyssa's site:

doctype html
    link(href='/css/main.css', rel='stylesheet', type='text/css')
    block head
    block heading
        block nav-homepage
            a(href='/') Homepage
        block nav-projects
            a(href='/projects') Projects

    block body

      p &copy; Copyright 2016 Alyssa P. Hacker.
    script(src='/js/main.js', type='text/javascript')

Note how I've replaced the h1 element (the contents of which vary per-page) with block heading, I've added a block head directive so we can specify the title per-page, I've made a block for each navigation link so we can override them if we want to individually (otherwise they'll have the default of being a link), and I've added block body for the main content. I've also cleaned out a bunch of the cruft the automatic converter put in there.

Now, we can edit index.pug so that it inherits from layout.pug - we'll use the extends keyword for this. Then we just fill in the content we want using block. Here's what this looks like after we're finished with Alyssa's site:

extends includes/layout.pug

block head
  title Alyssa P. Hacker's homepage

block heading
  h1 Alyssa P. Hacker's homepage

block nav-homepage
  li Homepage

block body
  p This is the homepage of Alyssa P. Hacker. You can check out the projects I've worked on #[a(href='/projects') here].

You'll note that I've cleaned out some cruft here, too. We have one last thing to fix: if we change the layout, nothing will get rebuilt. We can fix this by changing the watch task again so that the line for watching Pug files reads:['src/*.pug', 'src/includes/*.pug'], ['build:html']);

Sweet! index.pug is way shorter than what we had before and includes just the content now. We can do the same thing to projects.pug. Then Alyssa can, for example, correct the copyright year in layout.pug - i.e., once - and that change will go into both index.html and projects.html. I've gone ahead and made the change for her.

To give a high-level overview, here's what Alyssa's site looks like now:

% tree -I node_modules .
├── dist
│   ├── css
│   │   └── main.css
│   ├── index.html
│   ├── js
│   │   └── main.js
│   └── projects.html
├── gulpfile.js
├── package.json
└── src
    ├── includes
    │   └── layout.pug
    ├── index.pug
    ├── projects.pug
    ├── scripts
    │   └── main.js
    └── styles
        └── main.css

7 directories, 11 files

Next time...

This post is long enough already, so I'll stop here. We've converted Alyssa's site to have a really solid base, so next time we'll build on top of this work to add superpowered blog features, powered by Stratic.

Now go apply this to your own site!

RC week 6

This is week 6 of being at the Recurse Center.

(I feel obligated to point out that this is being published a week later than it's supposed to be because I got behind. I am, however, very confident that the following is accurate. I used GitHub history and location data to help me remember the parts that were fuzzy.)

Day 19

Not at RC. Total time at RC 0h0m.

Traveled back from FOSDEM.

Day 20

Arrived ~16:45, departed ~22:10, total time at RC 5h25m.

Somewhat unproductive day. Spent most of it just trawling the web. I was completely exhausted since I got back to my apartment from the airport at 3 in the morning. (My flight got in late, I waited at the airport for a while to see my sister, and at 12:30 I got on my train away from the AirTrain station except that it was in the wrong direction. Also, fun fact, subways and trains run really infrequently at 1 AM. Who knew!?)

I did, however, write the slides for the next day's Security Club presentation on the basics of cryptography. Started them pretty late, though, and mostly finished them at home, which was unfortunate since I was up real late again.

Day 21

Arrived ~13:00, departed ~22:00, total time at RC 9h0m.

Arrived with a little time to spare before teaching Cryptography Basics for Security Club. Didn't do much in the afternoon before leaving to walk down to the waterfront with 15 people or so, which took an hour and was really, really lovely, honestly. Especially since the Winter 1 batch is ending so soon.

Day 22

Arrived ~12:00, departed ~5:00, total time at RC 17h00m.

Spent the morning and early afternoon pairing with Jackie on icalc. We fixed a maddeningly weird bug where, when the user input an incorrect value, the operation type prompt would be displayed twice. You can see the fix here - the problem was that getchar() was retrieving the newline used to "submit" the operation type when the function was called again (recursively). In other words, the stdin stream had two characters on it - the incorrect character, and the newline. The solution was to just use a different function from libc that did this correctly. The entire process was pretty educational, since it required delving into C strings, the libc manpages, etc. After fixing that, Jackie and I paired on a JavaScript project that will display Bank of America account balances on the commandline (a non-trivial problem, since Bank of America requires JavaScript).

In the late afternoon, switched gears a bit to review a PRISM Break PR.

In the evening, I gave a presentation at Thursday night presentations entitled "Mercurial for Git folks". I put zero effort into slides and instead simply opened up a bunch of tabs and terminal windows with content I wanted to point to while I was talking. Then I got up there and just... talked about why Mercurial is neat. At the beginning I asked who used Git (most hands, including mine, went up) and who hated Git (a fair number of hands, including mine, went up). So that was pretty amusing. I talked about how the design was pretty similar to Git, how Mercurial has extensions (because it's written in Python), how that plays into its "disable confusing/dangerous stuff like the staging area and rebase by default" philosophy, and how I've been learning it by just using hg-git with GitHub. I thought it went pretty badly, because I didn't feel like it was very coherent. But the people I asked about it said it was actually pretty good, so that feels nice.

After that, we had the end-of-batch/goodbye Rose party. :'(

I spent a lot of time discussing politics and education with Stephanie, which was lovely, and had some other long and interesting conversations that I wish I remember but don't, truthfully. Spent quite a bit of time with Pieter, Heather, and David L messing around on top of the desk chairs, which completely filled Hopper. Eventually ended up sitting in Babbage with Rose, James, Heather, and some other folks (can't remember, honestly) just hanging out. We jacked the space heater way up so it felt like a sauna (aka reeeeeeal nice) and just talked about stuff. Around the 2-3 AM mark Heather and I got bundled up and went out to go to the pharmacy (which turned out to actually be literally right next door) and buy chocolate and nail polish. And then I spent somewhere between 1.5 to 2 hours painting my nails a fabulously sparkly red color called "Devil's Stare" from a brand called "Sinful Colors" - a combination which strikes me as being nothing short of wild.

At around 5 AM, everyone decided to go to sleep. I was the only person to go home, and honestly the only reason I did so was because I knew I'd want a shower in the morning. So I spent about 15 minutes trying to find the stupid switch to turn off the kitchen lights, finally gave up and went home, and fell asleep a short while after the sun rose.


Arrived ~18:00, departed ~1:15, total time at RC 7h15m. As always, Friday doesn't count as a day because RC is technically not in session.

Arrived exceedingly late since I woke up around, you know, 5. Got to RC and did very little before noticing a very large party leaving for dinner, which I joined and ate with (despite saying before I left that I'd had breakfast recently, so I "wasn't going to get anything"). We went out to get Chinese food, then went to a place that served Asian dessert of some kind (I've forgotten). It was beyond lovely, honestly.

(I also mentioned at dinner that I'd missed a week of blogging, so during a discussion with Chris about it I posted to a Zulip topic named "shame AJ" soliciting ideas for a punishment as well as moving the regular Iron Blogger meeting to Tuesday due to the Spring 1 introduction day.)

Executive summary

This week was particularly unproductive. I only did a little bit of work on icalc, my C project, and other than that the only really RC-related project I did was my security club presentation on cryptography basics. This was mostly because my sleep schedule was super messed up from Brussels, and then got messed up again (just as it was getting back to normal) on Thursday.

This week was also very emotional, since it was the Winter 1s' last week. To all my Winter 1 friends: you made my time at RC amazing and welcoming and exciting and wonderful and I loved being around you all. And I will miss you for a very long time. <3

(*starts crying a lil bit*)

Total time at RC 38 hours 40 minutes; cumulative time 239 hours 25 minutes.

Gender representation at RC

So I want to talk (real quick) about gender representation in tech. Specifically, the fact that it's abysmal. Women[1] usually account for, at most, 30% of the employees in a company and that number is often much lower. Even at the companies where it's 30%, that number is misleading because it doesn't adress management positions which are often much lower. (For reference, women accounted for about 59% of the overall workforce as of 2015, when the article I pulled those numbers from was published.) Here's an article that says that on average, women leave the industry within 7 years of being in it. Let me repeat that, because I didn't say they leave their jobs, or take a break. They completely leave the entire technology industry. And unfortunately it makes sense, since incidents of sexism in tech are rampant.

Unlike (most of) the rest of the tech world, the Recurse Center is mostly (completely?) gender-balanced. I want to point out that while for years I've believed in diversity, it didn't become clear to me how much implicit bias I held until I joined RC. I find that, having previously spent significant amounts of time in environments that were predominantly white, cis and male, I find myself needing to self-correct discriminatory and gender-based unconscious assumptions significantly less frequently at RC. In other words, my subconscious is making less incorrect, gender-based suggestions about what people are like that I then have to consciously reject. I would attribute this almost 100% to the fact that at RC I'm surrounded by lots of people who are a) super talented and know their shit and b) aren't male.

If I have a point to this blog post (and I'm not sure that I do), it'd be to underscore what many have said before me: representation and role models are important. Advocating for gender equality is good and important, but in the end what really matters is what's actually in people's heads. That's mostly influenced by what they see in their immediate lives, and that's why representation is so important - believing in equality is great, but presenting actual evidence to your subconscious that its biases are incorrect is a much more powerful way to improve the way you perceive the world.

If folks have any comments or different perspectives, I'd love to hear them privately or via WebMention.

[1]: I'm focusing on women here because I'm not aware of any statistics about non-binary people in tech. But it's worth noting that it's a huge problem for them, too.

Update February 22nd: the lovely Rachel Vincent from RC faculty says this about RC's gender-balancing:

Of the folks who are currently in-batch (Winter 2's and Spring 1's), ~36% identify as women. It's roughly the same for the community as a whole (~33%), though we don't have super accurate numbers because we don't update information on folks' gender. The most gender balanced batches were a little over 40% women. We're always pushing for 50/50, though. 😊 Our overall strategy is to try to get an applicant pool that is as gender balanced as possible, and we don't do any kind of balancing during the admissions process itself.