Руководства и обучающие тексты: разные жанры, разные правила
Новый инженер выходит в понедельник. В вики висит страница «Getting started», написанная полгода назад добросовестным человеком: девять экранов, шестнадцать команд, четыре раздела «если у вас macOS», один абзац про историю сервиса и ссылка на справочник конфигурации в третьем предложении. Автор проверял её на своей машине, и она работала.
К среде новичок задал в чат девять вопросов, три из них — тому самому автору. К четвергу
он завёл сервис локально: помог сосед, который знал, что переменная DB_DSN из примера
устарела в марте, а make seed теперь требует запущенного контейнера с Kafka, о чём в тексте
ни слова. Итог: три дня вместо запланированных сорока минут и вывод «у нас всё сложно»,
который новичок будет повторять полгода.
Разбор не про «мало написали». Написали как раз много. Страница пыталась одновременно научить незнакомого человека, провести его по конкретной задаче, объяснить устройство системы и перечислить все опции конфигурации. Это четыре разных документа с четырьмя разными читателями и взаимоисключающими правилами, склеенные в один файл. Каждый читатель находил в нём чужие куски и продирался сквозь них.
Обучающий текст обещает читателю конкретный исход и обязан этот исход обеспечить. Туториал обещает «после этого у вас заработает», инструкция — «после этого задача сделана», объяснение — «после этого вы понимаете, почему так». Смешивая обещания, вы не выполняете ни одного.
Общая рамка трека — читатель и решение — здесь принимает специфическую форму. Решение читателя обучающего текста мелкое и повторяющееся: «что нажать сейчас», «продолжать или бросить», «мой это случай или не мой». Оно принимается десятки раз за час чтения, и каждое неверно поддержанное решение — точка выхода.
Четыре состояния читателя, четыре жанра
Жанр определяется не темой, а состоянием читателя в момент открытия текста. Одна и та же тема — «ретраи в клиенте платежей» — порождает четыре разных документа.
| Состояние читателя | Вопрос в голове | Жанр | Что решает | Признак провала |
|---|---|---|---|---|
| Не знает даже, что спросить | «получится у меня вообще?» | туториал | продолжать или бросить | бросает на третьем шаге и не возвращается |
| Знает цель, не знает шагов | «как сделать именно это?» | инструкция (how-to) | какой шаг следующий | делает не то, откатывает, идёт в личку |
| Знает шаги, но под нагрузкой | «мой это случай, что нажать?» | раннбук | восстановить или эскалировать | минуты простоя, паника, лишние действия |
| Пользовался и удивился | «почему оно так устроено?» | объяснение | доверять системе или обходить её | самодельные обходы вместо штатного пути |
| Знает всё, забыл сигнатуру | «какой формат у поля?» | справочник | как вызвать | тикеты об «очевидном» |
Ближайший общепринятый ориентир — Diátaxis Даниэля Прокиды: две оси (учится/работает и практика/теория) дают четыре квадранта, и главный тезис системы — смешение квадрантов есть основная причина плохой документации. Раннбук в Diátaxis отдельно не выделен: формально это инструкция, но с настолько ужесточёнными правилами, что удобнее считать его пятым жанром.
Обратите внимание: «Почему у нас идемпотентность» стоит в квадранте обучения, хотя его читают опытные инженеры. Объяснение — обучающий жанр независимо от грейда читателя: он в нём учится, а не действует, и потому терпит нарратив, отступления и «а вот так мы пробовали и не вышло». В инструкции всё это — шум, который стоит читателю минут.
Один факт в четырёх жанрах
Проверка на понимание границ. Факт: клиент делает три повтора с экспоненциальной задержкой, базовая задержка 200 мс, только для идемпотентных методов.
| Жанр | Как этот факт выглядит |
|---|---|
| Туториал | не появляется вообще: он не нужен для первого успеха |
| Инструкция | «Если получили 503, повторите запрос не раньше чем через 200 мс с тем же Idempotency-Key» |
| Раннбук | «Всплеск 503 от провайдера — это ожидаемо, клиент сам повторит 3 раза. Эскалируйте, только если retry_exhausted_total растёт» |
| Справочник | retry.max_attempts: 3 (default), retry.base_delay: 200ms, retry.methods: [GET, PUT, DELETE] |
| Объяснение | «Мы повторяем только идемпотентные методы, потому что провайдер не гарантирует, что 502 означает невыполнение операции: платёж мог пройти» |
Один и тот же факт — пять разных форм и три разных длины. Автор, который пишет «универсальную документацию по ретраям», выбирает одну форму и обманывает четырёх читателей из пяти.
Почему смешение жанров разрушает оба текста
Это не эстетическое возражение. У смешения есть конкретный механизм.
Читателю туториала нельзя давать выбор. Он ещё не построил модель системы, а значит, не имеет оснований выбирать. Фраза «в зависимости от вашего окружения вам может понадобиться настроить X» останавливает его намертво: он не знает, его ли это случай, и любое действие кажется рискованным. Каждая развилка в туториале — это вероятность выхода.
Читателю инструкции нельзя давать нарратив. У него открыт тикет и цель; он сканирует текст глазами в поисках команды. Абзац про историю решения между шагами 4 и 5 он либо пропустит (и потеряет важное, если вы туда что-то спрятали), либо прочитает и потеряет минуту и место в списке.
Формальная причина — ограниченность рабочей памяти: устойчивая оценка ёмкости — около четырёх элементов (Cowan, 2001), и теория когнитивной нагрузки Свеллера различает нагрузку, идущую на суть задачи, и постороннюю нагрузку от формы подачи (Sweller et al., 2019). Чужой жанр в тексте — это чистая посторонняя нагрузка: он расходует те же четыре ячейки, которые нужны читателю на саму задачу.
Есть и вторая причина, чисто эксплуатационная: смешанный текст невозможно поддерживать. Он стареет по трём независимым причинам (изменился интерфейс, изменилась процедура, изменилось решение) и требует трёх разных владельцев. Об этом — раздел про устаревание ниже и глава «Структура».
Пара 0: расщепление смешанного абзаца
Как написано:
Configuring retries
The client supports retries. Retries are important because the payment provider is
not always available and we have seen up to 2% of requests fail during peak hours.
Set retry.max_attempts to a reasonable value. Note that retries only apply to
idempotent methods, since a 502 does not necessarily mean the payment did not go
through. If you are not sure, leave the defaults.
Пять предложений — четыре жанра. «Retries are important because…» — объяснение. «Set retry.max_attempts» — инструкция. «Note that retries only apply…» — справочный факт, поданный как ремарка. «If you are not sure, leave the defaults» — попытка спасти читателя, которого автор сам и запутал. Читатель под нагрузкой не найдёт здесь ничего, а новичок не поймёт, надо ли ему что-то делать.
Переписано — четыре куска в четырёх местах:
[справочник, генерируется из схемы конфигурации]
retry.max_attempts int default: 3 applies to: GET, PUT, DELETE
retry.base_delay dur default: 200ms
[инструкция: «Как изменить политику повторов»]
1. Set retry.max_attempts in config/payments.yaml. Values above 5 are rejected by
the config validator (see step 3).
[объяснение: «Почему мы не повторяем POST»]
A 502 from the provider does not tell us whether the payment was executed. Repeating a
non-idempotent POST can charge the customer twice, so the client refuses to retry it.
[туториал: ничего]
Последняя строка — самая важная. Убрать факт из туториала не значит его потерять: значит поставить туда, где его ищут.
Туториал: обещание, которое обязано сбыться
Туториал — единственный жанр, где успех читателя важнее полноты текста. Читатель не проверяет ваш продукт: он проверяет, стоит ли вообще в это вкладываться. Каждый его шаг — голосование «продолжаю».
Правила, каждое из которых — следствие этого:
- Обещайте измеримый исход в первой строке. Не «познакомитесь с API», а «через 10 минут
у вас будет тестовый заказ и его
idв терминале». - Ровно один путь. Ни одного «или», ни одного «в зависимости от». Если вариантов ОС три — это три туториала или один плюс контейнер, снимающий различия.
- Ноль решений на читателе. Все значения выбраны за него. Имена, порты, регионы — константы, а не «выберите подходящее».
- Каждый шаг заканчивается наблюдаемым результатом. Читатель должен видеть, что получилось, — вывод команды, строка в логе, запись в БД.
- Результат минимальный, но настоящий. Не «hello world» в вакууме, а самый маленький осмысленный артефакт домена: заказ, платёж, задача.
- Ни одного объяснения внутри. «Почему» выносится в конец блоком «Что здесь произошло» или в отдельный текст со ссылкой — и только после того, как всё заработало.
- Безопасная песочница. Читатель обязан знать, что не сломает прод, иначе он будет думать не о материале, а о риске.
- Предусловия — проверяемыми командами, а не прозой. Не «требуется Python», а
python3 --versionс ожидаемым выводом.
Это, по сути, минимализм Джона Кэрролла: обучение действием, короткий путь до первого результата и явная поддержка выхода из ошибки вместо длинного вводного курса («The Nurnberg Funnel», MIT Press, 1990 — описание). Тридцать пять лет спустя выводы не изменились: люди не читают введения, они пробуют.
Пара 1: открытие туториала
Как написано:
Getting Started
This guide will help you get started with the Orders API. Before you begin, make sure
you have the necessary prerequisites installed. Depending on your environment, you may
need to configure additional settings. For more information on configuration options,
see the Configuration Reference. Once everything is set up, you should be able to make
your first request.
Что здесь сломано, по предложениям:
- «will help you get started» — не обещает ничего проверяемого; читатель не знает, что у него будет в конце и сколько это займёт;
- «the necessary prerequisites» — предусловия названы, но не перечислены: читатель обязан догадаться сам;
- «Depending on your environment, you may need» — решение переложено на человека, у которого нет базы для решения; классический выход из туториала;
- ссылка на справочник в четвёртом предложении — приглашение уйти из текста до первого успеха;
- «you should be able to» — модальность вместо результата. «Должно получиться» — не проверка. О модальностях и словах-дырках см. «Ясность».
Переписано:
Create your first order (10 minutes)
By the end you will have a paid test order in the sandbox and its id printed in your
terminal. Everything runs against the sandbox: no real money, no production data.
You need:
- Python 3.11 or newer check: python3 --version
- a sandbox key get one at /sandbox (no card required)
Step 1. Install the client and check that it talks to the sandbox.
pip install orders-client==2.4
orders ping --env sandbox
Expected output:
sandbox: ok (region eu-1, api 2.4)
If you see `auth: missing key`, your key is not exported yet — go back to "You need".
Изменилось не «оформление». Изменился контракт: заголовок обещает исход и цену в минутах,
предусловия проверяются одной командой, версия клиента зафиксирована (==2.4, иначе туториал
сломает первый же мажорный релиз), у шага есть ожидаемый вывод и одна названная ошибка
с выходом из неё.
Три ловушки туториала
«Congratulations! You have created your first order.» Поздравление вместо проверки — самый частый способ соврать. Читатель, у которого не получилось, читает поздравление как насмешку. Правильный конец шага — то, что он видит на экране, а не то, что он должен чувствовать.
Ветвление по окружению. Три ветки «Linux / macOS / Windows» превращают текст в матрицу,
которую никто не тестирует целиком: через полгода живой останется ветка автора. Дешевле дать
docker run и одну ветку, а нативную установку вынести в отдельную инструкцию для тех,
кто уже прошёл туториал.
Метрика вместо мнения. У туториала есть измеримый показатель — время до первого успеха (в devrel его называют TTFHW, time to hello world) и доля дошедших до конца. Если инструмент показывает, что 40% выходят на шаге 4, спор о стиле заканчивается: смотрите шаг 4. Практику измерения хорошо описывает «Docs for Developers» (Bhatti, Corleissen, Lambourne, Nunez, Waterhouse; Apress, 2021).
Инструкция: читатель знает цель, но не знает шага
У инструкции читатель компетентен и занят. Ему не нужно объяснять, что такое деплой, — ему нужно повернуть трафик на новую версию, не уронив прод. Отсюда другие правила.
Заголовок — цель читателя, а не имя вашей подсистемы. Люди ищут по своей задаче.
| Как назвали | Как ищут на самом деле |
|---|---|
| «Configuring the Widget Manager» | «Как отключить письма о заказе для одного клиента» |
| «Retention Policy Administration» | «Как удалить данные клиента по запросу на удаление» |
| «Traffic Management» | «Как откатить релиз, если растут 5xx» |
Левый столбец написан из головы автора (он знает названия модулей), правый — из головы читателя (он знает только свою беду). Заголовок из левого столбца не находится поиском, и документ мёртв независимо от качества содержимого.
Анатомия шага. Шаг выполним, если после него читатель может сказать «получилось» или «не получилось» без звонка автору. Для этого в нём пять слотов.
Пара 2: шаг деплоя
Как написано:
5. Next, you'll want to update the deployment configuration. Make sure the replica
count is set appropriately for the expected load and that the readiness probe is
properly configured. Then apply the changes and verify that everything looks good.
Разбор: «you’ll want to» — ни утверждение, ни команда; «appropriately», «properly», «looks good» — три оценочных слова, каждое требует от читателя знания, ради которого он и открыл инструкцию; четыре действия в одном шаге (изменить реплики, проверить пробу, применить, проверить); ни ожидаемого вывода, ни признака отказа. Если на этом шаге что-то пойдёт не так, инструкция закончится, а читатель пойдёт в личку.
Переписано:
5. Поднимите число реплик до 6 (по 2 на зону):
kubectl -n payments set env deploy/api MIN_REPLICAS=6
kubectl -n payments rollout status deploy/api --timeout=120s
Ожидаемый вывод: `deployment "api" successfully rolled out` — обычно за 40–60 секунд.
Если команда завершилась по таймауту, новые поды не проходят readiness-пробу.
Откатитесь и перейдите к шагу 8:
kubectl -n payments rollout undo deploy/api
Один шаг — одно действие. Команда копируется целиком, без подстановки «вашего неймспейса» в трёх местах. Ожидаемый вывод дан буквально, вместе с реалистичным временем. Отказ назван конкретным признаком (таймаут), а не «если что-то пошло не так», и у него есть выход.
Ветвление в инструкции разрешено — но условиями, а не догадками. «Если у вас включён
мультитенант» — плохое условие: читатель не знает, включён ли. Хорошее условие проверяется
командой: «Выполните orders config get tenancy. Если ответ multi, идите к шагу 6a».
Императив, второе лицо, настоящее время. «Выполните», а не «нужно выполнить», «система выполнит» или «выполняется команда». Пассив и номинализации в инструкции особенно вредны: они прячут исполнителя ровно там, где исполнитель — это читатель. Подробно — в «Ясности». Стилевые каноны на этот счёт совпадают: Google developer documentation style guide и Microsoft Writing Style Guide.
Раннбук: инструкция под нагрузкой
Раннбук — инструкция, читатель которой разбужен в 03:40, видит алерт и имеет минуты. Все правила инструкции сохраняются и ужесточаются, плюс появляются собственные.
подтверждение симптома"] B --> C{"Симптом совпал?"} C -->|"нет"| D["Это другой раннбук:
ссылка на индекс алертов"] C -->|"да"| E["Шаги стабилизации:
вернуть сервис пользователям"] E --> F{"Помогло за 10 минут?"} F -->|"да"| G["Зафиксировать время и действия
для постмортема"] F -->|"нет"| H["Явный порог эскалации:
кого звать и как"] H --> I["Второй уровень:
деградация, фичефлаг, откат"] G --> J["Постмортем"] I --> J D --> K["Индекс: алерт → раннбук"]
Что отличает раннбук от обычной инструкции:
- один алерт — один документ, и ссылка на него лежит в самом алерте, а не в вики;
- первый блок — подтверждение: «вы попали по адресу, если видите X». Дежурный половину времени тратит на проверку, тот ли это случай;
- порог эскалации записан числом: «не восстановилось за 10 минут — звоните дежурному платформы», а не «при необходимости эскалируйте»;
- никаких «оцените», «проанализируйте», «при необходимости» — под нагрузкой человек не анализирует, он выполняет;
- сначала стабилизация, потом диагностика. Раннбук возвращает сервис, а не находит причину: причину ищет постмортем.
Процессная сторона дежурства и требования к алертам — в треке SRE: «Дежурство», «Реакция на инцидент» и «Алертинг».
Пара 3: объяснение внутри раннбука
Как написано:
3. Check the consumer lag.
Historically this queue was introduced in 2022 when we split the monolith. Because
of the way the old billing job worked, the consumer group name still contains the
word "legacy", which sometimes confuses people. Anyway, if the lag is high, you may
want to scale the consumers.
Три предложения истории между двумя действиями. В три часа ночи это чистая потеря: дежурный читает про раскол монолита в 2022 году, пока растёт очередь. Хуже того, полезный факт («не пугайтесь слова legacy в имени группы») утоплен в середине абзаца, а действие подано как «you may want to».
Переписано:
3. Проверьте лаг консьюмеров:
kafka-lag-exporter --group billing-legacy-v2
Имя группы содержит «legacy» — это нормально, историческое имя.
Лаг > 100 000 сообщений и растёт: перейдите к шагу 4 (масштабирование).
Лаг < 100 000 или падает: очередь разгребается сама, перейдите к шагу 7.
Почему группа называется так и что будет при переименовании — ADR-041.
История никуда не делась: она уехала в ADR, где её и ищут. В раннбуке остался один факт, который нужен именно ночью, — и он вынесен отдельной строкой, а не спрятан в третьем предложении.
Объяснение: жанр, который забывают написать
Объяснение отвечает на «почему так» для человека, который уже пользуется системой и столкнулся с неочевидным поведением. Его отсутствие стоит дорого и незаметно: инженеры изобретают обходные пути, потому что не понимают ограничений, и обход попадает в прод.
Признаки, что нужно объяснение, а не что-то другое: один и тот же вопрос всплывает в чате раз в месяц; в ревью регулярно приходится писать длинный комментарий про одно и то же; люди пытаются использовать компонент способом, который вы считаете очевидно неверным.
Правила у объяснения почти противоположны инструкции: можно нарратив, можно история, можно «мы пробовали X и отказались, вот почему», нужны альтернативы и границы применимости. Чего нельзя — пошаговых указаний: как только в объяснении появляется нумерованный список команд, оно превращается в плохую инструкцию и начинает устаревать по расписанию инструкции.
Пара 4: объяснение, которое ничего не объясняет
Как написано:
Idempotency
The Orders API supports idempotency. To use idempotency, pass an Idempotency-Key
header with a unique value. The key is stored for 24 hours. If you send the same key
twice, the second request returns the original response.
Это справочник, переодетый в объяснение: четыре факта, ноль модели. Читатель узнал, что делать, но не узнал, зачем ему это и что сломается без этого, — а значит, при первой спешке не поставит заголовок.
Переписано:
Why you need an idempotency key
Your request can succeed on our side and still fail on the network: the order is
created, the response never reaches you. Your client retries, and without a key we
have no way to tell the retry apart from a second, deliberate order — so the customer
is charged twice.
The key is your statement "this is the same intent as before". We store it for 24
hours together with the original response; a repeat within that window replays the
stored response instead of creating an order. After 24 hours the key is forgotten,
so a retry a day later will create a second order — this is why the key should be
derived from your business operation, not generated per attempt.
Reference: Idempotency-Key header. How to add it to the client: "Retries and
idempotency" guide.
Разница: появилась причинно-следственная модель (сеть ломается посередине → повтор неотличим от нового заказа → двойное списание), названы границы (24 часа) и следствие границы для читателя (ключ выводится из бизнес-операции, а не из попытки). В конце — по одной ссылке на справочник и инструкцию: объяснение не пытается их заменить.
Скриншоты, GIF и прочий дорогой носитель
Скриншот — самая дорогая единица документации из всех, что вы можете вставить.
| Свойство | Текст | Скриншот |
|---|---|---|
| Стоимость обновления | правка строки | пересъёмка, кроп, заливка, иногда локализация |
| Как ломается | заметно: команда не работает | незаметно: картинка выглядит правдоподобно |
| Поиск по содержимому | да | нет |
| Копирование команды | да | нет |
| Доступность для скринридера | да | только через alt |
| Перевод | автоматизируем | пересъёмка на каждом языке |
Отсюда рабочее правило: скриншот только там, где текст объективно не справляется — пространственное расположение элементов, диаграмма, результат, который надо узнать глазами. «Нажмите кнопку „Сохранить“» скриншота не требует; если требует, проблема в интерфейсе, а не в документации. Текст в интерфейсе — отдельный жанр со своими правилами, см. микрокопию в треке UX. Требования к alt и к тексту вместо картинок — «Доступность».
Если скриншоты всё же нужны (обычно — в пользовательской документации), единственный устойчивый вариант — генерировать их в CI. Playwright и аналоги умеют снимать экраны сценария на каждом релизе (документация); тогда картинка становится артефактом сборки, а не файлом, который кто-то однажды положил в вики. GIF и видео не поддаются даже этому: они устаревают целиком и переснимаются вручную, поэтому годятся для маркетинга и почти никогда — для документации, которая должна жить.
Почему обучающие тексты гниют быстрее всех
Общую механику устаревания разбирает
глава про поддержку; здесь важна специфика жанра.
Обучающий текст исполняется в окружении, а значит зависит не от одного факта, а от
цепочки: версия клиента, имя команды, формат вывода, набор флагов, адрес песочницы, права
доступа, названия кнопок, содержимое .env, поведение зависимости.
Арифметика беспощадна. Пусть в туториале двенадцать таких точек привязки и каждая остаётся верной с вероятностью 0.95 за полгода — это оптимистично. Вероятность, что туториал целиком пройдёт без единого сбоя, равна 0.95 в двенадцатой степени, то есть около 0.54. Через год — около 0.29. Заметьте: это не «документацию не обновляют», это математика связки. Никакая дисциплина не поднимет 0.54 до единицы, потому что человек не знает, что сломалось.
Ключевое состояние здесь — «Помечен». Промежуточное честное состояние между «работает» и «удалён» спасает больше времени, чем любые призывы: читатель, увидевший «последний успешный прогон 2026-02-14 на версии 2.1», сам решит, доверять ли, и не потратит час на отладку чужого устаревшего примера.
Пять структурных лекарств
Призыв «обновляйте документацию» не работает — он адресован человеку, который не знает, что его текст сломался. Работает изменение структуры.
1. Близость к коду. Руководство по сервису лежит в репозитории сервиса, а не в вики.
Тогда изменение кода и изменение текста попадают в один PR, а CODEOWNERS заставляет
ревьюера на них взглянуть:
/docs/guides/ @payments-team
/docs/runbooks/ @payments-oncall
/docs/tutorials/ @devrel
2. Генерация справочных кусков. Всё, что машина знает лучше человека — список флагов, поля конфигурации, коды ошибок, — генерируется, а не пересказывается. Пересказ обязан разойтись с реальностью, вопрос только когда. Подробно — в документации API.
3. Исполняемость: примеры проверяются машиной. Это главное лекарство именно для обучающих текстов. Инструменты для этого есть почти в каждой экосистеме:
| Экосистема | Механизм | Что проверяет |
|---|---|---|
| Python | doctest, pytest --doctest-glob='*.md' |
примеры в docstring и в markdown |
| Rust | doctests | все примеры из документации компилируются и запускаются |
| Go | Example-функции | пример компилируется, вывод сверяется с // Output: |
| Sphinx | make doctest |
примеры в документации проекта |
| mdBook | mdbook test |
блоки кода в книге |
| Shell-сценарии | bats-core, извлечение блоков и прогон в контейнере | что команды из туториала реально отрабатывают |
Пример на Python — документация, которая падает в CI, когда врёт:
def next_delay(attempt: int, base_ms: int = 200) -> int:
"""Задержка перед повтором: экспоненциальный рост от базовой.
Первый повтор идёт через базовую задержку, каждый следующий — вдвое дольше:
>>> next_delay(1)
200
>>> next_delay(3)
800
>>> [next_delay(i) for i in range(1, 4)]
[200, 400, 800]
"""
if attempt < 1:
raise ValueError("attempt начинается с 1")
return base_ms * 2 ** (attempt - 1)
Если завтра кто-то сменит базовую задержку на 100 мс, покраснеет не только тест бизнес-логики, но и документация. Именно этого мы и добиваемся: текст должен уметь ломаться заметно.
Для туториала целиком тот же приём — прогон в чистом контейнере на расписании и на каждом релизе клиента:
# .github/workflows/docs.yml
name: docs
on:
push: { branches: [main] }
schedule:
- cron: "0 6 * * 1" # каждый понедельник: ловим поломки от внешних зависимостей
jobs:
tutorial:
runs-on: ubuntu-latest
container: python:3.11-slim # чистая машина, как у нового человека
steps:
- uses: actions/checkout@v4
- name: Прогнать команды туториала
run: bash ci/run-tutorial.sh docs/tutorials/first-order.md
- name: Проверить ссылки
run: lychee --no-progress docs/
- name: Проверить стиль и запрещённые слова
run: vale docs/
ci/run-tutorial.sh извлекает блоки, помеченные как исполняемые, и выполняет их подряд,
сверяя вывод с блоками «Expected output». Расписание важнее пуша: туториал чаще ломается
не от вашего коммита, а от смены версии внешней зависимости или от изменений в песочнице.
Ссылки проверяет lychee, стиль —
Vale; общий подход называется docs as code и описан в
Write the Docs. Как встроить это
в пайплайн — «Основы CI».
4. Владелец и срок жизни в самом файле. Метаданные, которые видит и читатель, и робот:
---
title: "Создание первого заказа"
genre: tutorial # жанр объявлен: смешивать нельзя
owner: "@devrel" # человек, а не «команда документации»
tested_against: "orders-client 2.4, api 2026-05"
last_verified: 2026-05-14
review_by: 2026-08-14 # для туториала — квартал, для объяснения — год
---
Шаблон сайта показывает баннер, когда last_verified старше review_by. Робот раз в неделю
собирает просроченное и создаёт задачу владельцу. Раздел без владельца — первый кандидат
на устаревание, а затем на удаление.
5. Триггер обновления привязан к событию, а не к календарю. Календарное ревью пропускают;
событийное — нет, потому что оно стоит на пути. Практичные триггеры: изменение публичного
флага CLI требует ревью каталога docs/; изменение схемы конфигурации проваливает сборку,
если сгенерированный справочник разошёлся с коммитом; закрытие инцидента добавляет задачу
на проверку раннбука; выход нового человека в команду назначает ему первый PR — правку
руководства, по которому он заводился (широко распространённая практика онбординга, в том
числе в Google: новичок — единственный человек, у которого ещё нет проклятия знания).
И шестое, нелюбимое: удаление. Устаревшее руководство хуже отсутствующего: оно тратит время читателя и добавляет ложной уверенности. Если у текста нет владельца и он не проходил проверку год — удаляйте, а не «оставим, вдруг пригодится». В git всё сохранится; в поиске — перестанет мешать.
Честно: часть руководств пишут ради процесса
Не все обучающие тексты существуют ради читателя. Часть пишется потому, что этого требует ритуал: definition of done содержит пункт «документация обновлена», аудит требует «наличия инструкций для персонала», в чек-листе увольнения есть строка «передать знания», а в квартальных целях — «повысить покрытие документацией». Это реальность, и притворяться, что её нет, — плохая стратегия.
Отличить ритуальный документ от рабочего можно за десять минут, четырьмя проверками:
- Кто открывал текст за 90 дней? У любой вики есть аналитика. Ноль или только автор — ответ получен.
- Что случится, если удалить? Если ничего, кроме претензии на аудите, — документ существует для аудита, и писать его нужно ровно под аудит.
- Кто владелец и что он сделает, если текст сломается? «Команда» — не владелец. Отсутствие имени означает, что документ никому не нужен.
- Кто-нибудь проходил по нему целиком? Инструкция, которую ни разу не выполняли, — это гипотеза, а не инструкция.
Что с этим делать практически:
- Не воевать, а разделять. Ритуальную часть (соответствие шаблону, формальные разделы, подписи) держите отдельно и минимальной. Рабочую часть пишите под читателя. Один файл, два раздела, честная граница: «Разделы 1–3 — требование регламента X; рабочая инструкция начинается с раздела 4».
- Свести стоимость к минимуму. Ритуальный документ, генерируемый из шаблона за пятнадцать минут, — приемлемая цена соответствия. Ритуальный документ, на который ушёл рабочий день, — потеря, и об этом стоит говорить вслух с цифрами.
- Переводить требование в проверяемое. «Документация обновлена» в definition of done — пустой пункт. «Прогон туториала зелёный» или «раннбук изменённого алерта проверен» — проверяемый. Это разговор с владельцем процесса, а не с текстом; как его вести — «Работа вверх» и «Трудные разговоры».
- Иногда документ не нужен вовсе. Знание, которое передаётся один раз трём людям, дешевле передать встречей с записью, чем текстом, который потом никто не поддерживает. Форматы таких встреч — «Фасилитация».
Отдельный честный случай — «документация вместо онбординга». Когда команда пишет сорок страниц вводного текста, чаще всего это симптом того, что онбординг не выстроен как процесс: нет наставника, нет первой задачи, нет плана на две недели. Текст эту дыру не закрывает, см. «Онбординг» и «Первые 90 дней».
Как проверить руководство до публикации
Дешёвая процедура, снимающая большую часть проблем.
- Чистая машина. Контейнер или новый пользователь. Ваша машина врёт: в ней уже стоит всё, что нужно, и лежат ключи, о которых вы забыли.
- Свежий человек и протокол «думай вслух». Посадите того, кто похож на целевого читателя, попросите проговаривать вслух и не помогайте. Каждая ваша подсказка — это дефект текста, который вы только что скрыли. Записывайте места запинок.
- Пять человек достаточно. Классическая оценка Нильсена: пять участников находят около 85% проблем юзабилити (NN/g). Для текста работает так же. Протокол и обработка результатов — «Юзабилити-тестирование».
- Считайте, а не спорьте. Время до первого успеха, доля дошедших, шаг с максимальным выходом, количество вопросов в чате по этой теме за месяц.
- Ревью с явной ролью. Просите ревьюера читать в одном жанре: «проверь как дежурный» или «проверь как человек, который видит систему впервые». Как устроено ревью текста вообще — следующая по треку глава.
Типичные ошибки
| Ошибка | Как выглядит | Чем чинится |
|---|---|---|
| Смешение жанров | «Getting started» на девять экранов | расщепить по состояниям читателя |
| Развилка в туториале | «в зависимости от вашего окружения» | один путь, контейнер, остальное — в инструкции |
| Оценочные слова в шаге | «appropriately», «при необходимости» | число, команда, проверяемое условие |
| Нет ожидаемого вывода | шаг заканчивается командой | буквальный вывод и признак отказа |
| Заголовок из головы автора | «Traffic Management» | цель читателя: «Как откатить релиз» |
| Незакреплённые версии | pip install orders-client |
==2.4 и поле tested_against |
| Скриншот вместо текста | «нажмите кнопку (см. рис. 4)» | текст; скриншот — только для пространственного |
| Объяснение в раннбуке | история сервиса между шагами | вынести в ADR, оставить ссылку |
| Инструкция в объяснении | нумерованные команды в «почему» | ссылка на инструкцию |
| Нет владельца и даты | текст без шапки | owner, last_verified, review_by |
| Ни разу не пройдено | «должно работать» | прогон в CI и на живом человеке |
| Бессмертный труп | «оставим, вдруг пригодится» | удалить; история есть в git |
Мини-итог
- Жанр определяется состоянием читателя, а не темой: учится, действует, действует под нагрузкой, хочет понять. Пять состояний — пять текстов.
- Туториал обещает измеримый исход, ведёт одним путём, не даёт выбора и заканчивает каждый шаг наблюдаемым результатом. Его метрика — время до первого успеха и доля дошедших.
- Инструкция называется целью читателя, состоит из шагов с пятью слотами: действие, команда, ожидаемый результат, признак отказа, ветка и откат.
- Раннбук — инструкция под нагрузкой: подтверждение симптома, стабилизация раньше диагностики, порог эскалации числом, ноль слов «оцените».
- Объяснение даёт модель и границы; как только в нём появляется список команд, оно испортилось.
- Обучающие тексты гниют быстрее всех, потому что зависят от десятка внешних точек одновременно. Лечится структурно: близость к коду, генерация справочного, исполняемость примеров в CI, владелец и срок в шапке, событийные триггеры, удаление без сожалений.
- Часть руководств пишется ради процесса. Это распознаётся четырьмя вопросами и лечится разделением ритуальной и рабочей частей, а не героизмом.
Источники
- Daniele Procida. Diátaxis — система четырёх жанров документации: https://diataxis.fr/ (ранняя версия — Divio documentation system).
- John M. Carroll. The Nurnberg Funnel: Designing Minimalist Instruction for Practical Computer Skill. MIT Press, 1990 — https://mitpress.mit.edu/9780262031714/the-nurnberg-funnel/
- Jared Bhatti et al. Docs for Developers: An Engineer’s Field Guide to Technical Writing. Apress, 2021 — https://docsfordevelopers.com/
- Google developer documentation style guide — https://developers.google.com/style; бесплатные курсы Technical Writing for Developers — https://developers.google.com/tech-writing
- Microsoft Writing Style Guide — https://learn.microsoft.com/en-us/style-guide/welcome/
- Write the Docs Guide, раздел про docs as code — https://www.writethedocs.org/guide/docs-as-code/
- Nelson Cowan. The magical number 4 in short-term memory. Behavioral and Brain Sciences, 2001 — https://doi.org/10.1017/S0140525X01003922
- John Sweller et al. Cognitive Architecture and Instructional Design: 20 Years Later. Educational Psychology Review, 2019 — https://link.springer.com/article/10.1007/s10648-019-09465-5
- Jakob Nielsen. Why You Only Need to Test with 5 Users — https://www.nngroup.com/articles/why-you-only-need-to-test-with-5-users/
- Инструменты: Vale (стиль), lychee (ссылки), Playwright screenshots (генерация снимков экрана), Python doctest, Rust doctests, Go examples.
Что дальше
Мы разобрали жанры, в которых читатель что-то делает руками. Почти в каждом из них рано или поздно возникает соблазн нарисовать картинку — и здесь начинается отдельное ремесло со своими правилами: какие схемы окупаются, какие врут, что рисовать, а что описывать текстом, и как сделать так, чтобы диаграмма не устарела на следующий день после коммита.