Новое в журнале. Маленькие pull request: почему их быстрее ревьюят и как резать задачу
Рост и обучение

Как писать техническую документацию, которую реально читают

Разбираем три жанра технической документации: README для входа в проект, ADR для фиксации архитектурных решений и runbook для действий во время инцидента. У каждого жанра свой читатель и своя структура.

16 июня 20269 мин чтенияПрактикам
Как писать техническую документацию, которую реально читают
3 жанраREADME, ADR, runbook
5 минуториентир на время первого чтения README

Большая часть технической документации не читается не потому, что написана плохим языком, а потому, что перепутан жанр: README рассказывает историю проекта вместо того, чтобы объяснить, как его запустить, а запись о решении превращается в пересказ переписки в чате без самого решения. Три жанра, README, ADR и runbook, решают разные задачи и устроены по-разному, и смешивать их форматы вредит обоим читателям.

README: вход в проект за первые пять минут

README читает человек, который впервые видит репозиторий и хочет понять две вещи: что это за проект и как его запустить локально. Любая информация, которая не помогает ответить на эти два вопроса в первые минуты, должна уйти ниже по странице или в отдельный документ. Типичная ошибка - длинное философское вступление про историю проекта перед первой командой запуска, которую реально ищет читатель.

  1. одно-два предложения: что делает проект и для кого он
  2. требования к окружению: версия языка, база данных, внешние сервисы
  3. команды установки и запуска, именно команды, а не описание процесса словами
  4. как запустить тесты
  5. куда смотреть дальше: архитектура, вклад в проект, контакты
## Запуск

```
cp .env.example .env
docker compose up -d
docker compose exec php composer install
```

Приложение доступно на http://localhost:8080
Проверяйте README буквально: откройте его на чистой машине или в новом контейнере и пройдите шаги командой за командой. Если хотя бы один шаг не работает без дополнительного знания «само собой разумеется», README неполный.
Главная мысльДокументацию читают не ради объёма, а ради одного конкретного ответа, и чем быстрее до него добраться, тем выше шанс, что текст вообще откроют снова.

ADR: зафиксировать решение, а не процесс обсуждения

Architecture Decision Record, запись архитектурного решения, нужна не для истории переписки, а для одного конкретного вопроса, который возникнет через полгода или год: почему здесь сделано именно так, а не иначе. Хорошая запись короткая, обычно укладывается в одну страницу, и всегда содержит явно названную альтернативу, которую отклонили, с причиной отклонения, а не только выбранный вариант.

Раздел ADRЧто туда идёт
Контексткакая проблема стояла и какие были ограничения
Рассмотренные вариантыминимум два, включая отклонённый, с краткой причиной отказа
Решениечто выбрали, одно-два предложения, без оговорок
Последствиячто это решение упрощает и что усложняет, честно про обе стороны

Отдельное практическое правило: ADR не редактируют задним числом, если решение изменилось. Вместо правки старой записи создают новую, которая ссылается на предыдущую и объясняет, почему решение поменялось. Это сохраняет историю мышления команды, а не подменяет её одной финальной версией, которая выглядит так, будто решение было очевидным с самого начала.

Runbook: документ, который читают в стрессе

Runbook описывает действия во время инцидента, и его главный читатель - человек, который открывает документ в три часа ночи, когда прод не отвечает, и у него нет ни времени, ни спокойствия читать длинные объяснения. Структура runbook должна поддерживать это состояние: короткие пронумерованные шаги, конкретные команды, минимум теории про то, почему система устроена именно так.

# Runbook: API отвечает 502

1. Проверить статус сервиса:
   systemctl status app-api

2. Если сервис упал, посмотреть последние строки лога:
   journalctl -u app-api -n 100

3. Перезапустить сервис:
   systemctl restart app-api

4. Если после перезапуска ошибка повторяется в течение 5 минут,
   эскалировать на дежурного инженера базы данных.

Хороший runbook проверяют не чтением, а прогоном: попросить человека, который не писал документ, пройти по шагам вживую или на тестовом стенде и отметить, где возникла пауза или непонятный момент. Любое место, где читатель должен догадываться, что имелось в виду, в runbook недопустимо, потому что в момент реального инцидента на догадки нет времени.

Вы в начале разбораДальше: разбор компромиссов, чек-лист и ответы на частые вопросы.

Общее правило для всех трёх жанров: структура важнее объёма

Главная ошибка, общая для README, ADR и runbook, - попытка писать подробно вместо того, чтобы писать структурированно. Длинный текст без заголовков и шагов заставляет читателя заново разбирать, что к чему относится, каждый раз, когда он открывает документ. Короткий текст с чёткой структурой: заголовки, пронумерованные шаги, таблицы для сравнения вариантов, читается в разы быстрее и требует от автора не больше усилий, чем сплошной текст, просто эти усилия идут в организацию, а не в объём.

  • заголовки отражают вопросы читателя, а не этапы написания документа автором
  • команды и код всегда в отдельных блоках, не вперемешку с обычным текстом
  • таблица вместо абзаца там, где сравниваются несколько вариантов по нескольким критериям

