Matthew BauerBlogRSS

12 Nov 2017

Reproducible résumés

Recently, ‘reproducible’ papers have appeared in Academia. The idea is to make all of a paper’s data and code openly available so that readers can reproduce its findings. For software-heavy papers, it makes perfect sense to include code in papers because the code is often as important as the paper’s text.

Outside of Academia, there’s not as much interest in this idea of reproducibility. That’s unfortunate because it’s a really neat idea.

To make this idea more accessible for those outside Academia, I’m going to introduce a new kind of reproducibility. Everyone in the working world needs to apply for a job at some point. So, let’s figure out how to make a résumé, reproducible. This document will be all that is needed to generate my résumé. With some minor adjustments you can use it to generate your own.

At the end of this guide, you should have all of the information needed produce a PDF version of your résume.

1 The Résumé

LaTeX makes it very easy to generate Résumés. It provides extremely powerful typesetting features and can output PDF. Some of the syntax is scary to the unexperienced but I’ve tried to keep it as simple as possible.

LaTeX syntax can be a little confuging at first but basically everything is made up of commands. Each command starts with a \ followed by a command name. Each command can take a variable number of arguments and they are specified with { and }. LaTeX is made up a series of packages that provide these commands and do most of the work of typography automatically.

1.1 Document class

Every LaTeX document has a document class. Think of it like the CSS to the Résumé’s HTML. Classes tells LaTeX how to layout the document and there are a few builtin ones like ‘article’ that work fairly well for basic document. I’ve built up my own class that works well for résumés. It’s largely based off of @posquit0’s Awesome-CV but with some minor adjustments to make it a little cleaner. In addition, I’ve followed some of Matthew Butterick’s guide on résumés. You can read about them at Practical Typography.

I’ve make it available at resume.cls of this repo’s root but I won’t include it in this blog post (but it’s still in the ./README.html file).

1.2 Header

Every LaTeX document has a document class. For this document, I will use a custom resume class.


Next, I’ll provide some semantic information. Each of these commands is defined in the résumé class. You can make your own commands but I’ve tried to make this document as simple as possible.

All of the following elements correspond to my name, address, mobile, email and homepage. The contents are not parsed by LaTeX so you could put anything here, but you should aim to make each command as semantic as possible.

My personal info follows,

\address{10118 GARNETT ST, OVERLAND PARK KS 66214}
\mobile{(913) 671 0636}

This begins the document. The command makecvheader is from resume.cls and will setup the basic template.



Everything that follows is my own résume, but the ideas should be fairly self-explanatory.

1.3 Education

This LaTeX class defines \cvsection to separate sections of the résumé by a horizontal line and some blank space. The only argument for \cvsection sets the contents of the section header.

I start my résume with the ‘Eduction’ section.



Each cvsection is composed of multiple cventries.

  {University of Kansas}
  {Lawrence, KS, USA}
  {B.S. in Computer Science}
  {Aug. 2015 – Exp. Dec. 2018}
    \item { Participated in ACM and hackathon competitions }
    \item { Coursework includes Software Engineering, Programming Languages,
        and Communication Networks }

cventry can be though of as a function that takes a variable 5 arguments. The first will be the heading used. The second will be on the right-hand side in italics. The third will be in italics immediately below the first. The fourth will be on the right-hand side of the third argument. The last argument provides items for the entry.

For my résumé, I have chosen to use each of these arguments for certain purposes. It’s not necessary to do the same, but you should use a consistent style throughout your résumé.

left right
argument 1 argument 2
argument 3 argument 4

For me, they correspond to,

left right
Company or University Location
Position or degree Start date - end date

The next section has work experience. It follows the same pattern outlined in the Education section.

1.4 Work Experience

\cvsection{Work Experience}

  {, Inc.}
  {Seattle, WA, USA}
  {SDE Intern}
  {Summer 2017}
    \item { Worked on Mobile Identity team which manages the login screens for
            Amazon apps }
    \item { Project made it easier for teams to register new devices through
            Identity Services }
    \item { Used Agile development principles in design and development of
            project }
  {Lexmark Enterprise Software}
  {Lenexa, KS, USA}
  {Software Engineer Intern}
  {Summer 2015, Summer 2016}
    \item { Worked on the Client Architecture team which builds the JavaScript
        web framework which other teams use to build enterprise solutions }
    \item { Participated in high level design decision conversations }
    \item { Project moved the web framework away from in-house solutions to
        better maintained open source projects while preserving legacy
        compatibility }
    \item { Asked to return after impressive first year }
  {Lincoln, NE, USA}
  {Design Studio Intern}
  {Spring 2015}
    \item { Startup building web app to let doctors track patients recovering
        from surgery without frequent checkup visits }
    \item { The web interface is used by both patients to record progress and
        doctors to track progress }
    \item { Team used Scrum development principles for quick response and user
        focused design }

