Минимальный фреймворк для ведения документации
Думаю, никому не стоит объяснять, что документация к проекту такая же важная часть этого самого проекта, как и его код. Однако почему-то о качестве кода заботятся многие, а о доке - единицы. Мы пишем тесты, оставляем комментарии к неочевидным местам в коде, рассказываем и слушаем выступления про чистые архитектуры. А об описании системы не думаем…
Вы вот знали, что есть разные фреймворки для ведения документации12? Я вот нет :)
Но на самом деле, когда узнал, легче не стало, и об этом далее.
Классическая документация #
На всех проектах, к которым я успел приложить руку за годы разработки, документации либо не было вообще, и знания жили в головах ветеранов компании, либо дока представляла собой просто плоский набор заметок, когда-то давно написанный и ни разу не обновляемый.
В целом, оба варианта одинаковые - новому инженеру придется с 0 восстанавливать весь контекст о системе, бегая по разным людям. Второй, наверное, даже более печальный, ведь он вводит в заблуждение из-за своей неактуальности.
В век эйайя это еще чревато тем, что агент может очень лихо делать ошибочные предположения о работе всей системы и, следовательно, писать неправильный или опасный код. А ведь вам за ним как-то надо следить, и повезет, если у вас уже есть необходимый контекст. А если его нету? И почитать негде. В общем, на мой взгляд, для AI-разработки хорошая документация жизненно необходима.

Хорошая документация #
А что это?
Если хороший код - это ненаписанный код, то здесь, к сожалению, ситуация мало того что противоположная, так она еще и в разы сложнее.
I. Она есть #
Во-первых, как я упоминал выше, она должна быть и быть актуальной.
К сожалению, актуальность поддерживать непросто, и тут придется договориться с командой: «при значимых изменениях в проекте мы меняем и описание этой системы». Все. Других способов поддержания актуальности нет.
На мой взгляд, это правило легче всего соблюдать, если описание системы лежит не где-то в Confluence, а рядом с кодом. В таком случае и на ревью сразу видно, были ли зафиксированы изменения в системе. И агент сразу сможет ее подгрузить при исследовании проекта.
II. Простая #
Во-вторых, текст должен быть простым и понятным. Этот пункт связан с предыдущим. Сложный технический текст читать лениво, редактировать и дополнять его - еще более лениво.
Тут-то мне и не зашел arc42. 12 сущностей - это, конечно, круто. Можно, наверное, описать любую систему. Но я вот добавил новую крону в систему, мне куда ее разместить? Именно этим вопросом задастся младший специалист и просто забьет на ведение доки, ведь «это что-то для senior’ов». Так что чем проще структура, тем лучше.
Способ повествования тоже относится к этому пункту. Если для описания какого-то процесса вам хватило 2-х предложений, не надо пытаться придумать больше: со временем этот текст обрастет подробностями, если это будет необходимо. Тем более не надо вставлять AI-слоп на 3 экрана. Если уж вы при написании поленились прочитать все и сократить, остальные инженеры тем более не будут вникать в происходящее.
III. Рассказывает что-то новое #
В эту ловушку попадал и я сам, когда пытался описывать системы. Документация должна раскрывать новые подробности о системе, которые через код получить сложно.
«Когда сумма покупки превышает 7462 рубля и в корзине есть носки, мы дарим пользователю перчатки» - такая себе дока. Сегодня сумма одна, а завтра другая. Сегодня проверяем носки, а завтра
не носки. При каждом изменении кода придется менять и это описание.
Так мало того, что ее придется менять, придется помнить, что там это описано. И этот контекст нужно будет держать в головах всем, кто пишет код и его ревьюит. Понятно, что пример с носками утрированный, но суть, надеюсь, вы уловили.
Не пытайтесь пересказывать код. Пытайтесь рассказать то, что из кода малоочевидно. Вместо описания носков и перчаток лучше зафиксировать саму механику подарков за покупки.
Что я придумал #
Когда я наконец сформулировал для себя требования к хорошей документации, стало понятно и то, как она должна выглядеть.
Требование к актуальности превращается в свойство «живет рядом с кодом», а значит, все описано обычными текстовыми файликами. Требование к простоте превращается в плоскую структуру с минимальным набором сущностей. Требование к «новизне» определяет, какие именно сущности нужны.
Представляю вашему вниманию минималистичный фреймворк имени Алана Смитти:
docs/
├── architecture.md
├── adr/
│ ├── 0001-use-redis-pub-sub.md
│ └── 0002-migrate-from-mongodb.md
└── features/
├── gifts.md
└── wish_lists.md
Почему «имени Алана Смитти»? Потому что не я один пришел примерно к такой структуре3. Плюс набор сущностей формировался по принципу «где-то видел Х, было удобно». В общем, какой-то моей собственной разработки в этом нет: этот подход - дитя небольшой насмотренности и капли рефлексии.
Но вернемся к сути. Почему именно такой набор?
Архитектура #
Во-первых, архитектуру по коду зачастую сложно восстановить. Особенно если микросервисы и нет монорепы. А даже если у вас монорепа или монолит, с ходу понять, кто какую Kafka читает - сложно.
Во-вторых, нужно где-то описать взаимодействие наших сервисов с внешними системами. Архитектура подходит как нельзя лучше.
Я рекомендую для описания архитектуры начать с какого-то очень простого способа. Например, Mermaid4 и C45. Это позволит команде потренироваться описывать систему и со временем выработать свои требования.
В этом разделе не размещайте только диаграммы. Не стесняйтесь использовать и буквы для передачи
смыслов. Например, можно кратко описать, зачем нужна какая-то крона, и приложить ссылки на
features/. Или можете указывать для внешних систем ответственные за них команды.
Architecture Decision Record #
Честно признаюсь, впервые увидел эту сущность у OpenSpec2. Но это настолько классный и нужный вид записей, что я до сих пор удивляюсь, почему все этим не пользуются.
Бывало у вас, что вы смотрите на новую систему и не понимаете, зачем тут воткнули какой-то Redis pub/sub вместо всем любимой Kafka? Кафка ведь в разы лучше подходит и уже используется в системе. Повезет, если в компании остался разработчик, который принимал это решение и сможет рассказать вам: «Раньше команда была маленькая, и мы экономили на чем могли. Редис у сервиса уже был, а Kafka для компании была сильно дорогой, не было devops, кто мог бы ее поддерживать».
ADR как раз нужны для фиксирования принятых решений и передачи этих знаний будущим поколениям.
Форматов в интернете для описания этих records великое множество, но я снова предлагаю начать с чего-то минималистичного и позже нарастить мясцо:
# 0001. Использование Redis Pub/Sub
## Статус
Принято.
## Описание
Системе нужна шина данных для передачи сообщений между компонентами. Средств и возможностей нет для
поднятия Kafka, а Redis уже есть. Поэтому решено использовать pub/sub.
## Последствия
- компоненты системы могут обмениваться сообщениями;
- Redis для кэша сервиса X превратился в коммунальный редис нескольких систем, нужно мигрировать на
Kafka [TASK-123]
Обращаю внимание на Статус. Мне кажется, стоит фиксировать не только принятые решения, но и
отклоненные, чтобы снова не возвращаться к уже решенным вопросам.
Важный нюанс при ведении ADR: если все остальные сущности вы можете описать у уже существующей
системы без доки, то эту не получится. Не надо пытаться придумать, почему тут используется Redis.
Если не помните / не знаете, ничего не пишите! Иначе документация рискует стать ошибочной и вводящей
в заблуждение. Вам может казаться, что Redis тут используется из-за модели fire & forget, а на
самом деле «так исторически сложилось».
Функции системы #
В каталоге features/ предлагаю размещать основные фичи вашей системы. Не обязательно дробить
систему на миллион маленьких функциональных особенностей, например, на «подтверждение профилей»,
«права администратора», «блокировка аккаунтов» и т. п. Лучше описать 1 раздел «система прав», где
упомяните, что есть и подтверждение, и блокировка. Если читателю нужна будет конкретика, он всегда
может занырнуть в код (мы все-таки для инженеров пишем доку).
Также стоит помнить, что 1 ручка != 1 функция. Как правило, к конкретной функциональности относятся несколько ручек/микросервисов/кронов.
Если архитектура отвечает на вопрос «как система устроена», ADR - «почему система устроена именно так», то features отвечают на не менее важный вопрос «что вообще система делает?».
На первый взгляд это противоречит третьему принципу и просто пересказывает код. Но отдельная функция системы часто размазана между несколькими ручками, консьюмерами и сервисами. Из каждого компонента по отдельности совсем не очевидно, что вместе они реализуют один пользовательский сценарий.
Плюс новеньким на проекте не надо проводить онбординг, если есть этот раздел :)
Как этим пользоваться? #

