A README is the file a stranger reads first, usually at the root of a repository: what the project is, how to run it, and where things live. What belongs in a README for an application, as opposed to an open-source library, is 8 sections, and the one most templates leave out is the map: which folder owns which responsibility.

What belongs in a README: the eight sections for an application

An application’s README needs 8 sections: what it is, where it runs, how to run it locally, the repository map, one core feature traced through the code, the commands, configuration and services by name, and where the rest of the documentation lives. A library README’s badges, install snippet and contribution guide are optional unless the project is public.

For any project, GitHub’s docs on README files say in general terms what should be included in a README: it typically covers what the project does, why it is useful, how users can get started, where they can get help, and who maintains and contributes to it. GitHub surfaces a README to repository visitors when it sits in the repository’s hidden .github directory, the root or the docs directory.

That general list is a starting point. An application also has environments, services, folders and a flow that earns the money, and the next developer needs all of them. A documented repository layout is one of the engineering standards for AI-assisted teams: rules written down once, so a new hire and a coding assistant work to the same ones. To write a README for an application, go a section at a time in this order; the table is my working rule.

#SectionThe question it answersTarget lengthWhat makes it go stale
1What it isWhat does the product do, who uses it, and what state is it in?One paragraphA pivot, a rename, a retired feature
2Where it runsWhere are production and staging, on which host and database provider, and who has access?A short list of names, never credentialsA host or database move, someone leaving
3Run it locallyHow do I get it running on my machine?Three lines and a pointer to the setup guideA new prerequisite, a changed command
4The repository mapWhere does each responsibility live?A two-level tree, under one screenA new top-level folder, a moved module
5One core feature, tracedHow does the flow that matters most move through the code?Six to eight numbered hopsA refactor of that flow, a renamed file
6CommandsHow do I test, lint, type-check, migrate, seed and deploy, and what does each touch?One line per commandA renamed script, a new CI step
7Configuration and servicesWhich environment variables and third-party services exist, and where does each value come from?One line per variable and per serviceA new variable, a provider switch
8Where the rest livesWhere are the runbooks, the architecture diagram, the decisions, the AI instruction file, the license, and who do I ask?A short list of links and namesA moved document, a new owner

The run-it-locally section stays short on purpose: the prerequisites, the one command, the local URL and a fixture login. Every other setup step belongs in the setup guide, the one written for when a new developer cannot run the project locally, and the README points to it instead of copying it.

Where it runs and the map are the two sections a library README never needs, and they are the two an application’s next developer cannot work without. The configuration section names every environment variable and says where its value comes from (the host’s settings, a secrets manager, a local example file), and names every third-party service with what the app uses it for. Names only: a README is the wrong place for a value.

My working rule for length is a target: one to two screens per section at most, with the whole file readable in about ten minutes.

The README is the entry point, not the whole documentation set. Runbooks, the architecture diagram and decision records live in their own files, and the complete documentation set for a vibe-coded app sets out what each one holds.

Organizing the codebase consistently and explaining its layout in the README is deliverable 10.4 of the Production Hardening Sprint, because future developers and AI tools need to know where responsibilities belong.

What goes wrong without it: a new developer cannot find anything in the codebase

Five things go wrong when a repository has no README worth the name. The causes in the middle column are my reading.

What happensWhy, in my readingWhat prevents it
The lost first weekThe newcomer reads files at random because nothing says where billing, auth or email liveThe repository map
The same logic in three placesNobody knew a helper existed, so a person or an AI tool wrote anotherOne home per responsibility, named in the map
The AI assistant puts new code in the wrong layerA database call lands inside a component, an auth check in the client, because the repository never said where responsibilities belongThe responsibilities table and the AI instruction file
The README that liesIt describes the project as it was months ago, and people trust itA named owner, a change trigger in the pull request template, a last-verified date
The handover that fails reviewA buyer’s or client’s engineer opens the repository and cannot tell what is generated, what is dead and what is coreMap comments that mark generated, dead and core folders

Preventing the duplicate logic in the second row takes one line in the map per responsibility. Finding and removing copies that already exist is a separate cleanup, part of knowing how to find unused code in a repo.

In a small AI classifier I audited, the code, the config key name, the page footer and the README named three different AI providers for one API call. Following the README produced an error naming a key that appears nowhere in the setup instructions. The lesson I take from it: a README is only as true as the last time someone followed it. That kind of drift builds up quietly, and technical debt in AI-generated code covers how to measure it.

How to do it on the common stacks

Five pieces do the work: a folder convention, the repository map, one traced feature, a pointer to the AI instruction file, and a rule for keeping the file true.

When the codebase has no folder conventions: pick one and write it down

A codebase with no folder conventions is fixed by writing one rule per responsibility, not by reorganizing everything. Follow the framework’s reserved folders first, choose by layer or by feature for the rest, give data access, authorization, external clients and jobs one home each, and move only the files that break the rule.

