Files
progcode/editorial/agent-rewrites/120.json
T
huncode 2d914b543f
Build and deploy / deploy (push) Failing after 15s
Publish rewritten technical article archive
2026-08-02 22:19:34 +03:00

2 lines
16 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"index":120,"slug":"editorial-2024-09-practice-adr-decisions","title":"ADR без бюрократии: как сохранить причину технического решения","excerpt":"Практический разбор ADR: от наблюдаемого симптома и цены ошибки до проверяемого решения, отрицательного пути и даты пересмотра.","contentHtml":"<p>После релиза в коде остаётся необычный обходной путь: запрос проходит через отдельный слой, хотя прямой вызов короче. Через полгода обсуждение исчезает из чата, авторы переключаются на другие задачи, а reviewer видит только итоговый diff. Симптом прост: команда снова спорит, зачем существует условие, очередь или дополнительная граница.</p><p>Цена ошибки выше стоимости потерянного контекста. Можно удалить защиту, которая всё ещё нужна для старого ограничения. Можно оставить дорогую схему после того, как ограничение исчезло. Оба решения выглядят разумно, если известен только код. Нужен артефакт, который связывает наблюдаемую проблему с выбором и его последствиями.</p><h2>Тезис: ADR хранит причину, а не оправдание</h2><p>Architecture Decision Record фиксирует один значимый выбор. Он отвечает на пять вопросов: что произошло, какие ограничения действовали, какие варианты сравнили, что выбрали и какую цену приняли. Отдельно записывают владельца, статус и сигнал для пересмотра. ADR не объявляет решение вечным и не доказывает, что реализация корректна.</p><p>Это важная граница. Ticket хранит работу и сроки. Code review хранит обсуждение изменения. Test проверяет поведение. Runbook описывает операционное действие. ADR хранит rationale — причину, по которой команда выбрала один путь среди допустимых. Ссылка на ticket не заменяет rationale, а commit не заменяет сравнение альтернатив.</p><h2>Механизм: от симптома к записи</h2><p>Сначала отделите факт от интерпретации. Факт можно увидеть в логе, контракте, trace, конфигурации или наблюдаемом поведении. Интерпретация объясняет факт, но требует проверки. В ADR полезно явно назвать стоимость ошибки: потеря обратимости, рост задержки, новый владелец данных, риск несовместимости или усложнение отката.</p><p>Затем ограничьте вопрос. «Как устроить export» слишком широко. «Где caller получает status, если synchronous boundary не выполняется» уже задаёт предмет. Один ADR должен описывать один выбор. Если обсуждение меняет два независимых контракта, разделите записи и свяжите их ссылками.</p><p>Варианты сравнивают по одним и тем же критериям. В простом случае достаточно reversibility, соответствия constraint, стоимости эксплуатации и доступного evidence. Не превращайте невыбранные варианты в карикатуры. Если вариант «ничего не менять» не рассматривался, его стоит назвать отдельно: иногда это самый дешёвый и самый обратимый путь.</p><pre><code>Title: bounded cache at the BFF boundary\nStatus: Proposed\nOwner: application owner\nReview by: 2025-03-31\n\nContext: source contract permits bounded freshness.\nOptions: direct read; local cache; shared cache.\nDecision: choose local cache with named expiry.\nConsequences: add freshness check and direct-read rollback.\nEvidence question: does the source contract still permit this cache?</code></pre><p>Пример учебный. Он не создаёт cache, не обращается к repository и не сообщает latency, hit ratio или экономию. Его задача — показать форму записи. В настоящем ADR каждое утверждение о контракте должно ссылаться на доступный артефакт, а владелец должен иметь право проверить его.</p><h2>Симптом → причина → проверка → действие</h2><div class=\"table-scroll\"><table><caption>Как переводить наблюдаемую проблему в следующий шаг</caption><thead><tr><th scope=\"col\">Симптом</th><th scope=\"col\">Причина</th><th scope=\"col\">Проверка</th><th scope=\"col\">Действие</th></tr></thead><tbody><tr><td>В review спорят о старом обходном пути</td><td>Контекст остался в переписке</td><td>Найдите исходное ограничение и отделите факт от гипотезы</td><td>Создайте proposed ADR и укажите ссылку на code</td></tr><tr><td>В записи перечислен только выбранный путь</td><td>Альтернативы не стали частью решения</td><td>Проверьте, можно ли сравнить варианты по одним критериям</td><td>Добавьте реально рассмотренные варианты и причины отказа</td></tr><tr><td>Текст обещает «простую поддержку»</td><td>Последствия описаны абстрактно</td><td>Спросите, кто что должен сделать и какой риск остаётся</td><td>Запишите владельца, cost, rollback и signal пересмотра</td></tr><tr><td>ADR принят, но code изменился иначе</td><td>Запись смешана с реализацией</td><td>Сопоставьте decision с контрактом и diff отдельной проверкой</td><td>Исправьте code или обновите ADR новым решением; не переписывайте историю</td></tr><tr><td>Ограничение больше не действует</td><td>У записи нет review boundary</td><td>Проверьте дату, source contract и сигнал изменения</td><td>Создайте successor ADR со статусом superseded для старого</td></tr></tbody></table></div><h2>Как читать короткий ADR</h2><p>Хорошая запись начинается с контекста, но не превращается в историю всей команды. Достаточно назвать границу системы, затронутый контракт, decision drivers и цену неверного выбора. Слова «быстрее», «надёжнее» и «проще» требуют уточнения. Быстрее для какого сценария? Надёжнее при каком отказе? Проще для какого владельца?</p><p>Последствия должны включать отрицательную сторону. Если выбран local cache, положительный эффект может быть ограничен целевым read path. Цена — необходимость хранить expiry, проверять freshness и иметь путь к direct read. Если данные могут быть чувствительными, добавляется отдельная проверка класса данных. Не прячьте эту цену под словом «trade-off»: читателю нужно понимать, что именно он будет поддерживать.</p><p>Запишите две границы: что решение делает и чего оно не делает. В учебном примере мы создаём bounded local state с именованным expiry. Мы не создаём shared invalidation system и не объявляем число запросов измеренным результатом. Такая отрицательная часть защищает от незаметного расширения scope.</p><figure><img src=\"/assets/editorial/2024/adr-decisions-2024-decision-flow.svg\" alt=\"Схема потока ADR: симптом и цена ведут к контексту, вариантам и проверке; затем следуют решение, человеческое принятие, связь с кодом и пересмотр.\" loading=\"lazy\" /><figcaption>Схема показывает порядок работы с решением. Она не описывает конкретную approval-систему, репозиторий, CI или состояние production.</figcaption></figure><h2>Почему запись не заменяет проверку</h2><p>ADR может быть логичным и всё равно ошибочным. Он фиксирует состояние знаний на момент выбора. Контракт источника может измениться. Ограничение по данным может оказаться неверным. Операционная цена может вырасти. Поэтому Accepted означает «решение принято», а не «реализация доказана во всех средах».</p><p>Свяжите ADR с отдельным evidence question. Для cache это вопрос о допустимой freshness и о том, как обнаружить нарушение. Для миграции это вопрос о совместимости схемы и обратном пути. Для security-решения это вопрос о threat model и обязательной проверке. Не подменяйте evidence красивой формулировкой в разделе Consequences.</p><h2>Порядок работы с одной перепиской</h2><ol><li><strong>Ограничьте вопрос.</strong> Сформулируйте один выбор, его границу и затронутый контракт.</li><li><strong>Соберите факты.</strong> Запишите наблюдаемый симптом, дату, источник, owner и цену неверного выбора. Догадки пометьте как гипотезы.</li><li><strong>Назовите варианты.</strong> Добавьте два-три реально доступных пути, включая вариант ничего не менять, если он был возможен.</li><li><strong>Сравните одинаково.</strong> Используйте один набор критериев: constraint fit, обратимость, стоимость эксплуатации и пробел в evidence.</li><li><strong>Зафиксируйте решение.</strong> Укажите выбранный вариант, status, дату и человека, который принимает решение.</li><li><strong>Опишите последствия.</strong> Назовите положительную сторону, цену, отрицательный путь и условие rollback. Не обещайте измерения, которых ещё нет.</li><li><strong>Назначьте пересмотр.</strong> Запишите дату или сигнал: изменение контракта, превышение лимита, новый класс данных или невозможность выполнить проверку.</li><li><strong>Свяжите артефакты.</strong> После реализации добавьте ссылки на code, test, metric question или runbook. Ссылки помогают навигации, но не заменяют чтение этих артефактов.</li><li><strong>Проверьте расхождение.</strong> Сравните ADR с реализацией. Если code ушёл в другой вариант, исправьте code либо оформите новый decision.</li></ol><h2>Ограничения и отрицательный путь</h2><p>ADR не заменяет design document, threat model, migration plan, benchmark, test plan, runbook или incident review. Он не гарантирует полноту альтернатив и не превращает согласие участников в технический факт. Формат нужно подстроить под локальные правила хранения, доступа и approval.</p><p>Не редактируйте старую запись так, чтобы она описывала новое решение. История нужна именно для ответа на вопрос «почему раньше сделали так». Если предпосылка исчезла, старый ADR получает статус superseded, а новый record объясняет следующий компромисс. Это сохраняет причинность и не заставляет будущего читателя угадывать, какая версия текста была действующей.</p><p>Не называйте synthetic пример production-результатом. Не добавляйте вымышленные числа, названия сервисов и ссылки на несуществующие dashboards. Если факт нельзя проверить, напишите, какой артефакт должен его подтвердить. Такой пробел полезнее уверенного, но ложного вывода.</p><h2>Проверяемый критерий готовности</h2><p>ADR готов, когда новый читатель без поиска по чату может назвать симптом, constraint, цену ошибки, выбранный вариант и отклонённые альтернативы. Он видит владельца, статус и дату или сигнал пересмотра. Он понимает отрицательный путь и знает, где проверяется реализация. При сравнении с code не возникает скрытого второго решения. Если хотя бы один пункт требует догадки, запись ещё не готова.</p><h2>Проверяемые источники</h2><ul><li><a href=\"https://adr.github.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Architectural Decision Records</a> — официальный сайт сообщества ADR: record фиксирует решение и rationale, включая trade-offs и consequences.</li><li><a href=\"https://github.com/adr/madr\" target=\"_blank\" rel=\"noopener noreferrer\">MADR: Markdown Architectural Decision Records</a> — официальный репозиторий шаблонов и документации MADR с вариантами обязательных и расширенных разделов.</li></ul>"}