Back to blog home

README Masterpieces

Alec Sloman
Sep 24
 - In 

Jump to:

When meeting candidates at Lookahead we often discuss what makes for an extraordinary code test. My general advice is to “make it your masterpiece”. Matt has written about this before so be sure to check that out.

Diving a bit deeper, I break down our rubric for evaluating code tests into two categories: the code, and everything else. “Everything else” is itself twofold: version control, and documentation.

In this post, I will touch on documentation, in particular what I believe are important things that should be included in every README. Where possible I will try to illustrate with examples.

I’m a documentation enthusiast and I believe a comprehensive README adds tremendous value to a codebase. It’s the face of your project and the gateway through which a new contributor enters the development cycle. Sadly there doesn’t seem to be a great deal of rigour around what READMEs should contain, and too many projects don’t have one at all. Having reviewed an awful lot of code tests, I’d like to take a shot at identifying and explaining what I feel are the elements that every README should contain.

Let’s begin with an outline:

  • Title & Description
  • Environments
  • System Dependencies & Configuration
  • Application Installation Instructions
  • Operating Instructions
  • Testing Instructions
  • Overview
  • Discussion
  • Contributing

Below is some short commentary on each of these points.


State your application’s name and describe its main functionality. A high-level description of what it does is an obvious but often overlooked place to start. If it’s part of a larger system, you can also discuss how it fits into the larger picture.


Provide details about the various environments the code is expected to run in, including:

  • OS and version

For example:

This application was developed on Ubuntu 14.04 x86_64

Generally speaking you’ll have test/staging and production environments that are similar (if not identical) to your development environment, however that’s not always the case. It may be necessary to provide additional commentary on specific environments.

  • Compatible/tested environments

Ideally, you should test your code in a variety of environments and list those that are found to be compatible, for example:

  • Ubuntu 12.04, ruby 2.1.0p0
  • Ubuntu 14.04, ruby 2.1.2p95

This provides a useful lock for developers who are setting up a development environment or deploying the application.

  • Incompatible environments

It can be useful to explicitly exclude environments that you know won’t support the application. For example:

  • ruby < 2.1.0

Whereas before I knew that 2.1.0p0 was supported, I now know that anything below that won’t work.


Before we install and run the application, we need to ensure our environment is correctly configured and necessary system-level dependencies have been installed.

You should provide:

  • a list of system-level dependencies,
  • instructions on how to check for their presence/version,
  • installation instructions, or a link to instructions.

For example, for applications that require Ruby you might write:

ruby => 2.1.0p0

To check your version run:

$ ruby -v

To learn how to install ruby visit []

While installing Ruby doesn’t require much configuration, other dependencies will. For example, if you’re writing a web application to be served by Nginx, you should provide information about how to configure the server, etc.


After you’ve ensured your environment is properly configured and the requisite system dependencies have been installed, you should provide instructions for installing the application itself. For example, if you’re writing a Ruby application you might provide instructions like:

To install the application, cd to the root directory and invoke:

bundle install


Provide the invocations for running/starting the application. Discuss salient configuration options, common configuration cases, and the like. For example:

To run the application in interactive mode, invoke:

$ app

To run the application with an input file, invoke:

$ app < /path/to/input

To run the application with debugging flags, invoke:

$ app -d

And so forth.


Provide the invocations for executing the application’s test suite.


Provide a detailed overview of the application’s functionality as it relates to concerns such as input format, business domain(s), output format, and so forth. For example:

The application is designed to read x from $stdin and print y to $stdout.


x’s passed into the program are expected to be be comma-separated values with the following format

`, ,


0, hello, #FFFFFF

<property1> is expected to be a whole value, thus only digits are allowed.

<property2> is expected to be a string.

<property3> is expected to be an HTML Color Hex code.


A y will be printed with the values computed from the x passed into the application, for example: 0 HELLO WHITE


Clearly this application is useless. All it does is transform input and print it out. The transformations are not even useful.


Input: 1, hello, #FF0000

Output: 1 HELLO RED

Even with such a useless illustration, hopefully you get the picture of what an “Overview” should include.


Provide a step-by-step description of how your application progresses from initialization, to taking and processing input, to doing work on that input, to printing output. For example:



$ app

app is an executable in your load path. It is a Ruby script with the following lines:

require 'app'


The #run method reads in x’s from the input source (defaults to $stdin) and passes them to a parser. The parser returns a structured item which is passed to y for transformation.

3. ???

Describe how the y is passed around from class to class, until it finally reaches…


After the y is printed, the application exits.

Certainly I could follow the path by reading your code, but do your readers a favor and spell it out for them. I’ve found that writing in plain english has the dual effect of communicating your application’s design and, if it’s too complicated to express in plain English, revealing needless complexity in your design.


High-level discussion about your design choices and how you arrived at them is extremely helpful. For example, if you implemented a particular software pattern, what is it? How does it solve the problem? Why did you choose it over other possibilities? For example:

For this application I chose to implement the <pattern> pattern because of <reasons>. I considered implementing <alternative pattern> but decided it was not the best option because of <further reasons>.

You should also discuss the parts of your implementation that you’re proud of. For example:

I was extremely happy with how <class> was implemented. I feel it has a clever <feature>, it’s interface is <superlative>, and is otherwise <good|great|amaze>.

You can also discuss details you’re not so sure about, for example:

I am not happy with <class>, I wasn’t sure about what I wanted to accomplish with it. It now resembles an <anti-pattern|ogre> that makes me deeply upset to think about.

If I encounter some code that’s… less than optimal, I be less judgy if the author is reflective about shortcomings in their README. No implementation is perfect, and demonstrating your thoughtfulness goes a long way in showing your maturity as a developer.


Google it.


A whole topic in itself.


READMEs say a lot. They tell how sympathetic you are to other developers, how comprehensive your general approach to software development is, how well you can communicate design decisions and issues, and generally how mature your development habits are. I guess the point is: IT’S IMPORTANT. Take the time to write a README masterpiece and your project will immediately level up.

Join our newsletter for updates and new openings:
The Lookahead office is located on the traditional lands of the Gadigal people of the Eora Nation. We acknowledge that sovereignty was never ceded and pay our respects to elders past, present, and future.
Thank you for subscribing!
Oops! Something went wrong while submitting the form.