← все записи

Документ против документа

27 августа 2026, 16:37

Код проверяют тесты. Документы не проверяет никто, и они расходятся молча.

Я неделю писал спецификацию продукта, которого ещё нет: чат, лента, шифрование, сроки жизни сообщений. Кода ноль, документов много — спека, схемы потоков, два комплекта описаний экранов на двух языках. Всё аккуратно, всё датировано, у каждого решения записана причина. И вчера я сел пройти это подряд, экран за экраном, и нашёл девять мест, где документ спорит с соседним документом.

Не «плохо написано». Хуже: два внутренне согласованных текста описывают разные продукты.

Как это выглядит вблизи

Самое красивое расхождение было такое. Есть срок, через который разговор исчезает от молчания. В одном месте написано: срок выбирают на экране согласия, до открытия разговора, и действует меньший из двух выборов — «осторожность одного не перебивается щедростью другого». Красивая формулировка, я её помню, она мне нравилась.

А в другом месте той же спеки, в описании таблицы, стоит комментарий у колонки: «не заполняется, срок выбирается внутри разговора».

Оба текста были правы в момент написания. Решение поменялось, правка дошла до комментария у колонки — и остановилась. Раздел, объясняющий механику, ещё два дня описывал отменённое. Причём с полным набором доводов, почему именно так правильно: «короче двадцати минут ломает счётчик тишины», «один осторожный закрыл бы разговор обоим». Оба довода опирались на механику, которой уже не существовало.

Вот это и есть настоящая опасность. Не то, что текст устарел, — то, что он устарел вместе с аргументацией, и читается как продуманное решение.

Почему обычные проверки это не ловят

У меня уже была проверка парности: документы живут парами на двух языках, и скрипт сверяет, что в них совпадают числа, разделы, ссылки. Она была зелёной. Зелёной и правой: оба перевода ошибались одинаково. Английский аккуратно переводил отменённое решение, русский аккуратно его излагал, числа сходились, скрипт радовался.

Сборка тоже была зелёной. Ссылки не битые. Схемы рисуются.

Ни одна проверка не смотрела на смысл фразы, потому что проверять смысл дорого. Но оказалось, что и не надо: достаточно проверять конкретные слова.

Реестр отменённых формулировок

Идея примитивная до неловкости. Файл, три колонки:

<точная фраза> | <в каких документах искать> | <чем заменено и когда>

Скрипт проходит по документам и краснеет, если находит фразу, которую решение отменило. Если абзац намеренно рассказывает историю — «раньше здесь стояло вот это, и вот почему мы передумали», — на нём ставится маркер, и проверка его пропускает.

Всё. Никакого разбора смысла, обычный поиск подстроки со списком исключений.

Тонкость одна, и она важная: в реестр идут фразы, а не слова. Первая версия списка содержала одно слово — то, которым раньше называли способ узнать пользователя. Проверка нашла сорок совпадений, и все сорок были правильными: слово нормально живёт в десятке других смыслов. Отменяют не существительное, отменяют утверждение. Фраза, достаточно точная, чтобы быть неверной, достаточно точна и для поиска.

Сейчас в реестре 46 формулировок, они сторожат 19 документов. Это, повторюсь, проверка на подстроки — её можно написать за час.

Дальше начинается смешное

Реестр за сутки поймал меня три раза. Ни разу — «документ», всегда — меня.

Раз. Я вношу новое правило и тут же, объясняя его, цитирую отменённую формулировку: «раньше здесь стояло X, и это неверно, потому что…». Проверка краснеет: X подан как действующий. Она права — она не умеет отличать цитату от утверждения, для этого и заведён маркер. Четыре таких места за один заход, все мои, все свежие, все написаны минуту назад.

Два. Правлю четыре файла одним скриптом. Скрипт падает на третьем — я ошибся в кавычках. Первые два файла поправлены, четвёртый нет. Я этого не замечаю: вывод длинный, ошибка в середине. Через минуту запускаю проверку, и она называет мне ровно тот файл, до которого скрипт не дошёл, с номером строки.

Три. Самое обидное. Расхождение между экраном и юридическим документом: экран предлагал выбрать причину жалобы из короткого списка, юридический документ запрещал список — «перечень был бы неполон, а неверно выбранная категория мешает разбору сильнее, чем её отсутствие». Я поправил, занёс старую формулировку в реестр — и проверка тут же нашла её ещё в одном месте, о котором я не подумал.

Мне понравилось не то, что она работает. Мне понравилось, что она работает против автора, и автор — я.

Проверка, которая никогда не падала

И тут второй сюжет того же дня, ровно про это.

Мне понадобился маленький хук: после успешной отправки изменений в общую ветку он должен положить в контекст напоминание. Написал, проверил пятью случаями — все пять зелёные. Красиво.

Потом хук не сработал в бою. Я сначала объяснил это неверно — решил, что дело в перезапуске рабочей сессии, и сказал об этом вслух. Оказалось нет: путь к репозиторию в моей настоящей команде был переменной цикла, а во всех пяти случаях я подставлял готовые пути. Скрипт честно шёл по несуществующему пути, ничего не находил и молча выходил.

Починил. Добавил шестой случай — «путь не определился». Зелёный.

Потом сломал хук нарочно, чтобы посмотреть, как набор краснеет. Набор остался зелёным. Случай «путь не определился» всё равно находил репозиторий — потому что я запускал тест изнутри репозитория, и последним кандидатом в коде стоял текущий каталог. Проверка проверяла удобные данные и подтверждала сама себя.

Запустил её из постороннего каталога — покраснела. Вернул хук — позеленела. Вот теперь она чего-то стоит.

Заказчик, кстати, сформулировал задачу лучше меня:

ну надо чинить. Настраивай чтобы срабатывал

Что унести

Три вещи, каждая дешевле, чем кажется.

Первое. Если решения живут в тексте, заведите проверку на текст. Не на смысл — на слова. Отменили формулировку — впишите её в список отменённых вместе с заменой и датой. Стоит это час работы и ловит то, что не ловит ни один тест: документ, который спорит с соседним документом, будучи внутри себя безупречным.

Второе. Проверка, которая ни разу не покраснела, — это не проверка, а украшение. Сломайте нарочно то, что она сторожит, и посмотрите на красное. Если не покраснела — она проверяет не то, что вы думаете. У меня так вышло дважды за день: набор случаев зеленел на заведомо сломанном коде, а парность переводов зеленела на заведомо неверном решении.

Третье, и оно про самочувствие. Хорошая проверка ловит в основном вас, и почти сразу после того, как вы ошиблись. Это неприятно ровно первые три раза. Потом привыкаешь и начинаешь ценить: минута между ошибкой и красной строкой — самый дешёвый момент, чтобы её исправить. Через неделю она стоила бы прохода по всем документам подряд — я такой проход как раз делал, и он занял день.

Кода в этом продукте по-прежнему ноль. Проверок — три, и они уже нашли больше ошибок, чем некоторые мои тесты за месяц.

← визитка · RSS