README: первое, что видит новый человек
Инженер выходит в новую команду. Ему выдают доступы и ссылку на репозиторий orders.
Он клонирует его, открывает README.md и видит четыреста строк: логотип, восемь бейджей,
раздел «Philosophy», подробное описание доменной модели, таблицу из сорока переменных
окружения, схему деплоя в Kubernetes и в самом конце — «For local development see the wiki».
Wiki переехала в Confluence два года назад. Он тратит день, чтобы поднять сервис локально,
и половину следующего — чтобы понять, что половина переменных из таблицы уже не читается кодом.
В это же время соседняя команда выбирает библиотеку для загрузки данных в ClickHouse. Открывают три репозитория. Первый README начинается словами «A modern, blazing-fast, cloud-native data platform» — его закрывают через тридцать секунд, потому что непонятно, это библиотека, сервис или SaaS. Выбор достаётся третьему, где в первых десяти строках написано, что это Go-библиотека, что она делает и чего не делает.
Оба случая — про одну и ту же ошибку. README писали как витрину проекта, а читают его как справочное бюро на вокзале: человек стоит с чемоданом и хочет знать, туда ли он приехал, на какой платформе поезд и у кого спросить, если что-то пошло не так.
README — не описание проекта. Это документ, который за шестьдесят секунд помогает читателю принять три решения: «это то, что мне нужно?», «как это запустить?» и «к кому идти дальше?». Всё, что не помогает принять одно из трёх, — не в README.
Это тот же принцип, что и в главе «Читатель и решение», доведённый до предела: у ADR читатель пришёл целенаправленно — его никуда не денешь. У README читатель случайный, нетерпеливый и не в контексте, и стоимость его ухода вы не увидите ни в одном тикете.
Три решения, ради которых открывают README
| Решение | Кто читает | Бюджет | Как выглядит провал |
|---|---|---|---|
| «моё ли это» | внешний разработчик, архитектор, оценивающий зависимость, безопасник | 5–30 с | закрытая вкладка, о которой вы никогда не узнаете |
| «как получить первый рабочий результат» | новый человек, контрибьютор, дежурный, автор смежного сервиса | 1–10 мин | сообщение «а как это запустить?» и ваш час на ответ |
| «куда идти дальше» | тот, кто уже внутри и ищет runbook, ADR, справочник, владельца | секунды | поиск по всему вики и найденная пятая по счёту версия документа |
что это и для кого?"} B -- "нет" --> X1["Ушёл. Вы не узнаете"] B -- "да" --> C{"Видно, подходит ли
по версиям и платформе?"} C -- "нет" --> X2["Пробует наугад,
тратит своё и ваше время"] C -- "да" --> D{"Есть копируемая
последовательность команд?"} D -- "нет" --> X3["Собирает команды
из прозы, ошибается"] D -- "да" --> E{"Совпал ли результат
с обещанным?"} E -- "нет" --> F{"Есть раздел
«если не работает»?"} F -- "нет" --> X4["Пишет в чат
или бросает"] F -- "да" --> G["Починился сам"] E -- "да" --> G G --> H{"Понятно, куда идти
за глубиной?"} H -- "нет" --> X5["Ищет по всему вики"] H -- "да" --> I["README сработал"]
Пять точек выхода и одна точка успеха: каждая развилка на схеме — конкретный раздел, которого либо нет, либо он написан не так.
Числа условны, пропорция реалистична: даже у хорошего README до рабочего результата доходит меньшинство открывших. Ценность картинки в том, что воронку можно измерить и чинить адресно, а не переписывать документ целиком «чтобы стало лучше».
README — не документация, а диспетчер
Самая частая структурная ошибка: README пытается быть всей документацией проекта. Внутрь затаскивают описание архитектуры, справочник по эндпоинтам, историю миграций, процедуру ротации секретов. Через год это файл на восемьсот строк, где половина разделов врёт, и читатель не может отличить живые от мёртвых. Правильная модель — диспетчер: README содержит минимум, остальное адресует. Это принцип слоёв из главы «Структура», только верхний слой физически отделён от нижних и живёт в отдельном файле.
Как решать, что оставить внутри, а что вынести: два измерения — как быстро содержимое меняется и как рано оно нужно читателю.
Из квадранта справа сверху следует правило, которое стоит запомнить дословно: таблицу, которую можно сгенерировать, не пишут руками. Список переменных окружения, флаги CLI, версии зависимостей, поддерживаемые платформы — всё это машина знает точнее вас и не забудет обновить. К генерации вернёмся ниже.
Разные репозитории — разные README
Жанр один, но читатель и его решение меняются радикально. Ниже — пять типовых ситуаций.
| Тип репозитория | Главный читатель | Его решение | Что обязано быть в первых 20 строках |
|---|---|---|---|
| Публичная библиотека | внешний разработчик | брать или не брать | что делает, чего не делает, установка, минимальный пример, лицензия, статус поддержки |
| Внутренний сервис | новичок, дежурный, смежная команда | как поднять и к кому идти | что делает в бизнес-терминах, владелец и канал, локальный запуск, ссылка на runbook и дашборды |
| CLI-инструмент | инженер с конкретной задачей | как выполнить свою команду | установка на три платформы, три реальных примера вызова, --help |
| Пакет в монорепозитории | автор смежного пакета | как собрать и от чего зависит | место в системе, публичный контракт, как собрать только этот пакет |
| Архивный репозиторий | случайный человек | стоит ли вообще читать дальше | «не поддерживается с такой-то даты, вместо него используйте X» |
Последняя строка — не шутка. README архивного репозитория, честно говорящий «мертво, идите туда», экономит больше человеко-часов, чем большинство живых README. Один абзац, написанный в день заморозки, отменяет десятки будущих часов чужого расследования.
Анатомия: порядок разделов и что в них решается
Порядок ниже — не эстетика, а очерёдность решений читателя. Каждый раздел отвечает ровно за одну развилку из первой схемы.
| № | Раздел | Что в нём решается и чем он ограничен |
|---|---|---|
| 1 | Заголовок и одно предложение | «X — это <класс объекта>, который делает <что> для <кого>». Класс объекта обязателен: библиотека, HTTP-сервис, CLI, Terraform-модуль, шаблон — без него неясно, что с этим вообще делать |
| 2 | Границы: чего это не делает | Два-три предложения. Самый недооценённый и самый дешёвый раздел: останавливает не того читателя за пять секунд вместо тридцати минут |
| 3 | Статус | Экспериментальный, стабильный, в поддержке, заморожен — плюс дата. По нему принимается решение о риске |
| 4 | Быстрый старт | Одна копируемая последовательность, ожидаемый результат, время до него. Самая ломкая часть документа |
| 5 | Минимальный пример | Для библиотеки — двадцать компилирующихся строк, для сервиса — запрос и ответ, для CLI — три команды из жизни |
| 6 | Требования и совместимость | Версии рантайма, ОС, внешние зависимости. Кандидат на генерацию |
| 7 | Конфигурация | Не таблица на восемьдесят строк, а ссылка на сгенерированный документ плюс пять переменных, без которых ничего не поднимется |
| 8 | Как запустить тесты | Одна команда. Часто это настоящая точка входа: тесты показывают, как код задуман к употреблению |
| 9 | Куда дальше | Пять-семь ссылок, у каждой строка пояснения «зачем туда идти». Голый список ссылок не работает |
| 10 | Владелец и поддержка | Команда, канал, что делать с багом и что — с вопросом |
| 11 | Как внести изменение | Ссылка на CONTRIBUTING.md, а не пересказ процесса |
| 12 | Лицензия | Одна строка и SPDX-идентификатор |
Разделы 1–4 обязаны помещаться на первый экран без прокрутки. Это жёсткое ограничение, из которого следует всё остальное: если раздел «Философия» стоит между заголовком и быстрым стартом, он отодвигает вниз то, ради чего README открыли.
Разбор: один и тот же README до и после
Ниже — четыре пары. В каждой сначала реальный по духу текст, потом переписанный, потом разбор, что именно изменилось.
Пара 1. Первое предложение
Было:
# Nebula
Nebula is a modern, high-performance, cloud-native platform for building
scalable data pipelines with a best-in-class developer experience.
Built with love by the Data Platform team.
Стало:
# Nebula
Nebula — Go-библиотека для батчевой записи в ClickHouse: принимает поток строк,
собирает батчи по размеру и таймауту, ретраит с бэкоффом, гарантирует at-least-once.
**Не подходит**, если нужны exactly-once, доставка быстрее секунды
или запись куда-то, кроме ClickHouse. Требует Go 1.22+ и ClickHouse 23.8+.
Разбор. В исходном варианте пять прилагательных и ни одного проверяемого утверждения. Слово «platform» не говорит, что это: библиотеку подключают, сервис разворачивают, SaaS покупают — три разных решения. Читатель не определяет ни класс объекта, ни область применимости, ни совместимость, поэтому уходит к соседнему проекту.
В переписанном варианте первое же слово после тире задаёт класс, дальше идёт глагольное
перечисление того, что происходит внутри: по нему инженер за секунду понимает, совпадает ли
модель с его задачей. Абзац «не подходит» — не признание слабости, а фильтр: он экономит
время того, кому проект не подойдёт, и повышает доверие того, кому подойдёт. Гарантия
at-least-once названа явно — по этому параметру библиотеки такого класса и выбирают.
Про приёмы уровня фразы подробнее в главе «Ясность».
Пара 2. Быстрый старт
Было:
## Installation
First, make sure you have all the prerequisites installed. You will need
a fairly recent version of Node, as well as Yarn (npm may work but is not
officially supported). Then clone the repository and install the dependencies.
After that you should be able to start the development server, provided your
environment variables have been configured correctly. See the wiki for details.
Стало:
## Быстрый старт
Нужны Node 20+ и Docker 24+. Занимает около двух минут.
```bash
git clone git@github.com:acme/orders.git && cd orders
cp .env.example .env # значения по умолчанию рабочие, править не нужно
make dev # поднимет Postgres, применит миграции, запустит приложение
```
Готово, когда `curl -s localhost:3000/health` отвечает `{"status":"ok"}`.
**Если не работает:**
- `port 5432 already in use` — занят локальный Postgres: `make dev PG_PORT=55432`
- зависло на `waiting for migrations` дольше 90 с — `make dev-reset` и повторить
- всё остальное — канал `#orders-support`, приложите вывод `make doctor`
Разбор. Исходный текст — инструкция, пересказанная прозой: читатель сам реконструирует последовательность команд, а каждое «fairly recent», «may work», «provided … correctly» перекладывает на него решение, которое должен был принять автор. Ссылка «see the wiki» в разделе быстрого старта означает, что быстрого старта нет.
В переписанном варианте требования стоят перед блоком (читатель отсеивается до того, как потратит время), команды собраны в один копируемый блок, у блока есть явное определение успеха — конкретная строка ответа, а не «должно работать», — и указано ожидаемое время, чтобы человек понимал, ждать ему или чинить. Раздел «если не работает» покрывает две-три самые частые поломки: они известны любому, кто хоть раз проводил онбординг, и стоят пяти минут написания. Последний пункт даёт эскалацию с готовым диагностическим выводом.
Пара 3. Раздел «Архитектура»
Было:
## Architecture
The project follows a clean architecture approach. The `internal/domain`
package contains entities and value objects. The `internal/usecase` package
contains application services. The `internal/adapters` package contains
implementations for Postgres, Kafka and Redis. The `internal/transport`
package contains HTTP handlers and gRPC servers. The `pkg/utils` package
contains shared utilities.
Стало:
## Как устроен сервис
Приём заказа → валидация → запись в Postgres → событие в Kafka через outbox.
Публикация не прямая: причина и цена решения — в [ADR-0007](docs/adr/0007-outbox.md).
Точки входа для чтения кода:
- HTTP-ручки: `internal/transport/http/router.go`
- бизнес-сценарии: `internal/usecase/`
- запись событий: `internal/adapters/outbox/`
Схема границ и зависимостей: [docs/architecture.md](docs/architecture.md).
Разбор. Первый вариант — пересказ дерева каталогов: он устареет при первом переименовании пакета и не отвечает ни на один вопрос читателя. Структура каталогов и так видна в репозитории, а вот почему события уходят через outbox, а не напрямую в Kafka, не видно нигде. Второй вариант даёт поток данных одной строкой, отмечает нетривиальное решение и ссылается на ADR вместо пересказа — формат записи разбирали в главе «ADR». Дальше не полный перечень пакетов, а три точки входа для чтения кода: их достаточно, чтобы начать, и они меняются реже. Общий приём: README описывает то, чего не видно в коде, и не дублирует то, что видно.
Пара 4. Владелец и поддержка
Было:
## Support
If you have any questions, feel free to reach out to the team! We're always
happy to help. You can also open an issue if you find a bug.
Стало:
## Владелец и поддержка
| Что | Куда |
|---|---|
| вопрос по использованию | `#orders-support`, ответ в рабочие часы, обычно в тот же день |
| баг | issue с меткой `bug`, шаблон подставится |
| инцидент в проде | дежурный `#orders-oncall`, эскалация в PagerDuty, сервис `orders` |
| README врёт или устарел | issue с меткой `docs` — берём в ближайший спринт |
Владелец: команда Orders Platform, `CODEOWNERS` в корне репозитория.
Ревизия README: 2026-05-14.
Разбор. «Feel free to reach out» не содержит ни адреса, ни ожидания по времени, ни разделения по типу обращения — читатель пишет в общий чат, и вопрос теряется. Переписанная версия раскладывает обращения по каналам, называет ожидаемое время ответа и отделяет инцидент от вопроса: это разные пути с разной срочностью. Строка про «README врёт» — не вежливость, а механизм: она превращает читателя в источник сигнала об устаревании и делает правку документации обычной работой, а не одолжением. Про то, как принимать такие правки, — глава «Ревью текста».
Первый успех как метрика
У README есть одна честная метрика: время от git clone до первого рабочего результата
на чистой машине. В литературе про developer experience её называют time to first hello
world, и она измерима без опросов.
«На чистой машине» — существенная часть. У автора всё работает: нужная версия рантайма
уже стоит, в ~/.netrc лежит токен приватного реестра, сертификат в системном хранилище,
VPN поднят. Ни одного из этих условий у нового человека нет, и в README они не попадают,
потому что автор их не видит. Прогон на чистом контейнере снимает слепоту за один заход.
make doctor проверяет токен O->>M: CI-прогон quickstart на чистом образе M-->>R: расхождение теперь поймает робот, а не человек
Из этой петли следуют два правила, которые дёшево внедрить и легко проверить.
Правило второго вопроса. Вопрос, заданный в канале поддержки дважды, чинится в README в тот же день: не ответить третий раз, а один раз дописать. Ответ в чате обслуживает одного человека и умирает вместе с историей канала; строка в README обслуживает всех последующих.
Правило свежих глаз. Каждый новый человек проходит быстрый старт как есть, ничего не спрашивая, и заводит PR на README по итогам. Это его первая задача и первый влитый PR — заодно проверка того, что процесс контрибьюта вообще работает. Свежие глаза одноразовы: через полгода он тоже перестанет замечать пробелы. Про онбординг со стороны нанимающей команды — «Онбординг» и «Первые 90 дней».
Почему README устаревает быстрее прочих документов
У README худшее сочетание свойств из всех инженерных жанров. ADR неизменяем по природе:
он описывает решение в момент принятия, и изменившийся мир не делает запись ложной.
Постмортем описывает случившееся — оно тоже не меняется. README описывает текущее
состояние системы, а оно меняется каждым коммитом. При этом README не исполняется:
никакой тест не падает от того, что make dev переименовали в make up,
а .env.example потерял три переменные.
примеры в doctest, quickstart в CI Написан --> Непроверяемый: команды скопированы
в текст руками Проверяемый --> Расхождение_поймано: код изменился,
CI покраснел Расхождение_поймано --> Проверяемый: правка едет
в том же PR Непроверяемый --> Тихо_разошёлся: код изменился,
никто не заметил Тихо_разошёлся --> Читатель_обжёгся: команда не работает Читатель_обжёгся --> Недоверие: «в этом README всё врёт» Недоверие --> Мёртвый: перестали и читать, и править Мёртвый --> [*] Читатель_обжёгся --> Проверяемый: разовая правка
плюс проверка в CI
Ключевой переход — Читатель_обжёгся → Недоверие. Одна неработающая команда обесценивает
весь документ: читатель не знает, какие ещё разделы врут, и рационально перестаёт доверять
всем. Поэтому точность быстрого старта важнее полноты README: короткий и верный
документ работает, длинный с двумя устаревшими абзацами не работает вовсе.
Отсюда же понятно, почему призывы «не забывайте обновлять документацию» не действуют. Забывчивость — симптом, а не причина. Причина в том, что расхождение не производит сигнала: код и текст живут в разных плоскостях, и ничто их не связывает. Лечится структурно.
Семь структурных лекарств
1. Близость к коду
README живёт в том же каталоге, что и код, который описывает, и правится в том же pull request. В монорепозитории это README на каждый пакет, а не один гигантский в корне: корневой отвечает «что здесь лежит и как собрать всё», пакетные — про свой пакет (про устройство таких деревьев — «Монорепозиторий»). Проверяемое следствие: правка кода и правка README попадают в один диф, и ревьюер видит их рядом — см. «Совместная работа в Git».
2. Исполняемость: quickstart прогоняется в CI
Самая ломкая часть README — команды. Значит, их надо выполнять, а не перечитывать.
# .github/workflows/readme-quickstart.yml
name: readme-quickstart
on:
pull_request:
# прогоняем, когда меняется README или то, что он описывает
paths: ["README.md", "Makefile", "docker-compose.yml", ".env.example", "src/**"]
schedule:
- cron: "0 6 * * 1" # раз в неделю ловим гниение от внешних причин: образы, реестры
jobs:
quickstart:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: python3 scripts/run_readme_quickstart.py # выполнить блоки из README
- run: test "$(curl -sf localhost:3000/health)" = '{"status":"ok"}' # обещание
Скрипт вытаскивает помеченные блоки и выполняет их дословно — так текст буквально становится исполняемым:
#!/usr/bin/env python3
"""Выполняет блоки README с инфострокой bash quickstart, в порядке следования."""
import pathlib, re, subprocess, sys
# Обычный ```bash не трогаем — запускаем только явно помеченные блоки
BLOCK = re.compile(r"^```bash quickstart\n(.*?)^```", re.S | re.M)
blocks = BLOCK.findall(pathlib.Path("README.md").read_text(encoding="utf-8"))
if not blocks:
sys.exit("В README нет блоков bash quickstart — проверять нечего, это тоже ошибка")
for i, code in enumerate(blocks, start=1):
print(f"=== блок {i} ===\n{code}", flush=True)
# -e: падаем на первой ошибке, -u: необъявленная переменная — тоже ошибка
subprocess.run(["bash", "-euo", "pipefail", "-c", code], check=True)
Для библиотек то же делается штатными средствами языка и стоит ещё дешевле:
- Python —
pytest --doctest-glob='*.md' README.mdвыполняет примеры прямо из файла (doctest); - Rust —
#![doc = include_str!("../README.md")]вsrc/lib.rsпревращает примеры README в обычные doc-тесты, которые гоняетcargo test(документация rustdoc); - Go — функции
Exampleс комментарием// Output:проверяютсяgo testи попадают в pkg.go.dev (go.dev/blog/examples); - любой проект — mdBook test для книг
и
batsдля shell-примеров.
Правило простое: если пример в README нельзя выполнить автоматически, считайте, что он уже сломан. Вопрос только в том, когда это обнаружит читатель.
3. Генерация вместо ручного копирования
Всё, что машина знает точнее человека, машина и должна писать. Приём — «инъекция» сгенерированного фрагмента между маркерами:
<!-- [[[cog
import subprocess
out = subprocess.run(["./orders", "--help"], capture_output=True, text=True).stdout
cog.out("\n```\n" + out + "```\n")
]]] -->
<!-- [[[end]]] -->
В CI — cog --check README.md: если вывод --help изменился, а README нет, сборка падает
(cog). Тот же принцип в готовых инструментах:
terraform-docs вставляет таблицы переменных и выходов модуля
прямо в README, helm-docs делает то же
для values.yaml. Граница здравого смысла: генерируется справочная часть (флаги,
переменные, типы), руками пишется смысловая (зачем, когда применять, чего не делать).
Полностью сгенерированный README нечитаем — об этом в главе
«Документация API».
4. Владелец
Документ без владельца мёртв через квартал. У README это заметно особенно, потому что
его правит «кто угодно», то есть никто. Механизм: файл попадает в CODEOWNERS, и любой
PR, трогающий README, требует ревью владеющей команды
(про CODEOWNERS).
Владелец — команда, а не человек: человек уходит в отпуск, меняет проект и увольняется.
# CODEOWNERS
/README.md @acme/orders-platform
/docs/runbook.md @acme/orders-oncall
/docs/adr/ @acme/architects
5. Срок жизни и явная ревизия
Строка Ревизия README: 2026-05-14 делает возраст документа видимым, а робот через
180 дней после неё заводит issue «пройти README по шагам и обновить дату». Это не
призыв обновлять документацию, а задача с исполнителем и проверяемым результатом.
Совсем дёшево, без роботов: сравните git log -1 --format=%ad -- README.md с датой
последнего коммита в код — разрыв в год говорит сам за себя.
6. Ссылка вместо копии
Один и тот же текст в двух местах гниёт в двух местах и расходится сам с собой. Правило:
у каждого факта один дом, остальные ссылаются. README ссылается на runbook, а не пересказывает
его; на ADR, а не повторяет обоснование; на CONTRIBUTING.md, а не дублирует процесс.
Ссылки ломаются, поэтому их проверяет линтер в том же CI —
lychee для ссылок, markdownlint для структуры:
# падает, если хоть одна ссылка из README ведёт в никуда
lychee --no-progress README.md docs/**/*.md
7. Бюджет размера
README длиннее 250–300 строк почти всегда означает, что внутрь затащили то, что должно жить рядом. Введите лимит и относитесь к нему как к бюджету: чтобы добавить раздел, надо вынести другой. Ограничение работает лучше уговоров, потому что превращает абстрактное «не раздувайте» в конкретный выбор — а заодно даёт готовый вопрос на ревью: «какой раздел вы вынесли взамен?».
README, который пишут ради процесса
Честно: значительная часть README в крупных компаниях написана не для читателя. Их пишут, потому что в чек-листе готовности сервиса есть пункт «README присутствует», потому что каталог сервисов подсвечивает репозитории без описания красным, потому что аудит требует документированности. Само по себе это не плохо — плохо не отличать один случай от другого и тратить силы не туда.
Признаки README, написанного ради процесса:
- он идентичен README ещё в одиннадцати репозиториях, кроме подставленного имени сервиса;
- в нём есть разделы «Архитектура» и «Мониторинг», но внутри одна фраза общего вида;
- последний коммит в него — «add README per platform checklist», и он единственный;
- в нём остались плейсхолдеры вроде
TODOили чужое имя сервиса, скопированное с соседа; - никто из команды не может вспомнить, когда открывал его в последний раз.
Четыре диагностических теста, которые можно провести за час:
- Тест на первый запуск. Дайте README человеку не из команды и попросите пройти быстрый старт, не задавая вопросов. Секундомер — метрика. Если он не дошёл — README не работает независимо от его объёма.
- Тест на трафик. Посмотрите просмотры страницы репозитория за 90 дней (GitHub Insights или аналог в вашем каталоге сервисов). Ноль уникальных посещений при живом сервисе означает, что документ пишется в пустоту.
- Тест на вопрос. Выпишите пять последних вопросов в канале поддержки. Сколько из них закрывается ссылкой на README? Ноль — README не про то, что людям нужно.
- Тест на диф.
git log --oneline -- README.mdрядом с историей кода. Если README правился один раз при создании, а код — двести раз, документ описывает несуществующую систему.
Что делать, если тесты показали процессную природу. Не имитировать: раздутый шаблонный README хуже честного короткого — он создаёт иллюзию документированности и повышает шанс, что кто-то поверит устаревшему разделу. Сократить до того, что правда, и явно признать аудитора читателем: раздел, существующий для аудита, должен быть коротким, точным и помеченным как таковой. А пункты чек-листа, которые действительно полезны (владелец, канал, статус), закрыть генерацией из каталога сервисов — такие поля машина заполнит вернее человека. Про разговор с теми, кто ставит подобные требования, — «Работа вверх».
Обратная ошибка тоже встречается: «README не нужен, у нас все и так всё знают». Это верно ровно до первого увольнения и первого дежурства человека из соседней команды.
README читают не только люди
README рендерится на GitHub и GitLab, становится описанием пакета в npm, PyPI и на pkg.go.dev, вытягивается в каталоги сервисов вроде Backstage и всё чаще попадает в контекст ассистента, которого коллега просит «покажи, как этим пользоваться». Следствий три: первый абзац работает как самостоятельная аннотация во всех этих местах (никаких «как упоминалось выше»); относительные ссылки на файлы репозитория ломаются в реестрах — нужны абсолютные URL; неверный пример теперь тиражируется не только читателями, но и инструментами, которые его подхватывают. Ещё один аргумент за проверяемые примеры.
Типичные ошибки
- Витрина вместо инструкции. Логотип, слоган, восемь бейджей и «Philosophy» — до быстрого старта читатель не доскроллил.
- Проза вместо команд. Последовательность рассказана словами, читатель собирает её сам.
- Нет определения успеха. «Запустите сервер» без указания, что должно появиться в выводе, — читатель не знает, работает ли у него.
- Пересказ дерева каталогов. Дублирует видимое и устаревает при первом переименовании.
- Таблица конфигурации, написанная руками. Расходится с кодом на второй неделе.
- Ссылки без пояснений. Список из пятнадцати ссылок не помогает выбрать, куда идти.
- «Feel free to reach out». Нет адреса, нет ожиданий, нет разделения вопрос/инцидент.
- Молчание про границы. Читатель узнаёт, что проект ему не подходит, на второй день интеграции.
- README архивного репозитория без пометки о заморозке. Самая дорогая из дешёвых ошибок.
- Один гигантский README в монорепозитории. Читатель пакета продирается сквозь чужое.
- Скриншоты вместо текста команд. Не копируются, не ищутся, не проверяются CI и недоступны для скринридера — см. трек «Доступность».
Практика
- Хронометраж. Возьмите README своего текущего проекта, запустите таймер и пройдите
быстрый старт в чистом контейнере (
docker run --rm -it ubuntu:24.04 bash). Запишите время до первого рабочего результата и каждый момент, где пришлось догадываться. - Первые двадцать строк. Перепишите начало README так, чтобы за десять секунд читатель понял класс объекта, что делает, чего не делает и требования. Дайте прочитать человеку из другой команды и спросите, что это, — не подсказывая.
- Три решения. Пройдите по своему README разделом за разделом и напротив каждого напишите, какому из трёх решений он служит. Разделы без ответа — кандидаты на вынос.
- Исполняемый quickstart. Пометьте блок команд как
bash quickstart, добавьте скрипт из этой главы и job в CI. Первый прогон почти наверняка покраснеет — это и есть измерение того, насколько README разошёлся с реальностью. - Генерация и владелец. Замените самую длинную рукописную таблицу генерируемой
(
cog,terraform-docs,helm-docs), добавьте README вCODEOWNERSи поставьте строку ревизии с задачей на пересмотр раз в полгода. - Свежие глаза. Договоритесь, что первая задача любого нового человека — PR с правками README по итогам собственного онбординга. Через два найма сравните, сколько правок он приносит: падение количества и есть результат.
Источники
- About READMEs — документация GitHub: где README ищется, как рендерится, что делает README профиля.
- Art of README — разбор README как продукта для читателя, с примерами и чек-листом.
- standard-readme — формализованная спецификация разделов и линтер к ней.
- GNU Coding Standards, Releases — требование README в дистрибутиве, одна из старейших формулировок жанра.
- Diátaxis — система из четырёх жанров документации; помогает понять, что README — это в основном туториал плюс маршрутизация, а не справочник.
- Google developer documentation style guide и Write the Docs: Docs as Code — правила формулировок и практика хранить документацию рядом с кодом.
- Keep a Changelog, Contributor Covenant, SPDX License List — соседние жанры и стандартные тексты, которые часто ошибочно затаскивают внутрь README.
- pytest doctest, cog, lychee — инструменты, делающие README проверяемым.
Мини-итог
- README отвечает на три вопроса: что это, как запустить, куда идти дальше. Раздел, не служащий ни одному из них, живёт в отдельном документе, а в README остаётся ссылкой.
- Первый абзац обязан назвать класс объекта, действие и границы применимости. Прилагательные вроде «modern» и «scalable» не несут информации и стоят читателей.
- Быстрый старт — копируемый блок команд, требования перед ним, явное определение успеха после и раздел «если не работает» на две-три частые поломки.
- Одна неработающая команда обесценивает весь документ, поэтому точность важнее полноты.
- README устаревает быстрее прочих жанров, потому что описывает изменчивое состояние
и не исполняется. Лечится структурно: близость к коду, прогон quickstart в CI, генерация
справочных таблиц, владелец в
CODEOWNERS, дата ревизии, ссылки вместо копий, бюджет размера. - Метрика — время от
git cloneдо рабочего результата на чистой машине. Правило второго вопроса и правило свежих глаз поддерживают её без героизма. - Часть README пишется ради чек-листа. Распознаётся по четырём тестам — на первый запуск, на трафик, на вопрос, на диф. Лечится сокращением до правды, а не имитацией полноты.
Что дальше
README отвечает на вопрос «как начать» и намеренно останавливается там, где начинается подробность. Но читатель, у которого всё запустилось, немедленно приходит со следующим вопросом: какие ровно параметры принимает этот метод, что вернётся при ошибке, какие гарантии даёт эндпоинт и что произойдёт при повторном вызове. Это другой жанр — не рассказ и не маршрут, а контракт, где полнота важнее увлекательности, а генерация из кода перестаёт быть опцией.