Most READMEs are written for the person who wrote the code. That person already knows how the project works, what it depends on, and why it exists. They write the README as a formality and fill it with implementation details that are only interesting after you’ve already understood the project. Then a new team member opens it six months later, reads “this project implements a reactive event bus with configurable back-pressure,” and closes the tab.
A good README is written for someone who has never seen the project. It answers the questions they actually have, in the order they have them, without assuming context they don’t have. Here’s how to write one that people open twice.
Answer the “why” before the “what”
The first paragraph of a README should tell the reader what problem this project solves and why it exists. Not what it is technically – what it does for you.
Compare:
Bad: “notifier is a Node.js library that implements a pub/sub pattern with configurable delivery semantics.”
Better: “notifier sends real-time updates to connected browser clients when server-side data changes. Drop it into an Express app and replace polling with websocket push in about an hour.”
The second version tells me what I’m getting and roughly whether it’s worth reading further. The first one tells me the implementation approach, which I don’t need to know yet.
Structure: the five sections you always need
Every project README needs at minimum:
Quick start – the minimum steps to get a working instance running. This should work when copy-pasted by someone who has never heard of your project. If it takes more than ten commands, it’s not a quick start.
## Quick start
git clone https://github.com/your-org/notifier
cd notifier
cp .env.example .env
npm install
npm run dev
# open http://localhost:3000
Requirements – Node 20+, Postgres 15+, Redis. Be specific about versions. “latest Node” will cause problems when the next major version changes something.
Configuration – list every environment variable the application reads, what it does, whether it has a default, and what a valid value looks like. This is the section I refer back to most often when something isn’t working.
| Variable | Required | Default | Description |
|------------------|----------|-----------|------------------------------------|
| DATABASE_URL | yes | - | Postgres connection string |
| REDIS_URL | yes | - | Redis connection string |
| PORT | no | 3000 | HTTP port to listen on |
| LOG_LEVEL | no | info | debug, info, warn, error |
Development setup – running tests, linting, database migrations, common development tasks. This is where the Makefile targets or npm scripts go.
Deployment – even one paragraph on how this gets to production. Is there a Docker image? A CI pipeline? Manual steps? The new person joining the team should not have to ask five colleagues how deployments work.
Code examples that actually run
Code examples in READMEs go stale fast. There are a few ways to fight this.
Source examples from actual test files rather than writing them inline. If the README says “see examples/basic.js“, that file is more likely to stay current because it’s part of the test suite. If the example lives only in the README, no one has a reason to update it when the API changes.
Mark language in fenced code blocks. Not just for syntax highlighting – it also signals to the reader what they’re looking at. An unmarked code block is ambiguous.
```bash
npm run build
```
```typescript
import { createNotifier } from 'notifier'
const n = createNotifier({ port: 3000 })
n.on('connect', (client) => console.log('client connected'))
```
Include the expected output for commands where it’s not obvious. “You should see something like:” followed by a truncated output block answers the implicit question “is this working?”
Keep it honest
Don’t document features that don’t work or aren’t finished. A README that says “supports clustering” when clustering is a stub function erodes trust faster than not mentioning it at all. Same goes for badges: a test coverage badge showing 94% on a project with three tests is misleading. Either keep badges accurate or remove them.
If there are known limitations, list them. “Does not support Windows” or “not recommended for more than 100 concurrent connections” saves someone from building on your project for a week before discovering it won’t work for their use case.
Keep it current
Stale documentation is worse than no documentation – it actively misleads people. A few practices that help:
Add a CI check that runs the quick-start commands in a clean environment. If the setup instructions fail in CI, they fail loudly before someone else hits them.
Date-stamp major sections that change infrequently: “Deployment (last updated 2026-03-01)”. It sets expectations and prompts periodic review.
Make the README easy to find and update. Keep it at the repo root. Make the first contribution guideline something like “if the README doesn’t match what you found, update it.”
Length and tone
A README is not documentation. Documentation is comprehensive; a README is a doorway into the project. It should be long enough to answer the first five questions a new reader has, and short enough that they’ll read the whole thing. For most projects that’s 500-1500 words – roughly what you’re reading now.
Write like you’re explaining to a smart colleague, not like you’re writing a spec. Active voice, short sentences, concrete examples. Avoid jargon unless the reader already knows it (if they’re looking at your project, they probably do).
The GitHub guide to README files is a useful baseline: docs.github.com – about readmes. And the Make a README project has good templates organized by project type: makeareadme.com.
The README is often the first thing someone reads before deciding whether to use, contribute to, or hire the person who built something. It’s worth an extra hour to get it right.