agent-quality-kit 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +155 -0
  3. package/kit/docs/ai/agent-harness-playbook.md +596 -0
  4. package/kit/docs/ai/ai-native-development.md +371 -0
  5. package/kit/docs/ai/ai-sdlc.md +221 -0
  6. package/kit/docs/ai/anthropic-ai-native-sdlc-2026-08.md +294 -0
  7. package/kit/docs/ai/app-owner-strategy.md +921 -0
  8. package/kit/docs/ai/deep-research-2026-07.md +161 -0
  9. package/kit/docs/ai/harness-best-practices.md +385 -0
  10. package/kit/docs/ai/index.md +64 -0
  11. package/kit/docs/ai/project-baseline.md +261 -0
  12. package/kit/docs/ai/quality-gates-checklist.md +322 -0
  13. package/kit/docs/ai/sources-building-with-agents.md +111 -0
  14. package/kit/docs/ai/stream-2026-08-ai-coding-panel.md +304 -0
  15. package/kit/docs/ready-made-rules.md +170 -0
  16. package/kit/gates/README.md +231 -0
  17. package/kit/gates/_skip.sh +75 -0
  18. package/kit/gates/commit-explains-itself/README.md +45 -0
  19. package/kit/gates/commit-explains-itself/check.sh +63 -0
  20. package/kit/gates/commit-explains-itself/gate.yml +10 -0
  21. package/kit/gates/commit-explains-itself/green/COMMIT_MSG +6 -0
  22. package/kit/gates/commit-explains-itself/red/COMMIT_MSG +3 -0
  23. package/kit/gates/complexity-limit/README.md +37 -0
  24. package/kit/gates/complexity-limit/check.sh +44 -0
  25. package/kit/gates/complexity-limit/gate.yml +13 -0
  26. package/kit/gates/complexity-limit/green/flat.py +10 -0
  27. package/kit/gates/complexity-limit/red/deep.py +9 -0
  28. package/kit/gates/dead-code/README.md +30 -0
  29. package/kit/gates/dead-code/gate.yml +23 -0
  30. package/kit/gates/dead-code/green/mod.py +9 -0
  31. package/kit/gates/dead-code/red/mod.py +9 -0
  32. package/kit/gates/deps-are-pinned/README.md +29 -0
  33. package/kit/gates/deps-are-pinned/check.sh +49 -0
  34. package/kit/gates/deps-are-pinned/gate.yml +9 -0
  35. package/kit/gates/deps-are-pinned/green/nodep-go/go.mod +3 -0
  36. package/kit/gates/deps-are-pinned/green/package-lock.json +3 -0
  37. package/kit/gates/deps-are-pinned/green/package.json +4 -0
  38. package/kit/gates/deps-are-pinned/green/requirements.txt +2 -0
  39. package/kit/gates/deps-are-pinned/red/package.json +4 -0
  40. package/kit/gates/deps-are-pinned/red/requirements.txt +2 -0
  41. package/kit/gates/deps-are-pinned/red/withdep-go/go.mod +5 -0
  42. package/kit/gates/duplicate-code/README.md +40 -0
  43. package/kit/gates/duplicate-code/check.sh +58 -0
  44. package/kit/gates/duplicate-code/gate.yml +12 -0
  45. package/kit/gates/duplicate-code/green/common.py +9 -0
  46. package/kit/gates/duplicate-code/green/use.py +9 -0
  47. package/kit/gates/duplicate-code/red/a.py +12 -0
  48. package/kit/gates/duplicate-code/red/b.py +12 -0
  49. package/kit/gates/entry-links-exist/README.md +22 -0
  50. package/kit/gates/entry-links-exist/check.sh +24 -0
  51. package/kit/gates/entry-links-exist/gate.yml +16 -0
  52. package/kit/gates/entry-links-exist/green/AGENTS.md +5 -0
  53. package/kit/gates/entry-links-exist/green/rules/general.md +3 -0
  54. package/kit/gates/entry-links-exist/red/AGENTS.md +3 -0
  55. package/kit/gates/file-size-limit/README.md +22 -0
  56. package/kit/gates/file-size-limit/check.sh +34 -0
  57. package/kit/gates/file-size-limit/gate.yml +9 -0
  58. package/kit/gates/file-size-limit/green/a.py +251 -0
  59. package/kit/gates/file-size-limit/green/b.py +251 -0
  60. package/kit/gates/file-size-limit/red/big.py +601 -0
  61. package/kit/gates/gate-has-samples/README.md +29 -0
  62. package/kit/gates/gate-has-samples/check.sh +48 -0
  63. package/kit/gates/gate-has-samples/gate.yml +9 -0
  64. package/kit/gates/gate-has-samples/green/.aqk.yml +10 -0
  65. package/kit/gates/gate-has-samples/green/gates/no-print-in-prod/check.sh +2 -0
  66. package/kit/gates/gate-has-samples/green/gates/no-print-in-prod/green/good.py +2 -0
  67. package/kit/gates/gate-has-samples/green/gates/no-print-in-prod/red/bad.py +1 -0
  68. package/kit/gates/gate-has-samples/red/.aqk.yml +10 -0
  69. package/kit/gates/gate-has-samples/red/gates/no-print-in-prod/check.sh +2 -0
  70. package/kit/gates/gates-are-runnable/README.md +23 -0
  71. package/kit/gates/gates-are-runnable/check.sh +35 -0
  72. package/kit/gates/gates-are-runnable/gate.yml +9 -0
  73. package/kit/gates/gates-are-runnable/green/.aqk.yml +9 -0
  74. package/kit/gates/gates-are-runnable/green/checks/lint.sh +2 -0
  75. package/kit/gates/gates-are-runnable/red/.aqk.yml +6 -0
  76. package/kit/gates/gates-run-in-ci/README.md +29 -0
  77. package/kit/gates/gates-run-in-ci/check.sh +42 -0
  78. package/kit/gates/gates-run-in-ci/gate.yml +12 -0
  79. package/kit/gates/gates-run-in-ci/green/.aqk.yml +6 -0
  80. package/kit/gates/gates-run-in-ci/green/.github/workflows/ci.yml +7 -0
  81. package/kit/gates/gates-run-in-ci/green/checks/lint.sh +2 -0
  82. package/kit/gates/gates-run-in-ci/red/.aqk.yml +6 -0
  83. package/kit/gates/gates-run-in-ci/red/.github/workflows/ci.yml +7 -0
  84. package/kit/gates/gates-run-in-ci/red/checks/lint.sh +2 -0
  85. package/kit/gates/lesson-has-outcome/README.md +37 -0
  86. package/kit/gates/lesson-has-outcome/check.sh +50 -0
  87. package/kit/gates/lesson-has-outcome/gate.yml +11 -0
  88. package/kit/gates/lesson-has-outcome/green/.aqk.yml +2 -0
  89. package/kit/gates/lesson-has-outcome/green/incidents/README.md +32 -0
  90. package/kit/gates/lesson-has-outcome/red/.aqk.yml +2 -0
  91. package/kit/gates/lesson-has-outcome/red/incidents/README.md +13 -0
  92. package/kit/gates/no-print-in-prod/README.md +44 -0
  93. package/kit/gates/no-print-in-prod/check.sh +36 -0
  94. package/kit/gates/no-print-in-prod/gate.yml +15 -0
  95. package/kit/gates/no-print-in-prod/green/docs.ts +15 -0
  96. package/kit/gates/no-print-in-prod/green/legacy.py +9 -0
  97. package/kit/gates/no-print-in-prod/green/main.go +8 -0
  98. package/kit/gates/no-print-in-prod/green/main.rs +4 -0
  99. package/kit/gates/no-print-in-prod/green/service.py +8 -0
  100. package/kit/gates/no-print-in-prod/red/main.go +8 -0
  101. package/kit/gates/no-print-in-prod/red/main.rs +4 -0
  102. package/kit/gates/no-print-in-prod/red/service.py +3 -0
  103. package/kit/gates/secrets-not-in-code/README.md +29 -0
  104. package/kit/gates/secrets-not-in-code/check.sh +18 -0
  105. package/kit/gates/secrets-not-in-code/gate.yml +9 -0
  106. package/kit/gates/secrets-not-in-code/green/settings.py +4 -0
  107. package/kit/gates/secrets-not-in-code/red/settings.py +2 -0
  108. package/kit/gates/swallowed-error/README.md +26 -0
  109. package/kit/gates/swallowed-error/check.sh +54 -0
  110. package/kit/gates/swallowed-error/gate.yml +12 -0
  111. package/kit/gates/swallowed-error/green/loader.py +11 -0
  112. package/kit/gates/swallowed-error/green/run.js +8 -0
  113. package/kit/gates/swallowed-error/red/loader.py +5 -0
  114. package/kit/gates/swallowed-error/red/run.js +3 -0
  115. package/kit/gates/todo-without-task/README.md +26 -0
  116. package/kit/gates/todo-without-task/check.sh +19 -0
  117. package/kit/gates/todo-without-task/gate.yml +12 -0
  118. package/kit/gates/todo-without-task/green/order.py +9 -0
  119. package/kit/gates/todo-without-task/red/order.py +8 -0
  120. package/kit/ratchet/ratchet.sh +62 -0
  121. package/kit/rules/general.md +55 -0
  122. package/kit/rules/security.md +33 -0
  123. package/kit/rules/testing.md +46 -0
  124. package/package.json +41 -0
  125. package/tool/commands/doctor.mjs +230 -0
  126. package/tool/commands/gates.mjs +445 -0
  127. package/tool/commands/project.mjs +316 -0
  128. package/tool/lib/core.mjs +98 -0
  129. package/tool/lib/manifest.mjs +140 -0
  130. package/tool/lib/repo.mjs +270 -0
  131. package/tool/lib/templates.mjs +187 -0
  132. package/tool/program.mjs +81 -0
  133. package/tool/selfcheck/conditional.sh +24 -0
  134. package/tool/selfcheck/gates.sh +127 -0
  135. package/tool/selfcheck/smoke.sh +539 -0
  136. package/tool/selfcheck/syntax.sh +23 -0
  137. package/tool/selfcheck/units.mjs +105 -0
