«Составить самим ТЗ, 5 раз проверить, потом делать. Всё новое не портит старое — всё дополняет. Правим старое — не мешая новому.»
Архитектура из 8+ блоков с тысячами связей. Любое изменение может сломать то что работает. Без дисциплины — хаос за 2 месяца.
С дисциплиной 5-pass + forward-only:
Каждое ТЗ проходит 5 разных проверок. Между passes — пауза (минимум 1 час, лучше ночь), чтобы увидеть свежим глазом.
Все ли поля / связи / edge-кейсы описаны?
Как блок взаимодействует с другими?
Не ломает ли существующее?
RLS / валидация / audit
Выдержит ли ×10 нагрузку?
| Операция | Почему |
|---|---|
| ALTER TABLE ... DROP COLUMN | Старые запросы упадут |
| DROP TABLE старой | Старые ссылки на неё сломаются |
| Переименовать поле | Все SELECT * и старый код упадёт |
| Изменить тип поля | Данные потеряются / сломаются индексы |
| Удалить API endpoint | Клиенты / скрипты сломаются |
| Удалить событие | Подписчики не получат уведомление |
| ALTER COLUMN NOT NULL | Старые INSERT без этого поля упадут |
| Операция | Почему безопасно |
|---|---|
| ALTER TABLE ... ADD COLUMN ... DEFAULT NULL | Старые запросы не знают о поле, продолжают работать |
| CREATE TABLE новой | Не влияет на существующие |
| CREATE VIEW v_dashboard_v2 | Старая view v_dashboard_v1 остаётся |
| CREATE INDEX CONCURRENTLY | Без блокировки таблицы |
| Deprecate endpoint → /api/v2 | /api/v1 ещё работает год |
| Event: добавить опциональное поле | Подписчики просто игнорируют |
-- Помечаем но не удаляем COMMENT ON COLUMN urls.old_field IS 'DEPRECATED since v1.5, use new_field'; -- Работает 3-6 месяцев, потом удаляем
Не разом 28 000 записей, а кусками:
UPDATE urls SET new_field = COMPUTE(old_field) WHERE id IN (SELECT id FROM urls WHERE new_field IS NULL LIMIT 1000); -- Запускаем в cron каждые 10 минут пока не обработано всё
Новая логика пишет в новое место, старая продолжает работать. Сравниваем результаты N дней → переключаем.
-- Пишем в оба места BEGIN; INSERT INTO urls_old (...) VALUES (...); INSERT INTO urls_new (...) VALUES (...); COMMIT; -- Читаем из старого, но в фоне проверяем что новое совпадает -- Через 7 дней без расхождений — переключаем чтение на новое
Каждая миграция имеет обратную. Не имеет — не мерджим.
-- forward.sql ALTER TABLE urls ADD COLUMN new_field text; -- rollback.sql ALTER TABLE urls DROP COLUMN new_field;
# ТЗ: Блок {NAME} v{X.Y.Z}
## 1. Цель и место в архитектуре
## 2. Контракт (exports/imports/events/health)
## 3. Схема БД (таблицы, индексы, FK, RLS)
## 4. Миграции (forward + rollback)
## 5. Взаимодействия с другими блоками
## 6. Graceful degradation
## 7. Тесты (unit/integration/contract/health)
## 8. Dashboards / Views
## 9. Операции (cron, handlers)
## 10. Безопасность (RLS, audit, sensitive)
---
## 5-Pass Review
- [ ] Pass 1 — Полнота · дата · замечания · статус
- [ ] Pass 2 — Связи · дата · замечания · статус
- [ ] Pass 3 — Backward compat · дата · замечания · статус
- [ ] Pass 4 — Безопасность · дата · замечания · статус
- [ ] Pass 5 — Масштабируемость · дата · замечания · статус
**Итого:** готово к реализации / требует доработки
---
## Rollback план
## Canary план
## Метрики успеха
Сначала фундаментальные, потом наслаивающиеся:
| # | Блок | Зависимости | Статус ТЗ |
|---|---|---|---|
| 1 | CORE | — | ⏳ Pending |
| 2 | HOTELS | — | ⏳ Pending |
| 3 | CONTENT | CORE | ⏳ Pending |
| 4 | ANALYTICS | CORE | ⏳ Pending |
| 5 | SEO | CORE | ⏳ Pending |
| 6 | NOTIFICATIONS | CORE | ⏳ Pending |
| 7 | LEADS | CORE, HOTELS | ⏳ Pending |
| 8 | ADMIN | — | ⏳ Pending |
| 9 | AI ANALYST (future) | все 8 | 💭 Concept |
Обновлено 24.04.2026 · Правила для всего портфеля