Here we end cventries.


1.5 Honors & Awards

Again we must define a new section, this time for honors and awards.

\cvsection{Honors \& Awards}
{3rd Place}
{JayHacks Hackathon}
{Lawrence, KS, USA}
{Grand Prize}
{Google Code-in}
{Mountain View, CA, USA}

1.6 Footer

The makecvfooter command gives a nice footer that will be put at the bottom of each page. This can give us the document title and page numbering. In addition, the LastPage command will tell us how many pages there are in case we misplace a page while printing.


2 Building it

Nix makes it possible to make this Résumé truly reproducible. Nix is a purely functional package manager. This means that each package is defined in a functional language and we have much more powerful tools at our disposal.

Nix can be installed on both Linux and macOS machines. It is fairly easy to setup, provided you have sudo access. Run the following and follow some simple steps to get Nix working,

curl | sh

More information on Nix is available from the Nix homepage. On the next page, I’ll explain how build this résumé using Nix.

2.1 resume.nix

To start, we’ll this need to pull in Nixpkgs. Nixpkgs provides a set of packages for Nix to use. Because Nix is functional, we’ll make nixpkgs an optional argument if we ever want to work with multiple package set versions.

{nixpkgs ? <nixpkgs>}: with import nixpkgs {};

This syntax may be a little hard to understand for users new to Nix. {}: declares a function. This particular function will take up the entire file and Nix will autocall it when no arguments are necessary. This particular function has one arguments, nixpkgs, that refers to the package set being used. To make things easier we provide a default after the ? symbol. <nixpkgs> refers to the nixpkgs channels that the user has setup. It can be updated with,

nix-channel --update

Giving us a potentially newer version of Nixpkgs and its software to work with.

Almost everything in Nix is a derivation (including Nix itself). Each derivation has its own store path so we can reference it through

