How to Write a Good README: Structure, Examples & Best Practices (2026)

A README is often the first — and sometimes only — thing a developer reads before deciding whether to use your project. A poorly written README means fewer users, fewer contributors, and more support issues from people who couldn't figure out how to get started. This guide covers the structure, writing style, and tools that make a README genuinely useful.

Why READMEs Matter More Than You Think

For open-source projects, the README is your landing page, your documentation, and your marketing copy simultaneously. For internal projects, a good README means your teammates can get started without asking you. For personal projects on GitHub, a strong README is the difference between a portfolio project that impresses recruiters and one that gets ignored.

The Essential Structure

Every README should have these sections in roughly this order:

# Project Name

One-paragraph description of what this project does and why it exists.

## Features (optional but high-impact)

- Key feature 1
- Key feature 2

## Quick Start / Demo

The fastest path from zero to working — code-first, explanation second.

## Installation

Step-by-step setup instructions.

## Usage

How to use it after installation.

## Configuration

Environment variables, config files, options.

## API Reference (if a library)

Function signatures, parameters, return values.

## Contributing (for open-source)

How to submit issues and PRs.

## License

The Opening: Lead With What It Does

The most common README mistake is burying the explanation. Lead with a single sentence that answers: what does this do? Who is it for?

# Bad opening
This repository contains the source code for the application described
in the accompanying paper "Efficient Processing of..." (2026).

# Good opening
# zap — A zero-config TypeScript build tool

Zap compiles a TypeScript project to a single optimised JS file in under
100ms with no configuration. Drop it in any project and run `zap build`.

Badges: When to Use Them

Shields.io badges give readers instant signals about project health. Use them at the top, but don't overdo it:

