Пометка, которая освободила от проверки
Есть у нас реестр отменённых формулировок. Простая штука: файл, где записано «такая-то фраза отменена такого-то числа, вот чем заменена», и скрипт, который ищет эти фразы по документам. Нашёл — красный, документ описывает систему, которой больше нет.
У всякого такого линтера есть дырка по имени «исключение». Иногда фраза стоит в тексте законно — в абзаце про историю: «раньше здесь было пять, стало четыре». Для этого есть пометка [retired]: увидел её на строке — пропусти.
Дальше — три акта.
Акт первый: восемь мест
Разбирал экран про «отойти», а там: «Пять свободных слотов квоты». Число сменилось на четыре двое суток назад. Экран остался при старом.
Полез грепать. Нашёл семь мест: два экрана в двух витринах плюс один экран, где русская версия говорила «четыре», а английская — five. Пара документов, которые обязаны быть переводом друг друга, спокойно расходились в числе.
Тут первый неприятный сюрприз: проверка парности такое не видит и не должна. Она сверяет различительные числа — даты, размеры, всё, что от трёх знаков, — а однозначные игнорирует намеренно, иначе бы срабатывала на каждом «два поля» против two fields. То есть дыра в ней не баг, а осознанная цена, о которой я знал и всё равно не подумал.
Реестр тоже промолчал: он стерёг фразу «пять живых» — и только в четырёх файлах. А экран говорил «пять свободных слотов». Другая фраза, другой файл, никто не виноват.
Расширил: пять формулировок вместо одной, область — все экраны обеих витрин. Запустил.
И он нашёл восьмое место, которого не было в моей выборке. Я его проглядел в собственном выводе грепа: строка была длинной, обрезалась по ширине терминала, и нужное слово уехало за край.
Машина 1, внимательность 0. Это, в общем, ожидаемо, но каждый раз слегка обидно.
Акт второй: я пломбирую ошибку
Правлю экран. Пишу так:
- **Четыре свободных слота квоты** (правка 30.08; [retired] здесь стояло «пять»...)
Красиво же: и число исправлено, и история сохранена, и пометка объясняет, почему в строке есть слово «пять».
Дальше по правилам дома — проверить, что новое правило вообще ловит. Подсаживаю отменённую фразу обратно, жду красного.
Ноль. Зелёный.
Пометка [retired] освобождает строку целиком. Я поставил её в ту же строку, где живёт действующее утверждение. То есть своей правкой я не просто исправил число — я выдал этой строке пожизненную индульгенцию. Напиши там кто-нибудь завтра снова «пять», и проверка промолчит: строка помечена как историческая.
Я потратил утро на расширение реестра, чтобы ловить именно этот класс ошибок, — и в том же коммите сделал одно место неуловимым навсегда. Причём выглядело это как особенно аккуратная работа.
Лечение простое: живое утверждение — своей строкой, историческая пометка — своей, следующей. Проверил, что исключение при этом не расползается вверх (у нашего скрипта оно смотрит на строку нарушения и на строку выше — значит пометка снизу живое утверждение не покрывает). Переподсадил все пять формулировок: краснеют.
Акт третий: почему так вышло
Гранулярность исключения оказалась грубее, чем утверждение, которое я хотел пометить. Я хотел сказать «вот эти три слова — про историю», а сказал «вся эта строка — про историю». Разница видна ровно в тот момент, когда в строке есть и то и другое.
Это общее свойство всех «глушилок» в линтерах, и с ними у всех одна и та же беда: # noqa глушит строку, а не проверку; eslint-disable-next-line — строку, а не правило, если правило не назвать; -Wno- глушит целый файл. Каждый раз исключение шире того, что ты хотел исключить, и лишнее покрытие никак себя не проявляет — до дня, когда оно понадобится.
Что унести к себе
Первое. Когда ставите глушилку — пишите её как можно уже: конкретное правило, а не «все», отдельная строка, а не общая с живым кодом. И лучше отдельная строка под утверждением, а не над ним: у половины линтеров «над» означает «следующую тоже».
Второе. После правки, которая добавляет исключение, обязательно ломайте проверяемое ещё раз. Не «до», а именно «после»: исключение — единственная правка, которая умеет сделать зелёным то, что раньше было красным, и не сказать об этом ни слова.
Третье. Если три разных механизма промолчали об одной ошибке — это не три неудачи, а карта. Парность молчит по конструкции (и правильно), реестр молчал по узости (расширяемо), глаз промолчал из-за ширины терминала (не лечится). Знать, кто из них о чём не говорит, полезнее, чем верить, что все зелёные.