верхняя ступень ревью задана тестом, а не списком
«Идентичность, слияние, разбор» пришли из одного проекта, и в общем виде формулировка не читалась: вопрос «как применить это к моему проекту» не имел ответа в тексте. Теперь класс задан тремя условиями, не зависящими ни от домена, ни от языка: вариантов несколько и оба защитимы; спека между ними не выбирает; неверный выбор не падает, а даёт правдоподобный результат и молча меняет смысл данных. Отрицательный тест сильнее трёх положительных: то, что красит гейт, роняет запрос или ломает тест, в класс не входит — это ловят проходы дешевле. Отсюда же и причина, по которой класс достался самому дорогому проходу: независимая реализация выберет другой вариант, и дифф между вариантами и есть находка; там, где вариант один, она совпадёт с существующей. Три слова остались как три места, где такие правила водятся — граница, где данные входят или встречаются: состав ключа и нормализация перед сравнением; победитель конфликта и тай-брейк при равенстве; границы токенов и неоднозначный вход. Проект перечисляет свои места в docs/review.md, и перечень производен от теста, а не заменяет его. Две оговорки, без которых правило вырождается: - триггер — новое или изменённое по существу правило, а не код рядом с ним; иначе проект, чей домен и состоит из таких правил, всегда в deep; - проект, где такого класса нет вовсе, deep не запускает никогда, и это законное состояние, а не недонастройка. review-reimpl получил тот же тест и право сказать первой строкой, что позвали не на его класс, — строкой в границы покрытия, а не отказом работать. DECISIONS 18, XXX и следствия 76–77. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1310,6 +1310,22 @@ SSS: рубрика на узел без нового понятия порож
|
||||
ступени, делает ревью **дороже**: тот же объём тем же составом, но костяк оплачен
|
||||
дважды. Резать — когда разрез снимает дорогой проход с большей части диффа.
|
||||
|
||||
**XXX. Верхняя ступень задана тестом, а не списком.** «Идентичность, слияние,
|
||||
разбор» — формулировка, пришедшая из одного проекта, и в общем виде она не
|
||||
читалась: вопрос «как это применить к моему проекту» не имел ответа в тексте.
|
||||
Теперь класс задан тремя условиями, независимыми от домена и языка: вариантов
|
||||
несколько и оба защитимы; спека между ними не выбирает; неверный выбор не падает,
|
||||
а молча меняет смысл данных. Отрицательный тест сильнее положительных — то, что
|
||||
красит гейт или роняет запрос, в класс не входит. Три слова остались как **три
|
||||
места**, где такие правила водятся (граница входа данных и место их встречи), а
|
||||
проект перечисляет свои места в `docs/review.md` — перечень производен от теста и
|
||||
не расширяет класс.
|
||||
|
||||
Оговорка, без которой правило вырождается: триггер — **новое или изменённое по
|
||||
существу правило**, а не код рядом с ним. Проект, чей домен и состоит из таких
|
||||
правил, иначе оказывался бы в `deep` всегда — та же болезнь, от которой лечилась
|
||||
ступень `wide`.
|
||||
|
||||
### Что из этого следует
|
||||
|
||||
72. **Порога в числе границ не заводится.** Тот же принцип, что в теме 16 (CCC):
|
||||
@@ -1323,3 +1339,12 @@ SSS: рубрика на узел без нового понятия порож
|
||||
75. **Замер остаётся за обкаткой.** Правило выведено из состава проходов, а не из
|
||||
статистики прогонов: считать, какая доля задач попадает в каждую ступень,
|
||||
можно только на спринтах нового процесса (TODO шаг 4).
|
||||
76. **Отсутствие верхней ступени — законное состояние проекта.** Бывают проекты,
|
||||
где данные приходят нормализованными, ничего ни с чем не сливается, а внешних
|
||||
форматов нет: `deep` там не срабатывает никогда, и придумывать ему повод не
|
||||
надо. Раньше это читалось как недонастройка.
|
||||
77. **Ступень определяет класс правила, а не вид работы.** Миграция схемы —
|
||||
`standard`, но миграция, переносящая данные по правилу («сложить дубли»,
|
||||
«привести к одному виду перед сравнением»), несёт правило идентичности и
|
||||
потому `deep`. Одно слово в описании задачи попадает в разные ступени — это не
|
||||
противоречие, смотрят не на слово.
|
||||
|
||||
@@ -38,11 +38,32 @@ color: yellow
|
||||
твоя версия проще, потому что не знает, чего проект боится.
|
||||
|
||||
**Тебя запускают только в верхнем профиле, `deep`, а не всегда.** Он выбирается
|
||||
ровно тогда, когда изменение вводит **новое правило идентичности, слияния или
|
||||
разбора** (проектная формулировка — в разделе
|
||||
`docs/review.md`, если он там записан); ты — единственное, чем `deep` отличается
|
||||
от соседней ступени `wide`. Вне этого случая твой счёт — самый большой в
|
||||
конвейере (он
|
||||
ровно тогда, когда вводится или меняется по существу **правило идентичности,
|
||||
слияния или разбора**; ты — единственное, чем `deep` отличается от соседней
|
||||
ступени `wide`.
|
||||
|
||||
Класс задан тестом, а не списком, и тест не зависит ни от домена, ни от языка.
|
||||
Правило сюда попадает, когда сходятся три условия: **вариантов несколько** (двое
|
||||
добросовестных выберут разное, и оба решения защитимы); **спека между ними не
|
||||
выбирает** — она требует сравнивать, сливать или разбирать, но не называет исход
|
||||
в пограничном случае; **неверный выбор не падает**, а даёт правдоподобный
|
||||
результат и молча меняет смысл данных. Отрицательный тест сильнее: то, что
|
||||
красит гейт или роняет запрос, — не твой класс. Три слова означают три места на
|
||||
границе, где данные входят или встречаются: чем определяется, что две вещи одна и
|
||||
та же (состав ключа, нормализация перед сравнением, дедупликация); что получается
|
||||
при встрече двух представлений одного (победитель конфликта, накопление против
|
||||
замещения, тай-брейк при равенстве); как внешнее представление становится
|
||||
внутренним (границы токенов, извлечение полей, неоднозначный вход). Проектный
|
||||
перечень мест — в `docs/review.md`, если он там записан; он производен от теста,
|
||||
а не расширяет его.
|
||||
|
||||
**Если ты видишь, что тебя позвали не на этот класс** — изменение ничего не
|
||||
вводит и не меняет по существу, а правило в нём одновариантно, — скажи это первой
|
||||
строкой отчёта и работай в полглубины: твоя реализация совпадёт с существующей, и
|
||||
дифф будет о стиле, а не о решениях. Это строка в границы покрытия, а не отказ
|
||||
работать.
|
||||
|
||||
Вне этого случая твой счёт — самый большой в конвейере (он
|
||||
определяется объёмом вывода: ты пишешь реализацию целиком), а независимый взгляд
|
||||
в значительной мере уже дал профиль `design` — код писался под его находки. Если
|
||||
тебя позвали, значит случай тот самый: работай в полную глубину и не экономь на
|
||||
|
||||
@@ -177,8 +177,8 @@ charter'а, а модель потом двигает калибровка, и
|
||||
|
||||
Правило выбора профиля — **по факту изменения, не по ощущению важности**:
|
||||
|
||||
- трогается правило, определяющее **идентичность, слияние или разбор** данных →
|
||||
`deep`;
|
||||
- вводится или меняется по существу правило, определяющее **идентичность, слияние
|
||||
или разбор** данных (тест — ниже) → `deep`;
|
||||
- иначе изменение вводит **новое понятие или структурную единицу**: новый пакет
|
||||
или слой, новая точка входа, второй способ делать то, что уже делается, перенос
|
||||
ответственности между узлами → `wide`;
|
||||
@@ -215,6 +215,63 @@ charter'а, а модель потом двигает калибровка, и
|
||||
правда архитектурное (публичный SDK, чужие потребители), там же поднимает его до
|
||||
`wide` — и это уточнение, а не возврат прежнего умолчания.
|
||||
|
||||
### Идентичность, слияние, разбор — тест, а не список
|
||||
|
||||
Три слова названы затем, чтобы верхнюю ступень нельзя было выбрать по ощущению.
|
||||
Читаются они **тестом**, применимым к любому проекту на любом языке; домен, стек
|
||||
и имена узлов в тест не входят.
|
||||
|
||||
Правило принадлежит этому классу, если сходятся **три условия**:
|
||||
|
||||
1. **вариантов несколько** — два добросовестных исполнителя выберут разное, и оба
|
||||
решения защитимы;
|
||||
2. **спека между ними не выбирает** — она требует, чтобы вещи сравнивались,
|
||||
сливались или разбирались, но не называет исход в пограничном случае;
|
||||
3. **неверный выбор не падает** — он даёт правдоподобный результат и меняет смысл
|
||||
данных молча.
|
||||
|
||||
**Отрицательный тест, и он важнее трёх положительных:** если неверная реализация
|
||||
красит гейт, роняет запрос или ломает тест — это **не** сюда. Такое ловят проходы
|
||||
дешевле, и платить за него верхней ступенью не за что.
|
||||
|
||||
Отсюда же и причина, по которой класс достался самому дорогому проходу:
|
||||
независимая реализация **выберет другой вариант**, и дифф между двумя вариантами
|
||||
и есть находка. Там, где вариант один, она совпадёт с существующей — и верхняя
|
||||
ступень оплатит подтверждение того, что и так известно.
|
||||
|
||||
Три слова — это **три места**, где такие правила водятся, и все три стоят на
|
||||
границе, где данные входят или встречаются:
|
||||
|
||||
| Слово | Вопрос, на который правило отвечает | Что в нём выбирается |
|
||||
|---|---|---|
|
||||
| **идентичность** | когда две вещи считаются одной и той же | состав ключа и что в него намеренно не входит; нормализация перед сравнением — регистр, пробелы, кодировка, время, единицы, округление; дедупликация |
|
||||
| **слияние** | что получается, когда два представления одного встретились | кто побеждает при конфликте; накопительное против замещающего; что делать с отсутствующим полем; тай-брейк при равенстве |
|
||||
| **разбор** | как внешнее представление становится внутренним | границы токенов; извлечение полей; сопоставление с известным набором; поведение на неоднозначном входе |
|
||||
|
||||
**Триггер — новое или изменённое правило, а не код рядом с ним.** Правка
|
||||
сообщения об ошибке в узле, который разбирает вход, ступень не поднимает.
|
||||
Поднимают: заводится ключ или меняется его состав; в слияние добавляется источник
|
||||
или меняется победитель при конфликте; у разбора появляется новый вид входа или
|
||||
новая ветка неоднозначности. Без этой оговорки проект, чей домен и **состоит** из
|
||||
таких правил, оказывался бы в `deep` всегда — та же болезнь, от которой лечилась
|
||||
ступень `wide`.
|
||||
|
||||
**Ступень определяет класс правила, а не вид работы.** Миграция схемы сама по
|
||||
себе `standard` — но миграция, которая **переносит данные** по правилу («сложить
|
||||
дубли», «привести к одному виду перед сравнением»), несёт правило идентичности и
|
||||
потому `deep`. Одно и то же слово в описании задачи попадает в разные ступени, и
|
||||
это не противоречие: смотрят не на слово, а на то, есть ли выбор, которого спека
|
||||
не сделала.
|
||||
|
||||
**Проект, у которого таких правил нет вовсе, `deep` не запускает никогда.** Это
|
||||
законное состояние, а не признак недонастройки: бывают проекты, где данные
|
||||
приходят уже нормализованными, ничего ни с чем не сливается, а внешних форматов
|
||||
нет. Верхняя ступень там просто не срабатывает, и придумывать ей повод не надо.
|
||||
|
||||
Свои места проект перечисляет в `docs/review.md`, подраздел «Триггеры профиля» —
|
||||
поимённо, узлами или capability. Перечень **производен от теста**: он не расширяет
|
||||
класс, а называет, где этот класс живёт именно здесь.
|
||||
|
||||
### Профиль — максимум по поверхности, и отсюда размер задачи
|
||||
|
||||
Условия читаются сверху вниз, и **первое подошедшее отвечает за весь дифф**.
|
||||
@@ -458,14 +515,20 @@ Recall обоих равен длине их источника — это и е
|
||||
|
||||
- `review-reimpl` — пишет свою реализацию, не открывая существующую, затем
|
||||
диффит по решениям. **Профиль и есть его условие:** `deep` выбирается ровно
|
||||
тогда, когда изменение вводит новое правило идентичности, слияния или разбора
|
||||
(проектная формулировка — в `docs/review.md`, если записана). Это самый дорогой
|
||||
проход конвейера (его счёт определяется объёмом вывода — он пишет реализацию
|
||||
целиком), а вне этого случая независимый взгляд в значительной мере уже дал
|
||||
профиль `design`: код писался под его находки. Условие выбрано по факту:
|
||||
тогда, когда вводится или меняется по существу правило идентичности, слияния
|
||||
или разбора — по тесту из раздела «Идентичность, слияние, разбор»; проектный
|
||||
перечень мест, где такие правила живут, — в `docs/review.md`, если записан. Это
|
||||
самый дорогой проход конвейера (его счёт определяется объёмом вывода — он пишет
|
||||
реализацию целиком), а вне этого случая независимый взгляд в значительной мере
|
||||
уже дал профиль `design`: код писался под его находки. Условие выбрано по факту:
|
||||
единственный раз, когда триаж назвал отсутствие `reimpl` дырой покрытия, — это
|
||||
была задача с новым правилом слияния сущностей.
|
||||
|
||||
Такие правила обычно занимают десятки строк, но определяют смысл **всех** данных
|
||||
проекта. Отсюда особенность верхней ступени, из-за которой её легко выбрать
|
||||
неверно: самый дорогой проход тратится на самый **маленький** дифф. `deep` не про
|
||||
размер изменения и не про его опасность — он про класс правила.
|
||||
|
||||
Раньше это условие стояло **внутри** профиля, и `deep` означал то семь проходов,
|
||||
то восемь. Реестр состава, который «проверяется взглядом», проверять было нечем:
|
||||
у профиля не было одного правильного ответа. Теперь ступеней две — `wide` и
|
||||
|
||||
@@ -184,10 +184,11 @@ kebab-case.
|
||||
(<провенанс>)`;
|
||||
- **Триггеры профиля** — проектная конкретизация правила выбора профиля ревью:
|
||||
что в этом проекте считается **новым понятием или структурной единицей** (это
|
||||
поднимает прогон до `wide`) и что — правилом идентичности, слияния или разбора
|
||||
(до `deep`). Уточняет умолчания конвейера, а не отменяет их. Рабочее умолчание
|
||||
— `standard`: миграция схемы и публичный контракт ступень **не** поднимают,
|
||||
их проверяют проходы, которые в `standard` и так есть;
|
||||
поднимает прогон до `wide`) и **где живут правила идентичности, слияния и
|
||||
разбора** (до `deep`) — перечнем мест, производным от теста конвейера, а не
|
||||
вторым определением класса. Уточняет умолчания, а не отменяет их. Рабочее
|
||||
умолчание — `standard`: миграция схемы и публичный контракт ступень **не**
|
||||
поднимают, их проверяют проходы, которые в `standard` и так есть;
|
||||
- **Недоступно проверке** — два подраздела: «не проверит ни один проход»
|
||||
(принципиальная граница, по факту промаха не пересматривается) и «перестали
|
||||
проверять сознательно» (пересматривается первым).
|
||||
|
||||
@@ -284,9 +284,17 @@
|
||||
|
||||
Проектная конкретизация правила выбора профиля: что здесь считается **новым
|
||||
понятием или структурной единицей** (поднимает прогон до `wide` и запускает
|
||||
архитектурный проход) и что — правилом идентичности, слияния или разбора
|
||||
(до `deep`, запускает независимую реализацию). Уточняет умолчания конвейера, не
|
||||
отменяет их; рабочее умолчание — `standard`.
|
||||
архитектурный проход) и **где живут правила идентичности, слияния и разбора**
|
||||
(до `deep`, запускает независимую реализацию) — перечнем узлов или capability,
|
||||
поимённо. Уточняет умолчания конвейера, не отменяет их; рабочее умолчание —
|
||||
`standard`.
|
||||
|
||||
Перечень для `deep` **производен от теста конвейера**, а не заменяет его:
|
||||
правило попадает в класс, когда вариантов несколько, спека между ними не
|
||||
выбирает, а неверный выбор не падает, а молча меняет смысл данных. Перечисляй
|
||||
места, где этот класс здесь живёт, а не переписывай определение. Таких мест нет
|
||||
вовсе — так и напиши: `deep` тогда не запускается никогда, и это законное
|
||||
состояние.
|
||||
|
||||
### Недоступно проверке
|
||||
|
||||
|
||||
Reference in New Issue
Block a user