Writing a Good README

A README is the first file most people see when they land on your repository — GitHub automatically renders it right on the repo’s homepage. A good README explains what your project does, how to install and use it, and how to contribute, in a way that a stranger can follow without asking you a single question. A bad or missing README is one of the most common reasons a genuinely useful project gets ignored.

Overview: What a README Is and Why It Matters

Technically, a README is nothing special to Git itself — it’s just a file, usually named README.md, committed like any other. Git doesn’t know or care that it exists; there is no git readme command. The magic happens on GitHub’s side: when you visit a repository page, GitHub looks in the tree of your default branch for a file named README (case-insensitive) and, if it finds one, renders it as HTML directly below the file listing.

GitHub checks a few locations, in order: the repository root first, then a .github/ folder, then a docs/ folder. It also understands several markup formats — Markdown (.md), plain text (.txt), reStructuredText (.rst), AsciiDoc (.adoc), and a bare README with no extension — but README.md using GitHub Flavored Markdown (GFM) is by far the most common and the one this lesson focuses on.

Where a README Fits in the Object Model

Because a README is just a tracked file, it lives in Git’s object model like everything else: its contents are stored as a blob, referenced by a tree (the snapshot of your project’s root directory), referenced by a commit. When you edit and commit README.md, Git writes a new blob for the new content, a new tree that points at that new blob instead of the old one, and a new commit object pointing at that tree — the branch pointer then moves to the new commit. Nothing about this is README-specific; what makes the file special is purely GitHub’s convention of looking for it by name and rendering it.

Anatomy of a Great README

Most strong READMEs follow a predictable shape, roughly in this order:

  • Title and one-line description — what the project is, in plain language.
  • Badges (optional) — build status, package version, license, code coverage.
  • Description — a short paragraph on the problem it solves and why it exists.
  • Table of contents (for longer READMEs) — links to the sections below.
  • Installation — exact commands to get it running.
  • Usage — a minimal, copy-pasteable example.
  • Configuration — environment variables, config files, options.
  • Contributing — how to submit changes, link to CONTRIBUTING.md if you have one.
  • License — which license applies, linking to the LICENSE file.

Syntax: Markdown Basics for READMEs

A README is written in GitHub Flavored Markdown. You don’t need much of it to write a great one — headings, lists, links, code blocks, and emphasis cover 95% of real-world READMEs.

Element Markdown Purpose
Heading ## Section Title Creates a section; GitHub auto-generates a link anchor from it
Bold / italic **bold** / *italic* Emphasis
List - item or 1. item Bulleted or numbered steps
Link (url) Hyperlinks, including in-page anchors like #installation
Inline code `code` Command names, flags, file names
Code fence triple backticks with a language tag Multi-line, syntax-highlighted command blocks
Blockquote > note Callouts and warnings
# H1 heading
## H2 heading

**bold text** and *italic text*

- bullet item
- another item

1. first step
2. second step

