Writing a README people actually read
A README is not a brochure. It is the first operational guide for a repository, so it should help a reader decide what the project is, whether it is relevant, and how to run or us…
A README is not a brochure. It is the first operational guide for a repository, so it should help a reader decide what the project is, whether it is relevant, and how to run or use it without hunting through the tree.
Start with the reader's first question
A useful README answers the first question before anything else: what does this repository do?
Open with a short description in plain language. Name the problem the project solves, the main audience, and the expected result. Keep it concrete. Avoid vague claims such as "simple", "powerful", "modern", or "production ready" unless the rest of the README proves exactly what those words mean.
A good opening usually covers:
- What the project is.
- Who it is for.
- What outcome it gives the reader.
- The current status, if it is experimental, deprecated, private, or incomplete.
Do not make the reader infer purpose from package names, badges, directory names, or screenshots. Those are supporting details, not the explanation.
Put the shortest successful path near the top
The next section should give the shortest reliable path from clone to working result. This is not the place for every option. It is the place for the normal case that a competent newcomer should run first.
Use commands that can be copied as written. Include required versions only when they matter and can be verified. Show expected output when it reduces ambiguity, especially for setup, tests, local servers, or generated files.
npm install
npm test
npm run devIf setup depends on environment variables, configuration files, credentials, generated code, services, or local tooling, state that before the command sequence. A README that hides prerequisites creates wasted debugging time.
Explain the repository shape
After the quick start, describe the major directories. The goal is not to list every file. The goal is to give the reader a map.
src/
application code
tests/
automated tests
docs/
longer form documentation
scripts/
repository maintenance scriptsKeep descriptions functional. "Contains helpers" is rarely useful. "Builds the search index used by the static generator" is useful because it explains why the directory exists.
When a repository has several entry points, identify the most important one. For a service this may be the server bootstrap. For a library it may be the public package export. For a command line tool it may be the executable wrapper and the command handler.
Treat this as a pointer, not a full tour. The detailed layout belongs in dedicated docs so the README stays short.
Separate usage from development
Usage instructions and development instructions answer different questions. Usage explains how to consume the project. Development explains how to change it safely.
For a library, usage may include installation, import examples, public API links, and compatibility notes. For a service, usage may include local configuration, runtime dependencies, and deployment assumptions. For development, include test commands, lint commands, formatting commands, fixture generation, and common failure modes.
Keep this distinction visible with headings. A reader who only wants to use the project should not have to read the contribution workflow. A maintainer who wants to patch the project should not have to reverse engineer the test command.
Keep examples small and executable
Examples should be short enough to understand and complete enough to run. A fragment that omits imports, setup, or required context can be worse than no example, because it creates false confidence.
Prefer one realistic example over several decorative ones. Name values clearly. Avoid placeholder names that look like real configuration. If an example is intentionally partial, say so before the block.
import { parseUserId } from "./user-id";
const userId = parseUserId("user_123");
console.log(userId.value);Do not label non-shell content as bash. Configuration belongs in a single fenced block with the correct language label, not in a shell code group.
Link out instead of duplicating long documentation
A README should be a good front door, not the whole building. Link to longer files when details would bury the quick path. Good candidates include architecture notes, contributing rules, security policy, release process, and generated API references.
Use descriptive link text. "Read the contributing guide" is better than "click here" because it stays meaningful when scanned out of context, and it reads clearly to anyone using a screen reader who hears links out of their surrounding text.
When documentation lives in several places, make the README the route map. The reader should know where to go next and what each link is for.
Make status explicit
A repository can be active, experimental, archived, internal, deprecated, or a historical reference. Say so. Status changes how readers interpret risk.
If the project is not ready for production use, write that directly. If the public API is unstable, say what may change. If the repository is no longer maintained, add a short note near the top and point to the replacement if one exists.
Do not hide status in release notes. The README is where new readers arrive.
Avoid badge clutter
Badges can show useful machine status, such as build, package, licence, or coverage state. They should not become a visual header that delays the actual explanation.
Use a badge only when a reader can act on it or trust it as live status. Remove stale badges. A failing badge that nobody owns is not transparency. It is decay.
Maintain the README as code changes
A README becomes misleading when it is treated as a launch artefact. Update it in the same pull request as changes to behaviour, commands, public APIs, configuration, or directory structure.
The best review question is simple: would a newcomer succeed if they followed this after the change?
Conclusion
A readable README is direct, current, and operational. Start with purpose, give the shortest successful path, map the repository at a high level, separate usage from development, and link to deeper documentation only when the reader needs it. The result is not more prose. It is less uncertainty.