Кто поддерживает документацию после того, как она написана

Примеры вместо абстрактных описаний

Документация, написанная абстрактно, без конкретного примера входных и выходных данных, заставляет читателя строить догадки и проверять их на практике, что занимает намного больше времени, чем чтение готового примера. Фраза «функция возвращает список активных пользователей с учётом фильтра» менее полезна, чем короткий пример вызова с реальными параметрами и примерным видом результата, потому что пример сразу снимает вопросы о формате и граничных случаях, которые абстрактное описание неизбежно оставляет открытыми.

Это особенно важно для документации API и внутренних библиотек: пример рабочего запроса с реальными полями экономит разработчику минуты на каждое обращение к документу, а за месяц использования такая экономия складывается в заметное количество освобождённого времени для всей команды, которая обращается к этому документу регулярно.

Язык документации: простые предложения важнее формальности

Техническая документация не обязана звучать официально, чтобы быть профессиональной. Длинные предложения с несколькими вложенными придаточными частями требуют от читателя больше усилий на разбор синтаксиса, чем на само содержание, особенно если читатель уже устал или читает текст в стрессовой ситуации, как в случае runbook. Короткие предложения, активный залог и конкретные существительные вместо общих слов делают текст быстрее читаемым без потери точности.

Полезная проверка перед публикацией документа: прочитать его вслух самому себе. Предложение, которое сложно произнести на одном дыхании без пауз, скорее всего сложно и прочитать с первого раза. Это простой и быстрый способ заметить перегруженные конструкции до того, как документ увидят читатели, у которых нет времени разбирать синтаксис ради одного практического ответа.

Иллюстрации тоже стоит использовать по назначению: диаграмма связей между сервисами помогает там, где текстом пришлось бы описывать структуру из многих частей сразу, но превращать в картинку то, что проще сказать одним предложением, только добавляет лишний шаг между вопросом читателя и ответом. Схема оправдана, когда она показывает связи, которые тяжело держать в голове по тексту, а не когда она просто иллюстрирует уже понятную мысль.

Полезно заранее решить, где документ будет жить и как на него будут ссылаться из кода и из задач в трекере. Runbook, который лежит в личных заметках одного инженера, бесполезен для дежурного, который видит инцидент впервые. README, который невозможно найти без точного знания, в какой папке репозитория он лежит, не выполняет свою функцию входной точки. Единое, предсказуемое место для каждого жанра документации сокращает путь от вопроса до ответа почти так же сильно, как сама хорошая структура текста внутри документа.

Документация, у которой нет явного владельца, устаревает быстрее кода, потому что код хотя бы ломается заметно при изменении поведения системы, а неверный текст продолжает выглядеть правдоподобно и никак не сигналит о своей неактуальности. Практичное решение - привязывать обновление README и runbook к самому пул-реквесту, который меняет описанное в них поведение, а не выделять отдельную задачу «обновить документацию» на потом, которая почти никогда не доходит до исполнения в условиях обычной загрузки команды.

Частые вопросы

Нужно ли писать ADR для каждого технического решения в проекте?

Нет, ADR имеет смысл для решений, которые дорого отменить и которые реально могут вызвать вопрос в будущем: выбор базы данных, подхода к аутентификации, крупной внешней зависимости. Для мелких решений, которые легко поменять без последствий, запись только добавляет лишний текст, который никто не станет перечитывать.

Как часто нужно проверять runbook на актуальность?

Разумный ориентир - проверять runbook после каждого изменения в системе, которое затрагивает описанные в нём шаги, и дополнительно прогонять его целиком хотя бы раз в несколько месяцев на тестовом стенде, даже если явных изменений не было. Команды и пути в выводе логов меняются вместе с инфраструктурой незаметно, и устаревший runbook в момент реального инцидента опаснее, чем его полное отсутствие, потому что создаёт ложную уверенность.

Стоит ли переносить документацию из Confluence или вики в сам репозиторий кода?

Для README, ADR и runbook, которые тесно связаны с конкретным кодом, хранение рядом с этим кодом в репозитории обычно удобнее: документация версионируется вместе с кодом, обновляется в том же пул-реквесте и не расходится с реальностью так быстро, как отдельная страница в вики, которую легко забыть открыть при изменении кода.

Код, который не стыдно передать

Начните с принципов, а дальше двигайтесь по рубрикам. Каждый разбор заканчивается чек-листом.

Случайный разбор