[Link text](https://example.com)

`inline code`

```bash
echo "fenced code block"
```

> Blockquote for notes

Every ## and ### heading becomes a clickable anchor: GitHub lowercases the heading text, replaces spaces with hyphens, and strips punctuation. A heading ## Getting Started becomes the anchor #getting-started — which is exactly what a table-of-contents link like [Getting Started](#getting-started) points at.

Examples

Example 1: Create and Commit a Minimal README

Every new repository should get a README in its very first commit. This keeps the repo non-empty (which lets you push and branch normally) and gives collaborators context from commit zero.

mkdir todo-cli && cd todo-cli
git init
echo "# todo-cli" > README.md
git add README.md
git commit -m "docs: add initial README"

Output:

Initialized empty Git repository in /home/user/todo-cli/.git/
[main (root-commit) 4f2a9c1] docs: add initial README
 1 file changed, 1 insertion(+)
 create mode 100644 README.md

git init creates the repository; the echo command writes a single Markdown heading into README.md; git add stages it into the index; git commit writes a blob for the file, a tree for the directory, and a commit object, then moves the main branch pointer to that commit. Push this to GitHub and the heading is all you’ll see rendered on the repo page — a starting point to expand on.

Example 2: A Well-Structured README Template

Here’s a realistic README for a small CLI tool, showing most of the sections described above in one file.

# TaskFlow

A lightweight command-line task manager written in Python.

[![Build Status](https://img.shields.io/github/actions/workflow/status/yourname/taskflow/ci.yml?branch=main)](https://github.com/yourname/taskflow/actions)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![PyPI version](https://img.shields.io/pypi/v/taskflow.svg)](https://pypi.org/project/taskflow/)

TaskFlow lets you add, complete, and organize tasks from the terminal, with tags and due dates.

## Table of Contents

- [Installation](#installation)
- [Usage](#usage)
- [Configuration](#configuration)
- [Contributing](#contributing)
- [License](#license)

## Installation

```bash
pip install taskflow
```

## Usage

```bash
taskflow add "Write README" --due 2026-08-05 --tag docs
taskflow list --tag docs
```

## Configuration

TaskFlow reads settings from `~/.taskflowrc`. See [docs/configuration.md](docs/configuration.md) for all options.

## Contributing

Pull requests are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before submitting one.

## License

Distributed under the MIT License. See [LICENSE](LICENSE) for details.

Notice the badges at the top are just Markdown images wrapped in links — each one fetches a small SVG from a badge service and links out to the relevant page (Actions, license text, package registry). The table of contents links resolve to auto-generated anchors from the headings below them. And the code fences inside the README (```bash) are themselves Markdown — they get GitHub’s syntax highlighting when rendered, which is why copy-pasteable install and usage commands should always live inside a fenced block, not inline text.

Example 3: Bootstrapping a New GitHub Repo With a README

If you’re starting from scratch on GitHub itself rather than locally, the gh CLI can create the repository and seed it with a README in one step.

gh repo create taskflow --public --add-readme
gh repo view --web

Output:

✓ Created repository yourname/taskflow on GitHub
  https://github.com/yourname/taskflow
Opening github.com/yourname/taskflow in your browser.

--add-readme tells GitHub to generate the repository with a starter README.md already committed, so the repo isn’t empty and you can immediately clone it and branch from main. gh repo view --web just opens the new repo page so you can see the rendered result.

How GitHub Renders a README Step by Step

When you load a repository page, GitHub does roughly the following:

  1. Resolves the default branch (usually main) and reads the tree at its latest commit.
  2. Searches that tree for a file matching README (case-insensitive) in the root, then .github/, then docs/, preferring recognized markup extensions.
  3. Fetches the blob content of the matching file.
  4. Runs it through its Markdown-to-HTML renderer, sanitizing any raw HTML for safety and applying syntax highlighting to fenced code blocks based on their language tag.
  5. Generates an anchor id for every heading so in-page and cross-file links (like #installation) work.
  6. Resolves relative links and images against the current branch and path, so docs/configuration.md in the README points at that file as it exists on main.

This is also why a broken relative link in a README often isn’t obvious until you push: it renders fine in a local Markdown previewer but 404s on GitHub if the target path or branch name doesn’t match reality.

Common Mistakes

Mistake 1: Absolute local file paths for images

Pointing an image at a path that only exists on your own machine breaks it for everyone else.

![Screenshot](C:\Users\me\Desktop\screenshot.png)

The fix: commit the image into the repo (e.g. under docs/images/) and reference it with a relative path such as ![Screenshot](docs/images/screenshot.png), or upload it through GitHub’s drag-and-drop editor, which gives you a stable user-images.githubusercontent.com URL.

Mistake 2: No license section, and no LICENSE file

Without a LICENSE file, GitHub shows a banner warning that the project has no license, and by default copyright law means nobody else has permission to use, modify, or redistribute your code — even if it’s public. Add a LICENSE file (GitHub’s repo creation flow can generate one for you) and link to it from a License section in the README.

Mistake 3: A wall of unstructured text

A README that’s one long paragraph with no headings is hard to scan, and GitHub can’t build a useful table of contents or anchors from it. Break content into ## sections (Installation, Usage, Configuration, and so on) so readers can jump straight to what they need.

Mistake 4: Stale badges after renaming the default branch

A CI badge URL often hardcodes a branch name, like ?branch=main. If you rename your default branch (e.g. from master to main) without updating the badge URL, it silently shows the wrong status or a broken image. Update badge URLs whenever you rename branches or move CI workflow files.

Best Practices

  • Put installation and usage instructions near the top — most readers never scroll past the first screen.
  • Show a copy-pasteable command block for the fastest path to running the project.
  • Keep the README in sync with the code; treat outdated instructions as a bug.
  • Use relative links for anything inside the repo so they work on forks and other branches too.
  • Add a Contributing section (or link to CONTRIBUTING.md) if you want pull requests from others.
  • Prefer a small number of meaningful badges over a long row of decorative ones.
  • Write commit messages for README changes the same way as code, e.g. docs: clarify installation steps, following Conventional Commits.
  • For large projects, keep the root README short and link out to a docs/ folder for deep detail.

Practice Exercises

  • Create a new local repository, add a README.md with a title, a one-paragraph description, and an Installation section, then commit it with a Conventional Commits-style message. Push it to GitHub and confirm it renders on the repo homepage.
  • Add a Table of Contents to that README linking to at least three ## sections using anchor links, and verify each link actually jumps to the right section on GitHub.
  • Intentionally add an image reference with a broken relative path, push it, and observe the broken image on GitHub. Then fix it by committing the image into a docs/images/ folder and updating the path.

Summary

  • A README is just a tracked file (usually README.md) that GitHub automatically finds and renders on your repo’s homepage.
  • GitHub looks for it in the root, then .github/, then docs/, and renders it with GitHub Flavored Markdown.
  • Good READMEs follow a predictable structure: title, description, badges, table of contents, installation, usage, configuration, contributing, and license.
  • Headings auto-generate anchors, which is what makes in-page table-of-contents links work.
  • Common failures are broken relative paths, missing license information, unstructured walls of text, and stale badges.
  • Commit and update your README the same way you commit code — as part of the same workflow, not an afterthought.