Зелёный билд скрывал баг: проверки не видели ошибку. Узнайте, как построить надёжную валидацию с фикстурами и мета-тестами. Начните применять прямо сейчас!
Представьте: вы запускаете проверку, видите зелёный статус, и уверенно выкатываете код. Но через неделю обнаруживаете, что всё это время в файле была критическая ошибка, которую проверка просто не замечала. Зелёный билд — самое опасное состояние конвейера: он создаёт ложную уверенность. В этой статье я расскажу, как я поймал такой баг в своём репозитории, который работает как киностудия для одного человека, и как я перестроил систему проверок, чтобы зелёный означал именно то, что нужно.
Мой репозиторий управляет созданием 10-серийного фильма. Главный герой — андроид, чьё тело меняется в трёх фазах: идеальное, повреждённое, золотое. Для каждой фазы есть референсное изображение, чтобы кадры были консистентными. Визуальный промпт-файл описывает каждый кадр: текст для генератора изображений и метаданные, указывающие, какой референс прикрепить. Критическое правило: для серии с повреждённым телом нужно прикреплять повреждённый референс. В 9-й серии был прикреплён идеальный референс — и мой валидатор показывал зелёный статус, хотя человек заметил бы ошибку мгновенно.
Первая проверка искала в тексте промпта строку "robotiko", чтобы понять, относится ли сцена к герою. Но промпты никогда не содержат это слово — они говорят "хромовый андроид", потому что так лучше для генератора. Проверка просто не срабатывала: ноль совпадений, зелёный статус.
Вторая проверка вообще не читала поле, где жила ошибка. Неправильный референс хранился в метаданных, которые валидатор не парсил. Я проверял описание картины, а неправильно подписанный холст висел рядом.
Обобщим: проверяйте данные там, где правило действительно живёт. Инварианты, хранящиеся в структурированных полях (ссылки, списки загрузок, конфиги), невидимы для проверок текста или имён. Они молча проваливаются как категория, а не как одна ошибка.
Глубже проблема была в том, что правило, которое нарушал файл, существовало только в моей голове. "Какое тело относится к какой серии" — это было неявное знание, известное одному человеку. Я превратил его в данные: создал phase_reference_map в JSON-файле с профилями персонажей, где каждая фаза сопоставлена с референсом, а исключения для конкретных серий записаны явно. Вся логика задокументирована в ADR (Architecture Decision Record) с датой.
Без записанного источника истины любая проверка — просто чьё-то мнение, зашитое в скрипт. Нечего проверять.
Переписанная проверка читала метаданные и сравнивала их с картой. Первый запуск на EP09 вернул красный статус: точный файл, точные сцены, запрещённый референс, исправление. Этот красный был доказательством, что проверка вообще работает.
Затем я исправил генератор, а не только файл. Ссылки в EP09 были исправлены, а файл навыка, который создаёт промпт-документы, был пропатчен, чтобы следующая серия не могла воспроизвести баг. Если чинить только артефакт, тот же неизменный процесс создаст тот же дефект при следующем запуске.
Проверка без доказательства, что она работает, — это более уверенный способ ошибаться. Поэтому я добавил тесты для тестов.
Две фикстуры были заморожены навсегда. Реальный сломанный файл, захваченный точно в том виде, как он был отправлен, должен падать. Его исправленный близнец должен проходить. Мета-тесты проверяют оба направления, а также то, что сломанная фикстура падает только на проверке ссылок, что фиксирует объяснение, почему она вообще прошла зелёной. Фикстуры не меняются — в этом их смысл.
Эта дисциплина стала обязательной для всех проверок в наборе. Сейчас мета-тестов 245. Если через полгода я ослаблю регулярное выражение и сломанная фикстура станет зелёной, мета-тест покраснеет и скажет мне, что проверка перестала проверять.
Когда я прогнал новые проверки по всем сериям, семь из них показали красный (EP02–EP08), и это выявило ещё более скрытую проблему. В EP06 использовался другой формат заголовков сцен. Парсер не нашёл ни одной сцены, поэтому проверка ссылок неделями проходила по пустоте.
Зелёный, означающий "я проверил всё", и зелёный, означающий "я проверил ничего", выглядят одинаково. Теперь набор проверок утверждает, что каждый отправленный файл парсит ненулевое количество сцен. Если ваш тест проходит по результатам запроса или списку файлов, проверяйте, что количество не равно нулю. Пустая итерация пройдёт любой тест, который вы напишете.
Красные результаты нужно было разбирать. "Исправить" их все было бы ошибкой. Я разделил их на три категории:
Если относиться к каждому красному как к багу, вы испортите правильные файлы. Если считать каждый красный шумом — вы заглушите реальные ошибки. Разбор — это и есть работа.
Ничто из этого не остаётся исправленным само собой. В репозитории есть паттерн "лестница выпуска урока", где исправление поднимается от разового фикса к гарантии, которая переживёт создателя:
Не каждый урок поднимается на все шесть ступеней. Многие остаются на второй навсегда, потому что это вопросы вкуса, которые не может оценить функция. Дисциплина — знать, на какую ступень урок может честно подняться, и не заявлять выше, чем он заслуживает.
Вы можете начать лестницу в любом репозитории без особых церемоний: запишите правило с датой, напишите самую маленькую проверку, которая может упасть, и заморозьте одну BAD-фикстуру. Третий шаг — тот, который я бы пропустил, и именно он держит проверку честной.
Журнал покрытия набора проверок отслеживает 41 инвариант: 30 с машинным контролем, 4 эвристики, которые могут ошибаться и сообщают об этом, 12 человеческих вкусовых гейтов без автоматизации, и 1 признанный пробел. Последняя категория важнее всего. Пробел, который вы назвали, есть в списке. Пробел, который вы не назвали, покрыт зелёной проверкой, которая его не покрывает.
Три контрольные точки в конвейере намеренно человеческие: одобрение после драматургии, после создания референсов и после сценария движения. Каждая записана с sha256 одобренного артефакта, чтобы дрейф после одобрения был виден. Никакая функция не решает, служит ли сцена истории. Смысл механизации всего остального — оставить людям свободу спорить о том, что действительно требует спора.
Теперь зелёный прогон сертифицирует ровно 30 машинных строк и ни дюймом больше. Это то, что я хотел, чтобы зелёный означал.
Репозиторий (валидаторы, замороженные фикстуры, мета-тесты, матрица покрытия и фильм, который они защищают) открыт под лицензией MIT: github.com/fibuladev/robotiko-v2. Лестница задокументирована в docs/method-lesson-graduation.md, а полное исследование — в _management/case_study_validation_backbone.md.
Прямо сейчас вы можете сделать три вещи. Во-первых, найдите проверку, которая никогда не падала, и намеренно сломайте её, чтобы увидеть красный. Во-вторых, запишите правило, которое живёт только в вашей голове, в файл с датой. В-третьих, заморозьте одну сломанную фикстуру и убедитесь, что ваша проверка на ней падает. Это займёт час, но спасёт вас от ложной уверенности.
Хочешь закрепить знания на практике?
Решай задачи на Algolit — интерактивная платформа для обучения
Начать бесплатно →