Для начала нужно осознать, что вам действительно нужна документация, а текущее состояние дел опасно для системы. Если всех устраивает плоский набор заметок или вы работаете над mvp, который через месяц выкинут и забудут, не тратьте силы на этот фреймворк.
Также стоит понимать, что это не жесткие требования к способу ведения технической документации. Это
лишь минимальный набор сущностей, который, по моему мнению, стоит описывать в первую очередь. Если
ваша система настолько сложная, что нужен глоссарий - добавляйте. Если вся архитектура не помещается
в 1 .md-файл, превращайте его в папку со слоями или отдельными сервисами.
Начните с архитектуры, зафиксируйте границы существующей системы. При ее описании в формате C45 не пытайтесь восстановить все 4 слоя. Начните с первых двух. Не понимаете, как система выглядит на втором уровне? Опишите только первый! Со временем появится конкретика и детали - дополните.
Пообщайтесь с бизнесом, потыкайте систему палочкой, определите, какие фичи есть, и зафиксируйте их. Нет конкретики про ролевую систему? Опять же, ничего страшного: со временем появятся подробности. Помните, что 3 поверхностно описанные фичи лучше, чем 0.
Когда принимаете какие-то архитектурные решения, фиксируйте их в adr и требуйте того же от коллег.
Ну и самое главное:
Пиши, бумага все стерпит
Цель этого фреймворка не в том, чтобы попытаться задокументировать весь мир, а в том, чтобы сделать минимальную полезную документацию настолько дешевой, чтобы команда действительно ее вела.