[![npm version](https://img.shields.io/npm/v/your-package.svg)](https://www.npmjs.com/package/your-package)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![CI](https://github.com/org/repo/actions/workflows/ci.yml/badge.svg)](https://github.com/org/repo/actions)
[![Coverage](https://img.shields.io/codecov/c/github/org/repo)](https://codecov.io/gh/org/repo)

Good badges to include: version, license, CI status, test coverage. Badges to skip: download counts (meaningless for new projects), "built with X" framework badges (adds noise, no signal).

Quick Start: The Most Important Section

Most readers skim. If they can copy 3 commands and see something working, they'll read the rest. If they can't, they'll leave. The Quick Start should be the shortest path to a working result:

## Quick Start

```bash
npm install your-package
```

```typescript
import { createClient } from 'your-package'

const client = createClient({ apiKey: 'your_key' })
const result = await client.process('Hello, world!')
console.log(result) // { text: 'HELLO, WORLD!', words: 2 }
```

That's it. See [Configuration](#configuration) for all available options.

Notice the comment in the code showing the output — readers don't have to run the code to know what it produces.

Installation: Be Explicit, Assume Nothing

## Installation

**Requirements:** Node.js 20+ and npm 9+

```bash
npm install your-package
# or
yarn add your-package
# or
pnpm add your-package
```

**Environment setup:**

Copy `.env.example` to `.env` and fill in your values:

```bash
cp .env.example .env
```

```env
API_KEY=your_api_key_here
DATABASE_URL=postgresql://localhost:5432/myapp
```

**Start the development server:**

```bash
npm run dev
# Server running at http://localhost:3000
```

Usage Examples: Show, Don't Tell

Code examples are worth ten times their weight in prose. Show the most common use case first, then edge cases:

## Usage

### Basic usage

```typescript
import { Parser } from 'your-package'

const parser = new Parser()
const result = parser.parse('input text')
```

### With options

```typescript
const parser = new Parser({
    strict:   true,       // throw on invalid input instead of returning null
    encoding: 'utf-8',    // input encoding (default: auto-detect)
    maxSize:  1024 * 100, // max input size in bytes (default: 10MB)
})
```

### Error handling

```typescript
try {
    const result = parser.parse(input)
} catch (err) {
    if (err instanceof ParseError) {
        console.error('Line', err.line, ':', err.message)
    }
}
```

Configuration Reference

A table works better than a list for configuration options:

## Configuration

| Option      | Type      | Default  | Description                              |
|-------------|-----------|----------|------------------------------------------|
| `apiKey`    | `string`  | required | Your API key from dashboard.example.com  |
| `timeout`   | `number`  | `5000`   | Request timeout in milliseconds          |
| `retries`   | `number`  | `3`      | Number of retry attempts on failure      |
| `debug`     | `boolean` | `false`  | Log all requests and responses           |
| `baseUrl`   | `string`  | see docs | Override the default API endpoint        |

Screenshots and Demos

For visual tools, a screenshot or GIF is worth more than any amount of description. Use VHS to record terminal demos as GIFs:

## Demo

![Demo showing the tool in action](./docs/demo.gif)

*Converts a 500-line JSON file to TypeScript interfaces in 0.3 seconds*

Keep GIFs under 5MB. For web UIs, a short Loom video link or hosted demo URL works better than a GIF.

API Reference

For libraries, document every exported function/class with its signature, parameters, and return value:

## API Reference

### `parse(input, options?)`

Parses the input string and returns a structured result.

**Parameters:**

| Name      | Type              | Description                          |
|-----------|-------------------|--------------------------------------|
| `input`   | `string`          | The string to parse                  |
| `options` | `ParseOptions`    | Optional configuration (see below)   |

**Returns:** `ParseResult | null` — `null` if input is empty or invalid in non-strict mode.

**Throws:** `ParseError` — in strict mode if the input is invalid.

**Example:**

```typescript
const result = parse('SELECT * FROM users WHERE id = 1')
// → { type: 'select', tables: ['users'], conditions: [...] }
```

Contributing Guidelines

## Contributing

Pull requests are welcome. For major changes, please open an issue first
to discuss what you'd like to change.

**Development setup:**

```bash
git clone https://github.com/org/repo.git
cd repo
npm install
npm run dev
```

**Running tests:**

```bash
npm test           # run all tests
npm run test:watch # watch mode
```

**Commit convention:** We use [Conventional Commits](https://www.conventionalcommits.org/).
Please use `feat:`, `fix:`, `docs:`, `chore:` prefixes.

Please make sure tests pass before submitting a PR.

README Anti-Patterns to Avoid

The Wall of Text

Long paragraphs without headings, code blocks, or lists kill readability. Break up every 3–4 sentences into a new point, list, or heading.

Installation Instructions That Skip Steps

"Install the dependencies and run the app" — which dependencies? How? With what command? Write instructions you'd give to someone who has never used the technology before.

Outdated Screenshots

A screenshot from v1 when the project is on v3 actively misleads users. Either keep screenshots updated or omit them in favor of a live demo URL.

Listing What the Code Does Instead of Why

The code is already there — it shows what it does. The README should explain the motivation: why does this project exist? What problem was it built to solve that existing solutions didn't address?

Missing License

No license means the code is legally "all rights reserved" even if it's on GitHub. Any serious user or company will pass on your project if there's no license. MIT is the most permissive and widely accepted. Apache 2.0 adds patent protection. GPL requires derivatives to be open-source.

Tools That Help

  • readme.so — drag-and-drop README builder
  • Shields.io — generate badge URLs
  • Carbon.sh — beautiful code screenshots
  • VHS by Charm — record terminal sessions as GIFs
  • Mermaid — Markdown-based diagrams (supported natively in GitHub)

A Complete Example README Opening

# quickmock

[![npm](https://img.shields.io/npm/v/quickmock)](https://npm.im/quickmock)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![CI](https://github.com/org/quickmock/actions/workflows/ci.yml/badge.svg)](https://github.com/org/quickmock/actions)

Generate realistic mock data for any TypeScript interface in one line.

```typescript
import { mock } from 'quickmock'

interface User {
    id:        number
    name:      string
    email:     string
    createdAt: Date
    role:      'admin' | 'user'
}

const user = mock<User>()
// → { id: 4821, name: "Alice Chen", email: "alice.chen@example.com",
//     createdAt: Date(2025-03-12), role: "admin" }
```

No configuration. No separate schema files. Just pass the interface — quickmock infers
field types and generates plausible values automatically.

Summary

A good README: opens with what the project does, gets readers to a working example in under two minutes, documents configuration in a scannable table, shows real code with expected output, and includes a license. Invest 30 minutes writing a proper README — it'll save hours of answering repeated questions and significantly increase adoption of anything you publish.

Frequently Asked Questions

How long should a README be?

Long enough to cover everything needed to get started, short enough that someone can scan it in 5 minutes. For a library or CLI tool: 300–600 lines including code examples is typical. For a full application: 200–400 lines. The anti-pattern is both too short (people can't figure out how to use it) and too long (people don't read it). Use headers and code blocks liberally — they make long READMEs scannable. If your documentation grows beyond ~800 lines, move detailed reference docs to a separate docs/ folder or a documentation site and link from the README.

Should I include a table of contents?

For READMEs longer than 400 lines, yes. GitHub auto-generates a table of contents accessible via the list icon on the README page, but an explicit ToC at the top is still helpful for very long files. Use GitHub's heading anchor syntax: [Installation](#installation). Keep the ToC to top-level headings only — linking every h3 makes it cluttered and hard to maintain. For shorter READMEs, skip the ToC and let the clear heading structure do the work.

What's the difference between a README and a CONTRIBUTING file?

The README is for users — people who want to use your project. The CONTRIBUTING file (CONTRIBUTING.md) is for contributors — people who want to improve the project. CONTRIBUTING covers: how to set up the development environment, how to run tests, branch naming conventions, commit message format, PR review process, and code of conduct references. GitHub automatically links to CONTRIBUTING.md when a user opens a new issue or PR — so it reaches exactly the right audience at exactly the right moment. Keep them separate so users don't have to wade through contribution logistics.

Do I need a README for private projects?

Yes, but it can be shorter. For a private team project, the README should cover: what the project is (1–2 sentences), how to run it locally (step-by-step), required environment variables (link to the team vault or .env.example), and key architectural decisions. Internal READMEs don't need marketing copy or extensive API documentation — focus on onboarding speed. A new team member should be able to run the project locally within 15 minutes using only the README. Time that as a benchmark.