Start with what the framework already decides. Next.js’s project structure docs name its top-level folders: app for the App Router, pages for the Pages Router, public for static assets to be served, and src as an optional application source folder. The same page says Next.js is unopinionated about how you organize and colocate your project files, and its advice is to choose a strategy that works for you and your team and be consistent across the project. In a new Rails application, the Rails getting started guide says app/ contains the controllers, models, views, helpers, mailers, jobs and assets for your application. Django’s first tutorial separates a project, a collection of configuration and apps for a particular website, from an app, a web application that does something.

Beyond the reserved folders, two conventions work. One splits by layer: UI, API handlers, services, data access, external clients and shared types each get a folder. The other splits by feature, so each feature folder holds its own UI, handlers and data access. Either is fine in my reading; a repository that does both at once with no rule for which wins is the one that loses people.

Whichever you pick, each responsibility gets one home and one place it must never appear. This table is my working rule.

ResponsibilityIts one homeWhat must never live there
Data accessOne data-access module, or one per featureNo database calls in components or pages
Authorization checksThe server-side handler or a shared policy moduleNo check that only runs in the client
External API clientsOne client module per providerNo scattered fetch calls that read the key inline
Background jobsOne jobs folder, one file per jobNo slow work done inline in a request handler
Email templatesOne templates folderNo HTML strings built inside handlers
Shared typesOne types folder, or the feature’s own types fileNo second copy of the same type
Configuration readingOne config module that reads environment variablesNo secrets read outside the config module
Generated codeOne folder marked generated, do not editNo edits the next generation run will overwrite

Folders called utils or helpers get a written rule for what may go in them, or they get emptied into the homes above.

Adopting a convention late, my working rule is to never reorganize the repository in one change. Write the rule down, then move only the files that break it and are being touched anyway, in small changes behind the test suite, and record each exception in the map until it is gone. For folder names, use whatever the framework uses, lowercase, and keep it consistent.

The repository map: the section most templates skip

The repository map is a tree two levels deep with one comment per folder saying what it is responsible for, what must not live there and what it may import. A short table beside it answers where a new route, table, email, job or page goes.

Two levels is my working rule, deep enough to place every responsibility and short enough to read in one screen. It is a map of responsibilities, not a list of files. Generated and vendored folders are marked so nobody edits them, and dead or legacy folders are marked as such until they are removed. Here is an illustrative map for a Next.js app organized by feature, with app as the router’s folder.

.
├── app/          # routes and pages only; no database calls; may import features/ and lib/
│   └── api/      # HTTP handlers: validate input, call a feature service, return
├── features/     # one folder per feature; may import lib/ and db/
│   ├── billing/  # checkout service, payment client, webhook logic
│   └── auth/     # session checks and the authorization policy
├── db/           # schema and migrations; only feature data modules import it
├── lib/          # config (the only file that reads env), logger, shared types
├── jobs/         # background jobs, one file each; may import features/
├── emails/       # email templates; no data access
├── generated/    # generated code: do not edit, rerun the codegen command
├── legacy/       # dead, kept until its last import is removed; add nothing here
└── docs/         # runbooks, decisions, the architecture diagram

Beside the tree, a short table answers the questions a new developer asks in their first changes.

I need to add…Where it goesWhat to copy
A new API routeA handler under app/api/, calling a service in the feature folderThe nearest handler in the same feature
A new table or columnA new migration in db/, then the feature’s data moduleThe most recent migration
A new emailA template in emails/, sent from the feature’s serviceA template with the same layout
A new background jobOne file in jobs/, enqueued from the feature’s serviceThe simplest existing job
A new pageA route folder under app/, with data fetched through the feature folderA sibling page in the same section

GitHub’s docs say you can define relative links in rendered files to help readers navigate to other files in your repository, so the folder paths in the table beside the tree can link straight to each folder. A large folder can carry a short README of its own, and the map points to it. The map shows folders; the architecture diagram of services and trust boundaries is a different document, part of the documentation set above.

One core feature, traced through the modules

Pick the feature that earns the money or holds the risk: checkout, signup, or the main AI call. Write its path as numbered hops with file paths and no prose. An illustrative trace for checkout in the map above:

  1. app/checkout/page.tsx: the checkout button posts the cart to the API.
  2. app/api/checkout/route.ts: checks the session and the cart, calls the billing service.
  3. features/billing/checkout.ts: creates the order row and the payment session.
  4. Tables touched: orders and order_items written, products read.
  5. Outbound call: the payment provider’s checkout API, through features/billing/payment-client.ts.
  6. app/api/webhooks/payments/route.ts: verifies the provider’s signature, marks the order paid.
  7. jobs/send-receipt.ts: enqueued by the webhook handler, sends emails/receipt.tsx.

This is what the verify step below follows, and what a reviewer reads first. Recording the same path as a narrated tour is the next step, and a separate subject: a recorded codebase walkthrough.