@@ -0,0 +1,921 @@
1
+
2
+ <!-- источник: audit_project/docs/ai/Стратегия_по_разработки_приложений.md -->
3
+ Существует принципиально 2 разных слоя разработки:
4
+ - Слой A — харнес разработки (.claude/: пайплайн агентов, хуки, скиллы, память). Тут мы зрелые, местами впереди сигнала.
5
+ - Слой B — сам продукт
6
+
7
+ ---
8
+ Список действий
9
+
10
+ 1. ФОРМУЛИРОВКА ЗАДАЧИ (БИЗНЕС ТЗ)
11
+ 2. ОГРАНИЧЕНИЯ (constraints): данности среды
12
+ 3. САЙЗИНГ: цифры
13
+ 4. ТЕХ-ДИЗАЙН: как именно   ← стек, топология, модель данных, API
14
+ ---
15
+
16
+ Общие принципы
17
+
18
+ 1. Декомпозиция — разбить туманную цель на части, которые правильно складываются.
19
+ 2. Предвидение отказов — знать, что и как может сломаться
20
+ 3. Замыкание обратной связи — сделать систему прозрачной (наблюдаемой)
21
+
22
+ ---
23
+ # ФОРМУЛИРОВКА ЗАДАЧИ (БИЗНЕС ТЗ)
24
+
25
+ Описание самого проекта и зачем в целом он создается. Здесь крупно описывается, смысл, цель и стремления которые хотят быть осуществлены заказчиком. Необходимо для формирования фундамента и выбора правильно архитектуры и стека проекта. Задача должна быть описана простыми словами без технической составляющей, ровно таким способом как бы это делал человек не зная вообще программирование. Чем подробней и детальней будет описано виденье проекта тем лучше. Как итог получаем 1 файлик.
26
+
27
+ Данный файл эволюционирует вместе с проектом и в процессе разработки может дополняться и расширяться.
28
+
29
+ 1. Необходимо описать проблему (боль) зачем в целом нужен программный продукт
30
+ 2. Цель и критерий успеха (измеримые)
31
+ 3. Пользователи (роли, язык)
32
+ 4. и тд можно дополнять пункты
33
+
34
+
35
+
36
+ # РЕСУРСОЕМКОСТЬ + ОГРАНИЧЕНИЯ (constraints)
37
+
38
+ 1. Описание того сколько человек будет заниматься проектом.
39
+ 2. Какие компьютерный мощности у нас есть
40
+ 3. Где распологается сервер
41
+ 4. Есть ли домен
42
+ 5. Бюджет
43
+ 6. Сроки
44
+ 7. и тд то есть вопросы общего характера
45
+
46
+
47
+ # Сайзинг
48
+
49
+ Cуществует устоявшийся список — нефункциональные требования (NFR), стандарт ISO/IEC 25010 и чек-лист Volere. Там не 8, а ~10-14 категорий. (Forasoft — 14 категорий NFR (https://www.forasoft.com/blog/article/non-functional-requirements-checklist-2026), Altexsoft (https://www.altexsoft.com/blog/non-functional-requirements/))
50
+
51
+ Прежде чем строить, надо понять «какая нагрузка нас ждёт» — чтобы сервер не упал, но и чтобы не построить дорогущий кластер там, где хватит одного компа.
52
+
53
+ <font color="#ffff00">DAU </font>— сколько людей пользуется за день?
54
+ DAU = Daily Active Users = сколько разных аудиторов реально заходят и работают за сутки.
55
+ Пример: 100 аудиторов в день.
56
+ 👉 Но не все они кликают в одну и ту же секунду. Поэтому следующий вопрос:
57
+
58
+ <font color="#ffff00">Пиковая одновременность</font> — сколько работают В ОДИН МОМЕНТ (в час пик)?
59
+ Из 100 за день — сколько сидят и кликают одновременно в самый загруженный момент (скажем, 11 утра)?
60
+ Пример: пик — 30 одновременно.
61
+ 👉 Грузят систему те, кто работает СЕЙЧАС, а не «за весь день». Но и сидящие не долбят без остановки. Поэтому:
62
+
63
+
64
+ <font color="#ffff00">Частота запросов (RPS)</font> — сколько кликов/запросов в секунду прилетает на сервер?
65
+ Каждый сидящий открывает отчёт/фильтрует раз в сколько-то секунд. Сложили всех → столько запросов в секунду валится на сервер. RPS = requests per second.
66
+ Это и есть «сколько работы в секунду».
67
+ 👉 Но запросы бывают разные — одни читают, другие пишут. Поэтому:
68
+
69
+ <font color="#ffff00">Read/Write</font> — доля чтения и записи?
70
+ «Чтение» = просто посмотреть/построить отчёт (ничего не меняем). «Запись» = изменить/добавить данные (у нас — загрузка снимка из 1С).
71
+ Пример: у нас почти всё — чтение. Запись редкая (по клику админа).
72
+ 👉 Это ВАЖНО: чтение масштабировать легко (копии, кэш), запись — тяжело.
73
+
74
+ <font color="#ffff00">Объём данных + рост</font> — сколько данных и как быстро растёт?
75
+ Пример: на клиента — миллионы строк, клиентов — тысячи. Много.
76
+ 👉 Миллион строк и миллиард — это разные машины и подходы (индексы, архивация старых клиентов).
77
+
78
+
79
+ <font color="#ffff00">Латентность (скорость ответа)</font> — как быстро отвечаем?
80
+ Латентность = задержка, сколько ждёшь ответа. «p95 ≤ 1 сек» читается так: «95% запросов быстрее 1 секунды» (почти всем быстро, кроме худших 5%).
81
+ Пример: хотим отчёт за ≤1 сек для 95% случаев.
82
+ 👉 Это наша цель по скорости, под неё строим.
83
+
84
+
85
+ <font color="#ffff00">Доступность (SLO) Service Level Objectiv</font> — сколько система обязана быть «живой»? «99.9%» = разрешено лежать ~8 часов в год. SLO = твоё обещание по доступности.
86
+ Пример: для внутреннего аудит-инструмента 99.9% норм.
87
+ 👉 Чем выше требование — тем больше дублирования (и денег).
88
+
89
+ <font color="#ffff00">Консистентность</font> — можно ли показывать слегка устаревшие данные? Если можно секунду показать «старое» — можно кэшировать (быстро и дёшево). Если нужно строго самое свежее — сложнее.
90
+
91
+ <font color="#ffff00">Рост / запас (headroom)</font> — на сколько раз вперёд закладываемся? (×2 за год? ×10?) Влияет на то, оставлять ли место для масштабирования.
92
+
93
+ <font color="#ffff00">Паттерн нагрузки / сезонность</font> — откуда берётся «пик»? У аудита есть горячий сезон (сдача отчётности) → пик в разы выше среднего.
94
+
95
+
96
+ 🔒 Безопасность и доступ
97
+
98
+
99
+ 💾 Хранение + бэкап/восстановление (RPO / RTO)
100
+
101
+ - RPO (Recovery Point Objective) = сколько данных не жалко потерять, в единицах времени. «RPO = 1 час» → бэкап минимум раз в час.
102
+ - RTO (Recovery Time Objective) = за сколько обязаны поднять систему после сбоя. «RTO = 2 часа».
103
+ - Срок хранения (retention) — сколько лет держим (аудит — по закону годами).
104
+
105
+ 🌐 Совместимость / локализация
106
+
107
+
108
+ ▎ 1. Литтл ($L=λW$) — связь «сколько одновременно / как часто / как долго». Рычаг: ускоряй запрос (W↓).
109
+ ▎ 2. Колено ($W=\frac{S}{1-ρ}$) — у 100% задержка взрывается → держи пик на ~70%, оставляй запас.
110
+ ▎ 3. Всплески — планируй по пику (пик-фактор), меряй p95/p99 из реальных трейсов, гаси burst кэшем/очередью/лимитом.
111
+ ▎ 4. Надёжность — девятки дороги; Люссер: меньше узлов = надёжнее (P4); чинись быстро (MTTR↓ = P6).
112
+
113
+
114
+ Утилизация и «колено»: почему нельзя грузить на 100%
115
+
116
+ Что такое утилизация
117
+
118
+ Утилизация (ρ, «ро») = насколько сервер занят, в долях от потолка. 0% — простаивает, 100% — забит под завязку.
119
+ Пример: потолок 2 запроса/с, приходит 1 запрос/с → утилизация 50%.
120
+
121
+ Главная (неинтуитивная) правда
122
+
123
+ Кажется логичным: «потолок 2/с — значит гоним 2/с, выжимаем по максимуму». Это грубая ошибка.
124
+
125
+ ▎ Когда утилизация подходит к 100%, задержка растёт не плавно — она ВЗРЫВАЕТСЯ.
126
+
127
+ Интуиция — шоссе (без формул)
128
+
129
+ Дорога с пропускной способностью X машин/час:
130
+ - загружена на 50% → едешь свободно, задержки ноль;
131
+ - на 80% → начинает уплотняться, лёгкие торможения;
132
+ - на 95% → пробка, одна лишняя машина = большая задержка для всех;
133
+ - на 100% → намертво стоит.
134
+
135
+ Пропускная способность та же, а ожидание у предела взрывается. Почему? Потому что машины приходят неравномерно (всплесками). Когда дорога почти полная,
136
+ любому случайному сгустку некуда деться → затор каскадом.
137
+
138
+ С сервером ровно так же: запросы приходят неровно (случайность/burst). У 100% занятости любой всплеск упирается в отсутствие свободной ёмкости → очередь →
139
+ задержка летит в небо.
140
+
141
+ Формула (простая) и почему это «колено»
142
+
143
+ $$W = \frac{S}{1 - \rho}$$
144
+ W — полное время (ожидание + обработка), S — время одного запроса, ρ — утилизация.
145
+
146
+ 🔑 Практическое правило Никогда не планируй сервер выше ~70-80% утилизации. Оставляй запас (это и есть наш пункт 15 — headroom).
147
+
148
+
149
+
150
+ Всплески и вероятность: почему среднее врёт
151
+
152
+ Запросы приходят НЕровно (это и есть burst) Пользователи не кликают как метроном. Они сбиваются в кучу: все логинятся в 9:00, все жмут «обновить» после загрузки снимка, конец дня.
153
+
154
+ - Научная модель этого — пуассоновский поток: случайные независимые приходы со средней частотой. Главное свойство: даже при стабильном среднем бывают
155
+ случайные сгустки.
156
+ - Интуиция — дождь: в среднем 10 капель/мин, но они не падают строго раз в 6 сек — иногда 3 разом.
157
+ - Пик-фактор = пиковая частота ÷ средняя. Вот почему планируем по пику, а не по среднему. У людей часто ×5…×20. А сезонность аудита (горячий сезон
158
+ отчётности) умножает ещё.
159
+
160
+ → Связка с прошлым: burst поднимает утилизацию → ты подлетаешь к «колену» (Шаг 2) → задержка взрывается. Поэтому пик — это не каприз, это где система
161
+ умирает.
162
+
163
+
164
+ Среднее ВРЁТ → меряем перцентилями
165
+
166
+ Если смотришь только на среднюю задержку — ты прячешь больной хвост.
167
+
168
+ - Перцентили: p50 (медиана) = половина быстрее; p95 = 95% быстрее (худшие 5% медленнее); p99 = 99% быстрее (худший 1%).
169
+ - Почему важно: среднее может быть 100 мс, а p99 = 3 сек — значит каждый сотый запрос ужасен. Аудитор за сессию делает ~100 действий → он гарантированно
170
+ поймает этот 3-сек тормоз. На масштабе p99 = много реально страдающих людей.
171
+ - Принцип Amazon: оптимизируют хвост (p99), а не среднее — потому что именно худшие случаи формируют ощущение «тормозит».
172
+ - ⚠️ И хвост раздувается под нагрузкой (Шаг 2): в burst p99 улетает, даже если среднее выглядит ок.
173
+
174
+ Часть C. Перцентили НЕ угадывают — их МЕРЯЮТ (вот тут наши рекомендации)
175
+
176
+ Это прямо из knowledge:
177
+ - Валера: «смотри логи всех сторон» + наблюдаемость (трейсы) → ты читаешь реальный p95/p99, а не предполагаешь.
178
+ - Термометр (твоя же метафора + Рефат): p99 говорит «ты болен», трейс показывает ГДЕ (вон тот медленный запрос к БД). Метрика диагностирует, трейс лечит.
179
+ - Это и есть внешний арбитр (P2): latency не «по ощущениям», а измеренная из реальности.
180
+
181
+
182
+ 🛡️ Надёжность: «девятки», Люссер, резерв
183
+
184
+ Часть A. «Девятки» — что такое доступность в цифрах
185
+
186
+ Доступность = % времени, что система жива. Каждая девятка дороже предыдущей ~в 10 раз:
187
+
188
+ ┌─────────────┬───────────────┬──────────────────┐
189
+ │ Доступность │ Простой в год │ Как зовут │
190
+ ├─────────────┼───────────────┼──────────────────┤
191
+ │ 99% │ ~3.6 дня │ «две девятки» │
192
+ ├─────────────┼───────────────┼──────────────────┤
193
+ │ 99.9% │ ~8.8 часов │ «три девятки» │
194
+ ├─────────────┼───────────────┼──────────────────┤
195
+ │ 99.99% │ ~53 минуты │ «четыре девятки» │
196
+ ├─────────────┼───────────────┼──────────────────┤
197
+ │ 99.999% │ ~5 минут │ «пять девяток» │
198
+ └─────────────┴───────────────┴──────────────────┘
199
+
200
+ 👉 Не гонись за девятками бездумно — каждая стоит кратно дороже. Выбери нужную и остановись.
201
+
202
+ Часть B. Закон Люссера — почему каждый узел РОНЯЕТ надёжность
203
+
204
+ Если узлы стоят последовательно (для работы нужны ВСЕ — цепочка), их надёжности перемножаются:
205
+ $$R = R_1 \times R_2 \times ... \times R_n$$
206
+
207
+ Каждый $R_i < 1$ → чем больше узлов, тем ниже итог. Считаем:
208
+ - 5 узлов по 99% → $0.99^5 = 0.951$ → всего 95%! (хотя каждый «надёжный»)
209
+ - 10 узлов по 99.9% → $0.999^{10} = 0.990$ → 99%
210
+
211
+ ▎ «Цепь не прочнее слабого звена» — а Люссер уточняет: она ещё слабее, потому что звенья перемножаются.
212
+
213
+ 🔑 Это математическое доказательство нашего P4 (минимальная достаточность): каждый лишний сервис/зависимость/узел умножает твою надёжность вниз. Меньше
214
+ деталей = надёжнее. Ровно «чем проще велосипед, тем надёжнее едет» — теперь ты знаешь, почему это математически верно.
215
+
216
+ Часть C. Резерв (параллель) — как отбиваться
217
+
218
+ Если поставить узлы параллельно (система жива, если работает ХОТЯ БЫ один — резерв), перемножаются уже вероятности отказа:
219
+ $$R = 1 - (1-R_1)(1-R_2)...$$
220
+
221
+ - 2 сервера по 99% → отказ каждого 1% → оба упали $0.01 × 0.01 = 0.0001$ → доступность 99.99%!
222
+
223
+ Два дешёвых ненадёжных в параллели = очень надёжно. Вывод: последовательное перемножает надёжность вниз (плохо) → минимизируй узлы (P4); параллельное
224
+ перемножает отказ вниз (хорошо) → добавляй резерв только там, где реально нужны девятки (это деньги).
225
+
226
+ Часть D. MTBF / MTTR — два рычага (и связь с P6!)
227
+
228
+ $$\text{Доступность} = \frac{MTBF}{MTBF + MTTR}$$
229
+ - MTBF = среднее время между поломками (как редко ломается).
230
+ - MTTR = среднее время починки (как быстро поднимаешь).
231
+
232
+ 🔑 Доступность улучшают двумя способами: ломаться реже (MTBF↑) ИЛИ чиниться быстрее (MTTR↓). И часто быстро чиниться — дешевле, чем никогда не ломаться.
233
+
234
+ → Это и есть наш P6 «дёшево ошибаться, быстро чинить» на языке математики! А из knowledge: NixOS-откат и snapshot-revert (Валера/Абдуллин) — это буквально
235
+ оптимизация MTTR (сломал → откатил за секунды).
236
+
237
+ ---
238
+
239
+ # Этап 4 — Архитектура
240
+
241
+ Осуществляется на основе предыдущих этапов
242
+
243
+ - SOLID (5 принципов) — особенно Single Responsibility (одна работа на модуль — это мы и делали) и Dependency Inversion (завись от интерфейсов, не от конкретики).
244
+ - Coupling & Cohesion — это почему за слоями: low coupling (части слабо связаны) + high cohesion (родственный код вместе). Главная мера хорошей структуры.
245
+ - Стили архитектуры и выбор — слоистая (наша), Clean/Hexagonal (домен изолирован от внешнего), модульный монолит, микросервисы.
246
+ - 🔴 Ключевое правило: «монолит-первым» (модульный монолит); микросервисы — только когда заставит масштаб (они добавляют ад распределёнки)
247
+
248
+
249
+ <font color="#ffff00">Модель данных: фундамент всего</font>
250
+
251
+ Почему это самое дорогое (повтор, но важно)
252
+
253
+ Код менять легко. Данные — тяжело. Почему: если поменял схему, надо перенести (мигрировать) все уже накопленные строки — миллионы. Код переписал за
254
+ минуты, а данные так просто не переложишь. Поэтому схема — это фундамент дома: стены (код) перекрасишь, а фундамент потом не подвинешь.
255
+
256
+ Что такое «модель данных» (на пальцах)
257
+
258
+ Это три решения:
259
+ 1. Какие «вещи» храним → таблицы (Клиент, Аудитор, Проводка, Счёт…).
260
+ 2. Какие у них поля → колонки (у Клиента: id, название, ИНН).
261
+ 3. Как они связаны → связи (Проводка принадлежит Клиенту).
262
+
263
+ Почему модель проектируешь ТЫ, а не агент
264
+
265
+ Схема кодирует доменную правду, которую знает только эксперт:
266
+ - двойная запись (у проводки дебет-счёт И кредит-счёт),
267
+ - что такое сальдо, как считается ОСВ,
268
+ - грязь 1С (byte-swap UUID, физические имена _Fld620).
269
+
270
+ Агент твоего домена не знает → тут твоя аудиторская экспертиза = реальный ров. Ты определяешь модель (или жёстко ревьюишь).
271
+
272
+
273
+ Модель данных = фундамент слоя Repo (из 4.1) и контракт, на котором стоит всё остальное. Сначала она — потом логика и UI.
274
+
275
+
276
+ - Нормализация (1NF/2NF/3NF) — не дублируй данные (одно и то же в одном месте). + денормализация ради скорости чтения — это ровно наши матвьюхи для отчётов.
277
+ - Индексы — на колонки в WHERE/JOIN/ORDER BY: ускоряют чтение, замедляют запись. У нас чтение-тяжёлое → индексы важны.
278
+ - 🔴 Транзакции / ACID (важный пропуск для тебя!) — операция «всё-или-ничего». Sync 1С должен быть атомарным: либо загрузился весь снимок, либо откат. Иначе полузагруженные данные = битый «байт-в-байт». Это критично для аудита.
279
+ - SQL vs NoSQL — реляционная (SQL) для связанных данных с целостностью (твой случай — однозначно SQL). NoSQL — для иного, не твоё.
280
+
281
+
282
+ <font color="#ffff00">Контракт API: договор между фронтом и бэком</font>
283
+
284
+ - 🟢 OpenAPI / Swagger (сильный добавок!) — контракт пишут как машиночитаемую спеку. Она = и документация, и контракт, из неё генерят моки/проверки.
285
+ Идеально для агента (это «legible» контракт, который агент читает) — прямо в дух harness-инжиниринга.
286
+ - Идемпотентность — повтор одного запроса = тот же результат. Idempotency-Key на POST → решает «двойной клик» на уровне API (а не только теста).
287
+ - Валидация на входе — проверяй данные на границе, fail fast, возвращай 422 с ошибками по полям (это и «разбирай формы на границе» из статьи OpenAI).
288
+ - Курсорная vs offset пагинация — курсорная стабильнее, когда данные меняются. Нюанс в нашу пользу: у нас снимок неизменен между sync → offset нам ок
289
+ (проще).
290
+
291
+
292
+ Как проектировать контракт (практика)
293
+
294
+ 1. Контракт — ПЕРВЫМ (до фронта и бэка). Обе стороны договариваются о форме → потом каждый строит под неё. Вот почему «бэк раньше фронта» = на деле
295
+ «контракт раньше обоих». Можно даже фронт делать на «фейковом» бэке (заглушке), пока бэк пишется.
296
+ 2. Форму делай под нужду ФРОНТА, а не под таблицы БД. Не вываливай сырые строки базы — отдавай то, что удобно показать. (Бэк сам перекладывает из БД в
297
+ форму ответа.)
298
+ 3. Базовые правила REST: GET = прочитать, POST = создать/действие, PUT/PATCH = изменить, DELETE = удалить. Адрес называет «вещь»: /clients, /reports.
299
+ 4. Коды ответа: 200 ок, 400 кривой запрос, 401/403 нет прав, 404 не найдено, 500 ошибка сервера. (Фронт по коду понимает, что случилось.)
300
+ 5. Пагинация для больших ответов: карточка счёта = миллионы строк → НЕ отдавай всё разом, отдавай страницами (?page=2&size=100). Это прямо из сайзинга
301
+ (латентность/трафик).
302
+ 6. Версионирование: если ВЫНУЖДЕН сломать форму — делай /api/v2/..., не ломай тихо /v1 (старые потребители продолжат жить). Это про 🔴 «дверь в одну
303
+ сторону».
304
+
305
+ Контракт = цель приёмочного теста
306
+
307
+ Кто проектирует
308
+
309
+ Ты определяешь контракт (это 🔴 граница + доменная форма), агент реализует. И договариваешься о нём до постройки обеих сторон — чтобы фронт и бэк не
310
+ разъехались.
311
+
312
+ Брандмауэр (firewall, «файрвол») — это система, которая фильтрует сетевой трафик: решает, какие соединения пропустить, а какие заблокировать. По сути —
313
+ «охранник на двери» между сетями.
314
+
315
+
316
+ <font color="#ffff00">🧰 Выбор стека: «скучные технологии»</font>
317
+
318
+ Принцип
319
+
320
+ По умолчанию бери скучные технологии — проверенные, популярные, стабильные. Не потому что «старьё хорошо», а потому что:
321
+ - 🤖 их много в обучении модели → агент пишет их хорошо, и ты можешь проверить (полно примеров/доков). Экзотику агент галлюцинирует, а ты не сверишь.
322
+ - 👥 большое комьюнити = грабли уже собраны, паттерны проверены, «если сломается — есть кому помочь».
323
+ - 🛡 стабильно = меньше сюрпризов (Люссер/P4).
324
+
325
+ Два правила
326
+
327
+ 1. Меньше технологий = надёжнее (Люссер). Не тащи новую либу под каждую мелочь — переиспользуй.
328
+ 2. Стек = 🔴 дорого менять (язык, БД). Выбирай вдумчиво. А на дешёвом (какая именно Excel-либа) — не агонизируй, поменяешь.
329
+
330
+
331
+ Цикл постройки
332
+
333
+ Единица работы — вертикальный срез
334
+
335
+ Напомню: строишь не «слой», а тонкий срез сквозь все слои — одну фичу от данных до UI, работающую end-to-end. Так петля обратной связи живёт с первого
336
+ дня, а не «в конце ад интеграции».
337
+
338
+ Цикл одного среза — пошагово (рецепт, который повторяешь)
339
+
340
+ Шаг 0. Выбери наименьший срез. Не «весь сайт», а одна фича. Пример: отчёт ОСВ для одного клиента.
341
+
342
+ Шаг 1. Мини-спека среза (5–10 строк): что делает, входы/выходы. «Аудитор выбирает клиента+период → видит ОСВ → итоги совпадают с 1С → может выгрузить в
343
+ Excel».
344
+
345
+ Шаг 2. Приёмочный e2e-тест = арбитр, ПЕРВЫМ. До кода. «POST /api/reports/osv {client_id, period} → totals = эталон из 1С байт-в-байт». Это и есть
346
+ «готово».
347
+
348
+ Шаг 3. Предскажи (письменно): что жду + что может сломаться («наверное, поплывут sentinel-даты», «UUID byte-swap»). Это твой тренажёр суждения.
349
+
350
+ Шаг 4. Агент строит срез — конкретным заданием, не «сделай ОСВ»:
351
+
352
+ ▎ «Сделай срез ОСВ: (1) repo getEntries(schema, period); (2) service buildOSV с правилами сальдо/оборотов; (3) endpoint POST /api/reports/osv; (4)
353
+ ▎ React-страница с гридом. Должен пройти вот этот приёмочный тест: [...]. Тесты — только этот e2e + юнит на расчёт сальдо.»
354
+
355
+ Шаг 5. Запусти и проверь ВНЕ себя: тесты зелёные + подними приложение + глазами дёрни отчёт + глянь логи всех сторон. (Не «агент сказал готово» — а арбитр
356
+ сказал + ты увидел.)
357
+
358
+ Шаг 6. Сравни предсказание ↔ реальность. Где разрыв — пойми ПОЧЕМУ (вот тут рождается твоё понимание), потом чини. Сначала своя гипотеза, потом спрашивай
359
+ агента.
360
+
361
+ Шаг 7. Ревью → мердж → деплой. Cross-model ревью (другой агент проверил), короткий PR, выкатил.
362
+
363
+ Шаг 8. Повторяющийся баг → затяни контроль. Если что-то сломалось второй раз — новый тест / линтер / правило в доке (это harness-петля OpenAI: «сделай
364
+ способность enforceable»). Дальше — следующий срез (карточка счёта).
365
+
366
+ Ralph-loop (автономный цикл)
367
+
368
+ Внутри Шага 4-5 агент может крутить сам: запустил тест → не прошёл → поправил → запустил → ... пока арбитр не станет зелёным. Ты задаёшь цель + арбитра,
369
+ агент итерирует. (Для этого дай агенту возможность сам запускать тесты/приложение — «agent-legible» среда из статьи OpenAI.)
370
+
371
+ Правила работы с агентом
372
+
373
+ - Мелкие срезы, короткие PR (фиксы дёшевы, ожидание дорого).
374
+ - Задание конкретное (что в каком слое), тесты — списком, не «напиши тесты».
375
+ - Дай агенту сам запускать и проверять (тесты, приложение, логи) — тогда он сам себя верифицирует.
376
+
377
+ Грабли (из knowledge)
378
+
379
+ - ⚠️ Агент рапортует «готово» на ~80% → проверяй финал (лучше в новой сессии).
380
+ - ⚠️ Агент ленится: может закомментить тест ради зелёного → e2e-арбитр это ловит (он про поведение, не подкрутишь).
381
+ - ⚠️ Дрейф (копирует плохие паттерны) → «золотые принципы» в репо + периодический GC-проход.
382
+
383
+
384
+
385
+
386
+ Статика (проверка БЕЗ запуска)
387
+
388
+ Что это
389
+
390
+ Статика = проверки кода, которые читают его, НЕ запуская. Самый дешёвый и быстрый слой: ловит ошибки за миллисекунды, ещё до единого теста. Три вида:
391
+
392
+ 1. Форматтер (formatter)
393
+
394
+ Авто-причёсывает код к единому стилю (отступы, пробелы, кавычки). Убирает все споры про стиль — нажал «сохранить», и код всегда одинаковый.
395
+ - Python: Ruff / Black · JS/TS: Prettier · Go: gofmt
396
+ - Не про баги — про единообразие.
397
+
398
+ 2. Линтер (linter)
399
+
400
+ Читает код и подсвечивает вероятные БАГИ и плохие паттерны: неиспользуемая переменная, обращение к несуществующей, == вместо ===, недостижимый код,
401
+ опасные конструкции.
402
+ - Python: Ruff / Flake8 · JS/TS: ESLint
403
+ - Ловит целый класс ошибок до запуска.
404
+
405
+ 3. Проверка типов (type checker)
406
+
407
+ Проверяет, что типы сходятся: не передал строку туда, где ждут число; не вызвал метод, которого нет.
408
+ - TS даёт это для фронта · Python: mypy / pyright · в Go/Rust встроено.
409
+ - 🔑 Вот почему берут TypeScript, а не голый JavaScript: TS добавляет проверку типов → ловит кучу багов до запуска.
410
+
411
+ Почему это ПЕРВАЯ линия (shift-left)
412
+
413
+ Ловит ошибку за миллисекунды, без запуска приложения и без написания теста. Самая дешёвая обратная связь. Опечатка/неверный тип/несуществующая переменная
414
+ никогда не доберутся до теста — их срубит статика.
415
+
416
+ 🤖 Связь с агентом (урок OpenAI)
417
+
418
+ - Линтер = feedforward+feedback для агента: запускается сам, агент читает ошибку → сам исправляется.
419
+ - Трюк OpenAI: пиши инструкцию по починке прямо в текст ошибки линтера → агент знает, как чинить.
420
+ - Кастомные линтеры принуждают ТВОИ инварианты — те самые правила слоёв из 4.1 («UI не импортит Repo»). Вот так архитектура держится механически, а не «на
421
+ честном слове».
422
+
423
+ Best practice
424
+
425
+ Включи статику с дня 1 (у OpenAI настройка CI/формата/линта была буквально в первом коммите). Дёшево, эффект огромный, плюс держит код агента
426
+ единообразным и legible.
427
+
428
+ 🔒 Триада безопасности в статике (для аудита/финансов — обязательно)
429
+
430
+ 1. SAST (Static Application Security Testing) — ищет уязвимости (инъекции, дыры в авторизации, кривая криптография) глубоким анализом по всему коду
431
+ (taint/data-flow). Это НЕ линтер: линтер = стиль/баги в одном файле, SAST = безопасность через весь код. Инструменты: Semgrep, Bearer, CodeQL, Bandit
432
+ (Python).
433
+ 2. Secret scanning — ловит захардкоженные секреты (ключи, пароли, токены) до того, как они попадут в git. Инструменты: gitleaks, GitGuardian. (Ровно наш
434
+ «секреты не в коде».)
435
+ 3. SCA (Software Composition Analysis) / сканирование зависимостей — проверяет чужие библиотеки на известные уязвимости (CVE), вредоносные пакеты,
436
+ лицензии + делает SBOM (опись компонентов). Инструменты: Dependabot, npm audit, Snyk. 👉 Это защита от supply-chain атак (axios/litellm из наших чатов!).
437
+
438
+
439
+ 📊 Метрики качества
440
+
441
+ - Сложность / дублирование / поддерживаемость (cyclomatic complexity, duplication). SonarQube собирает всё в одну панель. (У OpenAI это был документ
442
+ QUALITY_SCORE.md.)
443
+
444
+ ⏱ Важный нюанс ПО ВРЕМЕНИ (best practice)
445
+
446
+ - Быстрое (форматтер/линтер/типы) → на каждое сохранение/коммит (миллисекунды).
447
+ - Тяжёлое (полный SAST, SCA) → на PR / ночью в CI, НЕ на каждое нажатие (иначе тормозит работу).
448
+
449
+ 🛠 Оркестратор
450
+
451
+ - pre-commit (фреймворк) — гоняет все быстрые проверки автоматически перед коммитом.
452
+ - Минорно по инструментам: Ruff (Python — дефолт), Biome (JS/TS — современный всё-в-одном lint+format).
453
+
454
+
455
+
456
+ Шаг 2 — Тесты: как писать и запускать
457
+
458
+ Анатомия любого теста: AAA (Arrange–Act–Assert)
459
+
460
+ Любой тест = три части (это же = Given/When/Then из BDD):
461
+ Arrange (подготовь) — задай ситуацию (данные, состояние)
462
+ Act (сделай) — вызови функцию / дёрни эндпоинт
463
+ Assert (проверь) — сверь результат с ожидаемым
464
+ Наш test_ping был Act+Assert. А вот unit на бизнес-логику:
465
+ def test_build_osv_totals():
466
+ entries = [Проводка(дебет=100), Проводка(кредит=100)] # Arrange
467
+ osv = build_osv(entries) # Act
468
+ assert osv.totals.обороты == 100 # Assert
469
+
470
+ Test runner — инструмент, который находит и гоняет тесты
471
+
472
+ ┌───────────────────────────────────────────────────────────────────────────────┬───────────────────┐
473
+ │ Стек │ Раннер │
474
+ ├───────────────────────────────────────────────────────────────────────────────┼───────────────────┤
475
+ │ Python (бек) │ pytest │
476
+ ├───────────────────────────────────────────────────────────────────────────────┼───────────────────┤
477
+ │ React/TS (фронт) │ Vitest (или Jest) │
478
+ ├───────────────────────────────────────────────────────────────────────────────┼───────────────────┤
479
+ │ E2E (браузер) │ Playwright │
480
+ ├───────────────────────────────────────────────────────────────────────────────┼───────────────────┤
481
+ │ Запускаешь (pytest) → он находит все test_*, гоняет, пишет ✅/❌ + что упало. │ │
482
+ └───────────────────────────────────────────────────────────────────────────────┴───────────────────┘
483
+
484
+ Как писать каждый тип (на нашем стеке)
485
+
486
+ - Unit (pytest): чистая функция (расчёт сальдо, byte-swap UUID). Быстро, без БД.
487
+ - Integration (pytest + реальная тестовая БД): repo против настоящего Postgres — «сохранил → достал → совпало».
488
+ - E2E (Playwright): робот открывает страницу, кликает, проверяет, что отчёт совпал с эталоном.
489
+
490
+ Test doubles — подделки (mock / stub / fake)
491
+
492
+ Подменяешь медленное/внешнее (например, источник 1С) фейком → тесты быстрые и детерминированные.
493
+ ⚠️ Но не перемокай! Если замокать всё — тест ничего реального не проверяет. Для integration бери реальную тестовую БД, а не мок.
494
+
495
+ Coverage (покрытие) — сигнал, НЕ цель
496
+
497
+ % кода, который трогают тесты. Полезно как индикатор, но 100% ≠ хорошие тесты (можно «пройти» строку, ничего не проверив). Не гонись за цифрой — лучше
498
+ mutation testing покажет, ловят ли тесты реальные баги.
499
+
500
+ Качества хорошего теста
501
+
502
+ - Быстрый · детерминированный (не флейкает) · изолированный (не зависит от других) · проверяет одно · читаемый · проверяет поведение, не реализацию (→
503
+ переживает рефакторинг).
504
+
505
+ Агент-эра (свод)
506
+
507
+ 1. Приёмочный e2e пишешь ПЕРВЫМ → красный → код → зелёный (видели на слайсе).
508
+ 2. Тесты заказывай списком, не «напиши тесты».
509
+ 3. Приоритет: e2e критических путей → integration → unit для хитрой логики (трофей).
510
+ 4. Тест = арбитр (если про поведение — агент не подкрутит).
511
+
512
+ Подготовка/уборка (fixtures)
513
+
514
+ Перед тестом готовишь данные (свежая тестовая БД), после — чистишь. В pytest это fixture.
515
+
516
+
517
+ Тесты делятся не по одной оси, а по нескольким сразу (один и тот же тест попадает в несколько классификаций). Вот оси:
518
+
519
+ Ось A — по ОХВАТУ (сколько системы трогает) — главная ось
520
+
521
+ 1. Unit — одна функция/класс
522
+ 2. Component — один UI-компонент / модуль
523
+ 3. Integration — несколько частей вместе (service+БД, API+service)
524
+ 4. Contract — совпадает ли форма API у фронта и бэка
525
+ 5. E2E / System — вся система как пользователь
526
+
527
+ Ось B — функциональные («правильно ли делает»)
528
+
529
+ 6. Acceptance (приёмочные, BDD) — бизнес-критерий выполнен
530
+ 7. Smoke — «вообще живо?» сразу после деплоя
531
+ 8. Sanity — быстрая проверка конкретной починки
532
+ 9. Regression — старое не сломалось (гоняешь весь набор)
533
+
534
+ Ось C — нефункциональные («как хорошо», а не «что»)
535
+
536
+ 10. Performance — скорость одного действия
537
+ 11. Load — держит ли N юзеров/RPS
538
+ 12. Stress — где ломается под перегрузом
539
+ 13. Soak — держит ли долго (утечки памяти)
540
+ 14. Spike — резкий всплеск
541
+ 15. Security — DAST (на живом), pen-test (+SAST из статики)
542
+ 16. Accessibility (a11y) · 17. Usability · 18. Compatibility (браузеры) · 19. Reliability/Chaos (выживает при падениях)
543
+
544
+ Ось D — по ТЕХНИКЕ (как устроен сам тест)
545
+
546
+ 20. Example-based — конкретные кейсы (обычные)
547
+ 21. Property-based / fuzz — генерит кучу случайных входов → ловит то, что ты не придумал (наши unknown-unknowns!)
548
+ 22. Mutation — тест для тестов (ловят ли баги)
549
+ 23. Snapshot / golden — сравнение с сохранённым эталоном (твоя сверка ОСВ с 1С = golden-тест!)
550
+ 24. Visual regression — diff скриншотов UI
551
+
552
+ ▎ ⚠️ Ты не пишешь все 24 на каждый проект. Берёшь подмножество по нужде: типичный набор = unit + integration + e2e + (load) + (security). Экзотика (chaos,
553
+ ▎ soak) — только на масштабе.
554
+
555
+
556
+ Да, e2e МЕДЛЕННЫЕ — это ключевой компромисс
557
+
558
+ ┌─────────────┬────────────────────┬────────────────────────────────────────────┐
559
+ │ Тип │ Время ОДНОГО теста │ Почему │
560
+ ├─────────────┼────────────────────┼────────────────────────────────────────────┤
561
+ │ Unit │ миллисекунды │ ничего не запускает, чистая функция │
562
+ ├─────────────┼────────────────────┼────────────────────────────────────────────┤
563
+ │ Component │ мс–десятки мс │ рендер одного компонента │
564
+ ├─────────────┼────────────────────┼────────────────────────────────────────────┤
565
+ │ Integration │ десятки–сотни мс │ реальные вызовы к БД │
566
+ ├─────────────┼────────────────────┼────────────────────────────────────────────┤
567
+ │ E2E │ СЕКУНДЫ │ поднимает браузер, кликает, ждёт отрисовки │
568
+ ├─────────────┼────────────────────┼────────────────────────────────────────────┤
569
+ │ Load │ минуты–часы │ специально гоняешь под нагрузкой │
570
+ └─────────────┴────────────────────┴────────────────────────────────────────────┘
571
+
572
+ 500 e2e × 3 сек = 25 минут. Вот почему пирамида: много быстрых unit, мало медленных e2e (только критические пути).
573
+
574
+ 🔑 Главный ответ: ты НЕ ждёшь полдня — ты СЛОИШЬ прогоны по скорости
575
+
576
+ ┌──────────────────────────┬───────────────────────────────────────┬──────────────────────────────────────────────────────┐
577
+ │ Когда │ Что гоняется │ Сколько ждёшь │
578
+ ├──────────────────────────┼───────────────────────────────────────┼──────────────────────────────────────────────────────┤
579
+ │ на сохранение (редактор) │ unit для текущего файла │ миллисекунды │
580
+ ├──────────────────────────┼───────────────────────────────────────┼──────────────────────────────────────────────────────┤
581
+ │ на коммит (pre-commit) │ быстрые: unit + линтер + типы │ секунды │
582
+ ├──────────────────────────┼───────────────────────────────────────┼──────────────────────────────────────────────────────┤
583
+ │ на PR (CI) │ весь unit + integration + немного e2e │ минуты — но ты НЕ ждёшь, CI крутит сам, ты работаешь │
584
+ ├──────────────────────────┼───────────────────────────────────────┼──────────────────────────────────────────────────────┤
585
+ │ ночью (scheduled) │ полный e2e + load + полный SAST/SCA │ 30+ мин — пока ты спишь │
586
+ └──────────────────────────┴───────────────────────────────────────┴──────────────────────────────────────────────────────┘
587
+
588
+ 🏭 Часть 5, Шаг 3 — CI/CD (конвейер, который запускает всё сам)
589
+
590
+ 25. Что это простыми словами
591
+
592
+ - CI = Continuous Integration = непрерывная интеграция. На каждое изменение кода автоматически прогоняются все проверки (статика Шаг 1 + тесты Шаг 2).
593
+ Цель — поймать поломку сразу, а не через 2 дня.
594
+ - CD = Continuous Delivery/Deployment = непрерывная доставка/развёртывание. Если все проверки зелёные — код сам едет до прода (или до кнопки «выкатить»).
595
+ - Delivery = доведён до состояния «готов выкатить одной кнопкой» (кнопку жмёт человек).
596
+ - Deployment = выкатывается сам, без кнопки.
597
+
598
+ Для тебя на старте: CI обязателен, CD — сначала с кнопкой (человек подтверждает прод).
599
+
600
+ ---
601
+ 2. Зачем — слово практикам (knowledge)
602
+
603
+ Остриков (id40, harness-engineering):
604
+
605
+ ▎ Ты сэкономил 2 дня на кодинге, а потом ждёшь коммент Васи (LGTM завтра вечером), потом тестинг, потом фикс, потом мерж, потом дежурный толкает до прода…
606
+ ▎ и твои 2 дня размазались. AI-First = переложить работу всего IT-завода на автоматику.
607
+
608
+ Валера (id2165, «человек-оркестр»):
609
+
610
+ ▎ Что реально спасло — чёткий пайплайн тестов перед выкаткой, описанный скриптом. Smoke перед каждым пушем, полный прогон перед продом. Деплой не когда
611
+ ▎ модель написала done, а когда зелёный гейт сказал done и я глазами дёрнул метод и увидел ответ.
612
+
613
+ 🔑 Это и есть суть: CI/CD — автоматический внешний арбитр для выкатки. Не слово агента «готово» (= ~80%, помнишь?), а зелёный гейт.
614
+
615
+ ---
616
+ 3. Мысленная модель — конвейер с воротами (гейтами)
617
+
618
+ Пайплайн = цепочка стадий. Каждая стадия — ворота: красное → дальше не пускаем.
619
+
620
+ Те самые «слои прогона» из Шага 2 ложатся прямо на триггеры CI:
621
+
622
+ коммит (локально, pre-commit) → формат + линт + типы + unit файла ~секунды
623
+
624
+ PR открыт (CI-сервер) → весь unit + integration + чуть e2e ~минуты
625
+ + SAST/SCA/secret-scan (ты не ждёшь — CI крутит)
626
+ ↓ (всё зелёное + ревью)
627
+ мерж в main → собрать артефакт → выкатить на staging → smoke
628
+ ↓ (кнопка)
629
+ production
630
+
631
+ ночью (cron) → полный e2e + load + golden (ОСВ vs 1С) ~долго (спишь)
632
+
633
+ ---
634
+ 4. Словарь (8 понятий — без них не разберёшься)
635
+
636
+ ┌───────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
637
+ │ Термин │ Простыми словами │
638
+ ├───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
639
+ │ Триггер │ что запускает пайплайн: push, открытие PR, мерж, tag, расписание (cron) │
640
+ ├───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
641
+ │ Джоба/стадия │ шаг конвейера: job «линт», job «тесты», job «сборка», job «деплой» │
642
+ ├───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
643
+ │ Гейт (quality │ условие «красное → стоп». Автоматический арбитр │
644
+ │ gate) │ │
645
+ ├───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
646
+ │ Артефакт │ что собрали и передаём дальше — Docker-образ. 🔑 Build once, deploy everywhere: собрал один образ → тот же катишь на staging и │
647
+ │ │ на prod (не пересобираешь) │
648
+ ├───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
649
+ │ Среды │ dev → staging (копия прода, но не прод) → production. Артефакт «промоутишь» по средам │
650
+ ├───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
651
+ │ Раннер │ машина, где это исполняется (GitHub Actions runner) │
652
+ ├───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
653
+ │ Кэш │ кэшировать зависимости/слои Docker → пайплайн быстрый (иначе ставит всё заново каждый раз) │
654
+ ├───────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
655
+ │ Branch protection │ нельзя мержить в main без зелёного CI + ревью. Это «забор» вокруг прода │
656
+ └───────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
657
+
658
+ Два принципа скорости:
659
+ - Fail fast — дешёвые проверки первыми. Линт упал → не гоняем дорогие e2e (не жжём время/деньги).
660
+ - Параллелизм — независимые джобы запускаем разом (линт ∥ тесты) → конвейер короче.
661
+
662
+ ---
663
+ 5. CD — как именно выкатывать (стратегии) + откат
664
+
665
+ ┌────────────┬──────────────────────────────────────────────┬────────────────────┬────────────────────────────────┐
666
+ │ Стратегия │ Как │ Даунтайм │ Когда │
667
+ ├────────────┼──────────────────────────────────────────────┼────────────────────┼────────────────────────────────┤
668
+ │ Recreate │ выключил старое → включил новое │ да, секунды-минуты │ старт, один сервер │
669
+ ├────────────┼──────────────────────────────────────────────┼────────────────────┼────────────────────────────────┤
670
+ │ Rolling │ заменяем по чуть-чуть │ нет │ нужен HA (несколько инстансов) │
671
+ ├────────────┼──────────────────────────────────────────────┼────────────────────┼────────────────────────────────┤
672
+ │ Blue-green │ две копии прода, переключил трафик │ нет │ мгновенный откат │
673
+ ├────────────┼──────────────────────────────────────────────┼────────────────────┼────────────────────────────────┤
674
+ │ Canary │ 5% трафика на новое → смотрим метрики → 100% │ нет │ важна осторожность │
675
+ └────────────┴──────────────────────────────────────────────┴────────────────────┴────────────────────────────────┘
676
+
677
+ Практики на одном сервере (как у тебя): Абдуллин (id763) — zero-downtime через systemd socket activation; Остриков (id12716) — pre-stable/stable 5%/95% на
678
+ одной машине. Но это уже продвинутое.
679
+
680
+ Откат (rollback): благодаря build-once у тебя лежит предыдущий Docker-образ → откат = запустить его обратно, секунды. Валера (id2023) идёт дальше —
681
+ snapshot всей VM. Принцип Recovery-first: система должна быстро возвращаться в рабочую точку.
682
+
683
+
684
+ Часть 7 — Наблюдаемость (Observability): что система делает в проде прямо сейчас
685
+
686
+ 1. Что это — простыми словами
687
+
688
+ - Тесты = «правильно ли это ДО деплоя».
689
+ - Наблюдаемость = «что система делает в проде ПОСЛЕ деплоя, прямо сейчас, под реальными юзерами».
690
+
691
+ И сразу важное различие:
692
+
693
+ ┌───────────────┬────────────────────────────────────────────────────┬──────────────────────────────────────────────┐
694
+ │ │ Что мерит │ Ловит │
695
+ ├───────────────┼────────────────────────────────────────────────────┼──────────────────────────────────────────────┤
696
+ │ Мониторинг │ то, что ты заранее решил мерить (CPU, RPS, ошибки) │ known unknowns — «знаю, что может сломаться» │
697
+ ├───────────────┼────────────────────────────────────────────────────┼──────────────────────────────────────────────┤
698
+ │ Наблюдаемость │ можешь задать любой вопрос системе постфактум │ unknown unknowns — то, что не предусмотрел │
699
+ └───────────────┴────────────────────────────────────────────────────┴──────────────────────────────────────────────┘
700
+
701
+ 🔑 Помнишь твою тему «нельзя протестировать то, о чём не подумал»? Наблюдаемость — это тот самый механизм, который ловит unknown unknowns уже в ПРОДЕ.
702
+ Тесты ловят до, наблюдаемость — после.
703
+
704
+ ---
705
+ 2. Три столпа (это база, знать наизусть)
706
+
707
+ ┌─────────┬─────────────────────────────────────────────────┬──────────────────────┬────────────────────────────────────────────────┐
708
+ │ Столп │ Что это │ Отвечает на вопрос │ Пример │
709
+ ├─────────┼─────────────────────────────────────────────────┼──────────────────────┼────────────────────────────────────────────────┤
710
+ │ Логи │ записи событий «что произошло» (строка + время) │ что именно случилось │ user=42 открыл отчёт ОСВ, 340ms, ok │
711
+ ├─────────┼─────────────────────────────────────────────────┼──────────────────────┼────────────────────────────────────────────────┤
712
+ │ Метрики │ числа во времени (агрегаты) │ сколько / как часто │ p95 latency, RPS, error rate, queue depth │
713
+ ├─────────┼─────────────────────────────────────────────────┼──────────────────────┼────────────────────────────────────────────────┤
714
+ │ Трейсы │ путь одного запроса через всю систему │ где ушло время │ API 5ms → service 10ms → БД 320ms → render 5ms │
715
+ └─────────┴─────────────────────────────────────────────────┴──────────────────────┴────────────────────────────────────────────────┘
716
+
717
+ Мнемоника: Логи = что случилось · Метрики = сколько/тренд · Трейсы = где затык.
718
+
719
+ 🔑 Структурные логи (JSON), а не текст — чтобы можно было искать/фильтровать (level=error AND user=42), а не глазами читать простыню.
720
+
721
+ Рядом:
722
+ - События (events) — бизнес-факты: «оплата прошла», «отчёт сгенерирован» (иногда 4-й столп).
723
+ - Алерты — метрика пересекла порог → звонок. ⚠️ Только криты (Остриков id25: «пишем только то, из-за чего собираем звонок в любое время дня и ночи»).
724
+ - Дашборды — визуализация метрик (Grafana).
725
+ - SLI/SLO/SLA — Indicator (что мерим, напр. % успешных запросов) → Objective (цель: 99.5%) → Agreement (договор с клиентом + штраф). Разница между целью и
726
+ реальностью = error budget (бюджет на ошибки).
727
+
728
+ ---
729
+ 3. Золотая нить — correlation_id (как связать три столпа)
730
+
731
+ Сам по себе каждый столп — половина картины. Связывает их сквозной ID запроса (trace_id / correlation_id), который проходит через все сервисы.
732
+
733
+ Тогда работает магия расследования:
734
+ метрика «p99 подскочил» → проваливаешься в конкретные ТРЕЙСЫ этих запросов
735
+ → видишь span БД 320ms → открываешь ЛОГИ с тем же trace_id → видишь, какой SQL
736
+ Без сквозного ID ты не свяжешь «что» + «сколько» + «где». Это первое, что просишь у агента заложить.
737
+
738
+ ---
739
+ 4. Три вопроса, на которые отвечает наблюдаемость
740
+
741
+ 5. Что сломано? → алерт / метрика
742
+ 6. Почему? → трейс (где) + лог (что именно)
743
+ 7. Кого затронуло и насколько? → метрики + события
744
+
745
+ ---
746
+ 8. Слово практикам (это ИХ любимая тема — в сигнале её больше всего)
747
+
748
+ Валера (id1974): «самое важное — чтобы у агента были логи… не видел, чтобы хоть один блогер настроил логи и трейсинг для своего агента.»
749
+
750
+ Валера (id2165): «читать логи всех сторон системы (vllm, litellm, гейт, вебхук) — там вся соль, а не в "агент сказал готово". Единственная фича, которая
751
+ ни разу не подвела — дисциплина смотреть в логи каждый раз.»
752
+
753
+ Абдуллин (id812, AI Ops): node_exporter → VictoriaMetrics (метрики) + ClickHouse (структурные логи) → Grafana (дашборды) + Prometheus-счётчики в рантайм.
754
+
755
+ Абдуллин (id73217): «логи в структурированном формате, даём агенту RO-доступ + описываем что где лежит + примеры типичных запросов + скрипты для отладки →
756
+ агент сам расследует инциденты. Все кластера так обслуживаются.»
757
+
758
+ Mike Shevchenko (id58777): «LLM выдаёт текст — непонятно, как пришёл. Навигатор показывает карту — видишь маршрут.» Наблюдаемость = карта для агента
759
+ (закрывает feedback loop).
760
+
761
+ ---
762
+ 9. 🤖 Главное для вайбкодера: «agent-legible app» (приложение, читаемое агентом)
763
+
764
+ Это ключ из OpenAI harness + Абдуллина. Если тесты — это feedback loop до деплоя, то наблюдаемость — это feedback loop агента в ПРОДЕ.
765
+
766
+ Чтобы агент сам расследовал и чинил, дай ему:
767
+ - структурные логи (JSON, не текст);
768
+ - RO-доступ к телеметрии (логи/метрики/трейсы);
769
+ - doc «что где лежит» + примеры типичных запросов + скрипты отладки;
770
+ - иногда agent-режим приложения (Абдуллин id755): логов меньше, любая ошибка роняет приложение целиком — чтобы агенту было чисто себя проверять.
771
+
772
+ Тогда можно дать задание уровня (Остриков id40): «проверь все варианты создания аккаунта, чтобы в трейсах не было спанов больше 900 мс» — и агент сам
773
+ пройдёт, посмотрит трейсы, найдёт затык.
774
+
775
+ ⚠️ Анти-паттерн (Абдуллин id812): high-cardinality метрика (напр. метрика с user_id в лейбле) может уронить весь Prometheus. Осторожно с тем, что кладёшь
776
+ в лейблы.
777
+
778
+ ---
779
+ 7. Инструменты
780
+
781
+ - Стандарт инструментирования: 🔑 OpenTelemetry (OTel) — vendor-neutral, один SDK для логов+метрик+трейсов. Закладывай его, не привязывайся к вендору.
782
+ - Логи: структурный JSON → ClickHouse / Loki / ELK.
783
+ - Метрики: Prometheus / VictoriaMetrics → Grafana.
784
+ - Трейсы: OpenTelemetry → Jaeger / Tempo.
785
+ - Ошибки: Sentry (стектрейсы + группировка).
786
+ - Для LLM-под-капотом: Langfuse (трассировка цепочек агента) — у тебя на аудит-сайте пока не нужно, но запомни.
787
+
788
+
789
+ 4-й столп — Continuous Profiling (Datadog, и OTel уже завёл сигнал для него): логи/метрики/трейсы показывают «какой сервис тормозит», а профайлинг —
790
+ «какая функция/строка кода ест CPU/RAM в проде». Это последний уровень детализации.
791
+ 2. Фронт тоже наблюдаем (у тебя React!):
792
+ - RUM (Real User Monitoring) — телеметрия из браузеров реальных юзеров: время загрузки, Core Web Vitals, JS-ошибки.
793
+ - Synthetic monitoring — бот по расписанию дёргает эндпоинты (ловит проблему до юзеров). Это твой smoke, живущий в проде.
794
+ 3. SRE-дисциплина вокруг алертов (я сказал только «алерты на криты»):
795
+ - Burn-rate алерты — на скорость сжигания error budget, а не голый порог → меньше шума.
796
+ - Error budget policy — бюджет сожгли → фриз фич, чиним стабильность.
797
+ - Runbook — короткая инструкция «что делать по этому алерту», привязана к нему, обновляется после инцидента.
798
+ - Blameless post-mortem — после инцидента ищем корень, делаем систему лучше (твоя антихрупкость, Остриков id13454: «каждая ошибка должна улучшать
799
+ систему»).
800
+ 4. Экономика телеметрии (чтобы не разориться и не утонуть):
801
+ - Tail-based sampling — храним все error-трейсы + % успешных.
802
+ - Логи на info в проде, debug только при расследовании (один сервис на debug = больше данных, чем весь флот).
803
+ 5. AI-наблюдаемость (на будущее, когда добавишь LLM-под-капотом): трейс = весь путь решения агента (tool calls, retrieved docs, параметры); trace-to-eval
804
+ loop — прод-фейл становится eval-кейсом → гейт в CI (ровно 9 принципов Рефата). Инструменты: Langfuse (OSS) / LangSmith / Phoenix-Arize
805
+ (OTel-совместимый).
806
+
807
+
808
+ ⚙️ Часть 6 — DevOps (как код доезжает до прода и там живёт)
809
+
810
+ 1. Что это — простыми словами
811
+
812
+ Dev (пишет код) + Ops (эксплуатирует на проде) — раньше две команды, кидали через стену: «у меня работает» → «а на проде падает». DevOps = одна
813
+ ответственность за весь цикл «от кода до прода и эксплуатации».
814
+
815
+ 🔑 Девиз: «You build it, you run it» — кто построил, тот и отвечает за то, как оно живёт. Это культура/процесс, а не должность.
816
+
817
+ Практик-якоря:
818
+ - Валера (id2165): его база — devops/сети/linux/docker/k8s за 6 лет; без неё агент = «джун с уверенным тоном». [roadmap.sh/devops]
819
+ - Абдуллин (id769): «теперь можно частично снять DevOps-нагрузку (агент настроит)… но контролировать — не отменяет».
820
+ - Абдуллин (id812, AI Ops): Codex настроил телеметрию за него, но «не заменяет SRE — уроню стек, моя вина» (= P8).
821
+
822
+ ---
823
+ 2. Местность: что где крутится
824
+
825
+ ┌─────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────┬───────────────┐
826
+ │ Компонент │ Роль │ stateful? │
827
+ ├─────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────┼───────────────┤
828
+ │ Reverse-proxy (Caddy/nginx) │ единая точка входа: HTTPS снаружи + авто-TLS (Caddy) + маршрут на app + статика │ нет │
829
+ ├─────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────┼───────────────┤
830
+ │ App (FastAPI) │ твоя логика │ 🔑 stateless! │
831
+ ├─────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────┼───────────────┤
832
+ │ БД (Postgres) │ здесь живёт состояние │ да │
833
+ ├─────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────┼───────────────┤
834
+ │ Очередь+воркеры (Celery) │ фоновые задачи (синк из 1С) │ нет │
835
+ ├─────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────┼───────────────┤
836
+ │ Кэш (Redis) │ сессии, кэш отчётов │ (полу) │
837
+ └─────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────┴───────────────┘
838
+
839
+ 🔑 Stateless-приложение = app не хранит состояние в себе (ни сессий, ни файлов) — всё в БД/Redis. Тогда его можно перезапускать и размножать без потерь.
840
+ Это фундамент всей эксплуатации. Всё это — в Docker (один образ → воспроизводимо везде).
841
+
842
+ ---
843
+ 3. 12-Factor App — канон «приложения, удобного для эксплуатации»
844
+
845
+ Стандартный чек-лист (ключевое для тебя):
846
+ - Конфиг — в env-переменных, не в коде (один образ → разные среды через env).
847
+ - Stateless-процессы (см. выше).
848
+ - Логи — в stdout (а не в файл; их забирает система логов — Часть 7).
849
+ - Dev/prod parity — dev, staging, prod одинаковые, разница только в конфиге.
850
+ - Зависимости явные (lock-файл), сборка ≠ запуск ≠ конфиг.
851
+
852
+ ---
853
+ 4. 📌 Топология — твой реальный вопрос «1С на одном серваке, сайт на другом»
854
+
855
+ [Сервер 1С] ──читаем──> [Celery sync] ──пишет──> [твоя Postgres = витрина]
856
+ (источник, │
857
+ НЕ трогаем) ▼
858
+ [App-сервер: Caddy + FastAPI + React + Celery + Redis + Postgres]
859
+
860
+ приватная сеть/VPN (Tailscale)
861
+ - 1С-сервер — источник, его только читаем (твоё «копируем БД 1С»).
862
+ - Sync (Celery) тянет данные 1С → твоя Postgres-витрина (отсюда быстрые аналитические запросы — ты latency-bound).
863
+ - App-сервер на старте — один сервер, Docker Compose (one-server-first из тех-дизайна).
864
+ - Сеть между серверами — приватная/VPN (Tailscale — им пользуются Абдуллин/Валера), наружу не торчит.
865
+ - Разносишь на больше серверов только когда упрёшься (не раньше).
866
+
867
+ ---
868
+ 5. Эксплуатация на одном сервере (Ops)
869
+
870
+ - Health-checks (liveness «жив?» / readiness «готов принимать?») — proxy/оркестратор не шлёт трафик на мёртвый контейнер.
871
+ - Restart policy (restart: always в Docker) — упал → поднялся сам.
872
+ - Лимиты CPU/RAM на контейнеры — чтобы один (напр. тяжёлый отчёт) не сожрал весь сервер.
873
+ - Нулевой даунтайм на одном сервере: systemd socket activation (Абдуллин id763) ИЛИ blue-green через два compose + переключение Caddy ИЛИ просто recreate
874
+ + маленькое окно (на старте — норм).
875
+
876
+ ---
877
+ 6. 🛟 Бэкапы + Disaster Recovery (для аудита — критично)
878
+
879
+ - Бэкап БД регулярно + ПРОВЕРЕННОЕ восстановление. 🔑 Бэкап, который ни разу не разворачивали = нет бэкапа.
880
+ - Правило 3-2-1: 3 копии · 2 носителя · 1 offsite (вне сервера).
881
+ - RPO (сколько данных не жалко потерять → как часто бэкапим) / RTO (как быстро поднимаемся).
882
+ - Recovery-first (Пух id15542) + снапшоты (Валера id2023: snapshot всей VM → откат за секунды). Защита от ransomware (Часть 8).
883
+
884
+ ---
885
+ 7. 🔗 Связка ВСЕГО воедино (сквозной маршрут — это замыкает конституцию)
886
+
887
+ dev-машина (пишешь с агентом)
888
+ │ git push
889
+
890
+ CI/CD (Часть 5 Шаг 3): статика → тесты → build → sign → артефакт(Docker)
891
+ │ deploy
892
+
893
+ Сервер: Caddy(TLS) → App(stateless) → Postgres/Redis/Celery
894
+
895
+
896
+ Наблюдаемость (Часть 7): логи/метрики/трейсы
897
+ │ инцидент → алерт
898
+
899
+ Агент расследует по логам (agent-legible) → фикс → снова цикл
900
+ ↑________________ Безопасность (Часть 8) — сквозь весь маршрут _____________↑
901
+
902
+ ---
903
+ 8. 🤖 DevOps в агент-эру (твой режим)
904
+
905
+ - Агент настраивает сервер/сеть/телеметрию (Абдуллин id812: node_exporter→Prometheus→Grafana; Валера id2168: дал Клоду доступ к MikroTik, «прибрались»;
906
+ id769: Caddy+TLS+wildcard за минуты).
907
+ - 🔑 Декларативность = безопасный откат для агента: NixOS/Docker Compose/IaC в git → агент может менять инфру, откатишь rebuild’ом/git revert (Абдуллин
908
+ id763). Снапшот VM = тот же recovery-first.
909
+ - Но (P8): человек владеет. «Codex не заменяет SRE». Все изменения инфры смотришь глазами до применения (Абдуллин: NixOS позволяет пробежать диффы до
910
+ выката).
911
+
912
+ ---
913
+ 9. 📌 Под аудит-сайт (чек-лист DevOps)
914
+
915
+ - Caddy (авто-TLS) → FastAPI (stateless) + React-статика · Celery+Redis · Postgres — в Docker Compose, один сервер
916
+ - 1С читаем по приватной сети (Tailscale), sync’им в Postgres-витрину
917
+ - Конфиг в env, секреты отдельно (Часть 8), dev/staging/prod одинаковы
918
+ - Health-checks + restart: always + лимиты ресурсов
919
+ - 🛟 Бэкап Postgres ежедневно + тест восстановления + offsite + снапшот сервера
920
+ - Нулевой даунтайм — позже (старт: recreate + окно)
921
+ - Вся инфра = код в git → агент меняет, ты ревьюишь диффы