ГлавнаяБлогКак я поймал баг в зелёном билде: уроки валидации
Python

Как я поймал баг в зелёном билде: уроки валидации

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

Al
Редакция Algolitalgolit.ru
12 мин чтения19 августа 2026 г.

Почему зелёный билд — это не всегда хорошо

Представьте: вы запускаете проверку, видите зелёный статус, и уверенно выкатываете код. Но через неделю обнаруживаете, что всё это время в файле была критическая ошибка, которую проверка просто не замечала. Зелёный билд — самое опасное состояние конвейера: он создаёт ложную уверенность. В этой статье я расскажу, как я поймал такой баг в своём репозитории, который работает как киностудия для одного человека, и как я перестроил систему проверок, чтобы зелёный означал именно то, что нужно.

Контекст: репозиторий-киностудия

Мой репозиторий управляет созданием 10-серийного фильма. Главный герой — андроид, чьё тело меняется в трёх фазах: идеальное, повреждённое, золотое. Для каждой фазы есть референсное изображение, чтобы кадры были консистентными. Визуальный промпт-файл описывает каждый кадр: текст для генератора изображений и метаданные, указывающие, какой референс прикрепить. Критическое правило: для серии с повреждённым телом нужно прикреплять повреждённый референс. В 9-й серии был прикреплён идеальный референс — и мой валидатор показывал зелёный статус, хотя человек заметил бы ошибку мгновенно.

Две ошибки в проверке, которые усугубили проблему

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

Вторая проверка вообще не читала поле, где жила ошибка. Неправильный референс хранился в метаданных, которые валидатор не парсил. Я проверял описание картины, а неправильно подписанный холст висел рядом.

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

Правило, которое никто не записал

Глубже проблема была в том, что правило, которое нарушал файл, существовало только в моей голове. "Какое тело относится к какой серии" — это было неявное знание, известное одному человеку. Я превратил его в данные: создал phase_reference_map в JSON-файле с профилями персонажей, где каждая фаза сопоставлена с референсом, а исключения для конкретных серий записаны явно. Вся логика задокументирована в ADR (Architecture Decision Record) с датой.

Без записанного источника истины любая проверка — просто чьё-то мнение, зашитое в скрипт. Нечего проверять.

Сначала красный, иначе не считается

Переписанная проверка читала метаданные и сравнивала их с картой. Первый запуск на EP09 вернул красный статус: точный файл, точные сцены, запрещённый референс, исправление. Этот красный был доказательством, что проверка вообще работает.

Затем я исправил генератор, а не только файл. Ссылки в EP09 были исправлены, а файл навыка, который создаёт промпт-документы, был пропатчен, чтобы следующая серия не могла воспроизвести баг. Если чинить только артефакт, тот же неизменный процесс создаст тот же дефект при следующем запуске.

Проверяйте проверяющих

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

Две фикстуры были заморожены навсегда. Реальный сломанный файл, захваченный точно в том виде, как он был отправлен, должен падать. Его исправленный близнец должен проходить. Мета-тесты проверяют оба направления, а также то, что сломанная фикстура падает только на проверке ссылок, что фиксирует объяснение, почему она вообще прошла зелёной. Фикстуры не меняются — в этом их смысл.

Эта дисциплина стала обязательной для всех проверок в наборе. Сейчас мета-тестов 245. Если через полгода я ослаблю регулярное выражение и сломанная фикстура станет зелёной, мета-тест покраснеет и скажет мне, что проверка перестала проверять.

Два правила, которые всё держат

  • Сначала заставьте проверку падать. Зелёный, который вы никогда не видели красным, — не доказательство. Возможно, он ничего не проверяет.
  • Никогда не ослабляйте проверку без двунаправленного доказательства: один тест, что она ловит реальный баг, второй — что она игнорирует предполагаемый случай.

Самый страшный зелёный

Когда я прогнал новые проверки по всем сериям, семь из них показали красный (EP02–EP08), и это выявило ещё более скрытую проблему. В EP06 использовался другой формат заголовков сцен. Парсер не нашёл ни одной сцены, поэтому проверка ссылок неделями проходила по пустоте.

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

Красный не всегда означает баг

Красные результаты нужно было разбирать. "Исправить" их все было бы ошибкой. Я разделил их на три категории:

  • Исправить — реальные дефекты. EP04 и EP05 содержали ту же ошибку с неправильным референсом. Я исправил текст, но не менял уже выпущенные изображения, потому что фильмы уже вышли, и запись должна быть честной.
  • Белый список — намеренные и задокументированные исключения. В одной серии сон героя намеренно окружает его сотнями идеальных копий. В другой он противостоит идеальному конформисту. Запрещённое слово легитимно описывает не героя. Исключения записаны с привязкой к сцене и обоснованы, а не как общее "игнорировать здесь", чтобы реальная ошибка всё равно вызывала срабатывание.
  • Уточнить — проверка срабатывала ложно. Она помечала "идеальные полки" как персонажа, хотя это декорация. Проверка научилась судить о субъекте, а не о сцене.

Если относиться к каждому красному как к багу, вы испортите правильные файлы. Если считать каждый красный шумом — вы заглушите реальные ошибки. Разбор — это и есть работа.

Лестница уроков

Ничто из этого не остаётся исправленным само собой. В репозитории есть паттерн "лестница выпуска урока", где исправление поднимается от разового фикса к гарантии, которая переживёт создателя:

  1. Инцидент — кто-то платит за ошибку один раз.
  2. Правило с датой — одна строка на простом языке в файле уроков, с датой и случаем.
  3. Проверка валидатора — функция, которая читает артефакт и падает на паттерне.
  4. Двунаправленная фикстура — замороженный BAD-вход, который должен падать, и GOOD-близнец, который должен проходить, плюс мета-тесты.
  5. ADR — записанное обоснование, чтобы следующий человек унаследовал компромисс, а не отменил его молча.
  6. CI-гейт — одна команда запускает всё, красный блокирует слияние.

Не каждый урок поднимается на все шесть ступеней. Многие остаются на второй навсегда, потому что это вопросы вкуса, которые не может оценить функция. Дисциплина — знать, на какую ступень урок может честно подняться, и не заявлять выше, чем он заслуживает.

Вы можете начать лестницу в любом репозитории без особых церемоний: запишите правило с датой, напишите самую маленькую проверку, которая может упасть, и заморозьте одну 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.

Практический вывод

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

#валидация#тестирование#CI#зелёный билд#фикстуры
Al
Редакция Algolit

Пишем про алгоритмы, подготовку к собеседованиям и карьеру в IT — так, чтобы было понятно и полезно.

Хочешь закрепить знания на практике?

Решай задачи на Algolit — интерактивная платформа для обучения

Начать бесплатно →