stdenv.mkDerivation {
  name = "resume";
  src = ./.;

We’ll name this derivation resume and tell it to use the files in the current directory as source.

buildInputs = [
  (texlive.combine {
    inherit (texlive) scheme-basic xetex xetex-def setspace fontspec
                      chktex enumitem xifthen ifmtarg filehook
                      upquote tools ms geometry graphics oberdiek
                      fancyhdr lastpage xcolor etoolbox unicode-math
                      ucharcat sourcesanspro tcolorbox pgf environ
                      trimspaces parskip hyperref url euenc

Inputs in Nix are similar to dependencies in other package managers. Here, we list only one dependency which provides our LaTeX distribution. texlive.combine is a function that produces a derivation which will provide the xetex binary. Each attribute listed in between { and } will be passed as LaTeX packages to TeX Live. The inherit keyword tells Nix to pass everything after (texlive) as attributes of texlive to texlive.combine. Each one of those names listed should correspond to TeX Live packages that are needed to build the résumé PDF.

In the future, I’d like to get Tex Live to actually recognize the packages we are using within LaTeX, but nothing seems to exist to do this.

buildPhase = ''
  xelatex -file-line-error -interaction=nonstopmode resume.tex

Here we actually build the xelatex file. These options make it easier to debug xelatex when something goes wrong and makes sure we don’t get xelatex doesn’t require any user input. It will produce a file called resume.pdf that we can use as a résumé.

installPhase = ''
  cp resume.pdf $out

Finally, we copy this résumé to $out where the derivation will live.


2.2 Running the build

This entire document is built with ~org-mode~’s Babel engine. This means that we can generate the files needed to build the résumé from scratch. To do this, first we must clone this repository (if you haven’t already).

git clone
cd resume

Next, we need to open this file in Emacs and generate the files (tangle it in Babel lingo). Run this now, if you haven’t already,


Finally, let’s build these files. From Emacs, type the following: C-c C-v t (org-babel-tangle). This will take a little bit, but at the end of it you will have all of the files tangled inside You can build the résumé with,

nix-build resume.nix

3 Automating it

Sadly, Nix does not understand raw Org mode (yet). We need a bootstrap to generate a Nix script from this file to truly automate this. I’ve included it here for completeness, but you’ll need to generate it first before Nix will work. If you haven’t already, generate this in org-mode by moving the cursor into the src block below and pressing C-u C-c C-v t (org-babel-tangle). Alternatively, I’ve provided a pregenerated file at ./default.nix.

{nixpkgs ? <nixpkgs>}: with import nixpkgs {};

Again, we’re be defining a function. Now, we will be using the let…in syntax to define a derivation to use.

README = stdenv.mkDerivation {
  name = "README";
  unpackPhase = "true";
  buildInputs = [ emacs ];
  installPhase = ''
    mkdir -p $out
    cd $out
    cp -r ${./fonts} fonts
    cp ${./}
    emacs --batch -l ob-tangle --eval "(org-babel-tangle-file \"\")"
    cp resume.nix default.nix

The README derivation builds all of the things contained within this file. Almost every code block here will make a file that we’ll feed into Nix. Fonts are external to the README because they are binary and cannot be put in an Org file, but you can view them in ./fonts.

Now, we’ll create another derivation with Nix. This will utilize a little known feature of Nix called IFD. It might not make sense right now, but it runs the README derivation’s resume.nix file as its own Nix expression.

IFD stands for Import From Derivation. Basically, it means we can import data generated in one derivation, README, in Nix to generate another derivation. This will bootstrap the ./README.html and allow us to avoid generated files.

in import README {inherit nixpkgs;}

This can be thought of as a recursive call to Nix. It basically lets us use the Nix output of as an input for Nix. The derivation will produce a ./result file that will contain the output of build.nix for the Building it section.

Now, we can finally build the Résumé! To do this, we just need to run nix-build from the command line.


Look at ./result and it will be PDF file you can open.

3.1 Continous Integration

Travis makes it easy to run continuous integration on our résumé.

language: nix

Travis supports Nix projects out-of-the-box so all that’s really needed is the above. However, matrices are useful to make sure it runs on more than one machine and accross different versions.

script: nix-build --arg nixpkgs "builtins.fetchTarball \"$NIXPKGS\""

This line tells Travis what to build. The $NIXPKGS variable should become clear after reading the usage below.

We want to target Linux and macOS. This will make sure the build script is portable. Unfortunately, Nix does not support Windows systems and Travis does not support any BSDs.

  - linux
  - osx

Each value of NIXPKGS corresponds to a tarball release of Nixpkgs. This means that we can avoid problems that arise when the Nixpkgs repo is broken. Any URL to a tarball with a Nixpkgs set will work, but in Nix we call these ‘channels’. Each channel has a set of tests that are required to run before a new release of the channel. You could actually point directly to Nixpkgs HEAD using GitHub’s archive URL but this would constantly break as you would frequently have to rebuild whole packages when you could just let Hydra do it for you. You can find a whole listing at but I have included the most recent channels below.


Travis works with ‘matrices’ meaning that every attribute of one property (os) will get crossed with another property (env). We need to modify some of these to get a working matrix.


We exclude some (os, env) pairs here. It doesn’t really make sense to use a Darwin channel on Linux or a NixOS channel on macOS.

  - os: linux
  - os: osx
  - os: osx

We allow some failure for unstable branches. We don’t expect stable releases to always work.

  - env:
  - env:

This will end up building five résumés three on Linux machines and two on macOS machines. So, using ./.travis.yml, you can make Travis automatically build a resume.pdf every time you commit a change. You should be able to set this up yourself but alternatively you can look at my Travis dashboard.

4 Conclusion

More information on reproducible research is available at Reproducible Research. My hope is that eventually more things will become ‘reproducible’. Technologies like Nix and Babel make this fairly easy but they have not yet entered into the average Software Developer’s toolbelt. Reproducible projects may take longer to setup, but they lead to more robust software systems.

Résumé require a careful mix of informative content and flashy styling. Too much information and employers will be overwhelmed but too little and employers assume you are inexperienced. Likewise, . Perhaps eventually, software companies will read through résumés in org-mode instead of PDFs, but alas Silicon Valley has not yet reached this level of Nirvana.

I welcome everyone to fork the repo containing these files. You should be able to generate your own Résumé by modifying the contents of Semantic info and LaTeX document. Any contributions to the process of reproducible résumés are welcome and you can open them as issues under that GitHub repo. Alternatively, you can email me at