The README and the AI instruction file

AI coding tools read their own instruction file: AGENTS.md, CLAUDE.md for Claude Code, or Cursor rules. The README is not that file. What goes in an AGENTS.md file covers its contents, including the advice not to paste the README and to pull out only the commands and the few paths where a wrong edit is costly, and AGENTS.md vs CLAUDE.md sorts the facts every tool shares from those that stay with one. CI checks that enforce the rules are the guardrails for AI coding agents. A README is also input to any agent that opens the repository, so text copied in from third-party READMEs is untrusted input (what is prompt injection).

Keeping it true: who updates it, and when

The documentation article linked above already covers owners and change triggers for the whole set, in its section on keeping documentation current; here the idea applies to the README alone. My working rules:

  • The README has an owner, named.
  • The pull request template carries one line: does this change the map, the commands, the env variables or the services?
  • A link checker runs in CI on every Markdown file.
  • A last-verified date sits under the title and moves only when someone re-runs the test below.
  • The sections that go stale fastest, commands and environment variables, are generated or checked by a script where that is cheap.

How to verify it: follow the README from a clean clone

A README is verified in two parts. Someone who did not write it follows it from a clean clone to a running app, running its commands. Then they find every hop of one traced feature using only the map and trace, within about 15 minutes, my target, and an AI coding tool asked the same questions names the same folders.

Both parts are my working method. Part one (checks 1 to 3) follows the README, and part two (checks 4 to 6) traces through it.

  1. 01 A person who did not write the README takes a clean checkout on a machine that has never had the project and follows the Run it locally section with no help. Pass: the app is running.
  2. 02 They run every command in the commands section once. Pass: each one works, or the section is corrected.
  3. 03 They open every link and path in the README. Pass: none is broken.
  4. 04 They pick the traced feature and, using only the map and the trace, open each hop's file. Pass: every hop found within about 15 minutes, a target.
  5. 05 They answer three where-would-you-add questions from the table. Pass: their answers match the convention.
  6. 06 The same three questions go to the team's AI coding tool in a fresh session with only the repository as context. Pass: it names the same folders.

The full clean-machine procedure, with a stopwatch and a deviation log, belongs to the setup guide named in the first section. When the AI tool answers wrong, log it and re-read the map line it concerns; in my reading it counts as a README defect only when that line is missing or unclear, since a tool can answer wrong with a correct map.

Every wrong turn the person takes is logged as a defect in the README, not in the reader. Keep the defect log, the time taken, the date and who ran it. An illustrative log:

StepWhat the README saidWhat was trueFixed?
Where it runsStaging on the old hostStaging had moved to a new hostYes, host name updated
CommandsThe migrate command nameThe script had been renamedYes
Trace, webhook hopThe webhook handler’s pathThe handler had moved to another folderMap and trace updated

This is how deliverable 10.4 is verified in the Production Hardening Sprint: we follow the README from a clean checkout and trace a core feature through its documented modules.

Where the sprint does this

In the sprint, this page’s subject is deliverable 10.4, done and verified as described in the first section and the verify section. Four others sit close to it. Deliverable 10.10 provides one documented command that brings the application up locally, with an environment example and seed data. Beside it, deliverable 10.1 removes dead code and consolidates duplicated logic while preserving required behavior. For AI tools, deliverable 10.8 provides CLAUDE.md, AGENTS.md, Cursor rules, or equivalents describing conventions and protected patterns, and adds CI checks for enforceable rules. At handover, deliverable 13.1, the production readiness report, is verified this way: we account for all 123 IDs, keep failures visible until resolved and explain genuine non-applicable items. Outside the sprint: building new product features or modules, completing unfinished core features or business workflows, and rebuilding core functionality that does not yet perform its intended job. Every item is listed in the published scope.

Common questions about README files

Is a README file just a text file?

Yes. A README is plain text, often written in Markdown, which CommonMark describes as a plain text format for writing structured documents. Code hosts render it: GitHub recognizes a README in the repository’s hidden .github directory, the root or the docs directory and surfaces it to repository visitors.

Should README be MD or TXT?

MD, for any repository on a host that renders Markdown. For the rendered view of any Markdown file, including a README, GitHub generates a table of contents from the section headings, which a long application README needs. A .txt README only makes sense where nothing will render it, in my reading.

What is the correct syntax for a README file?

There is no README-specific syntax, only the syntax of the file’s markup: for a README.md, Markdown’s headings, lists, code fences and relative links, while GitHub also renders other markups such as reStructuredText and AsciiDoc. On GitHub the Markdown dialect is GitHub Flavored Markdown, which its spec calls a strict superset of CommonMark and the dialect currently supported for user content on GitHub.com.

What does a README file look like?

On GitHub, it is the rendered file shown to repository visitors, with a table of contents generated from its section headings under the Outline menu icon. For an application, it is the eight headings from the table near the top of this page, in that order, each with a few lines under it.