Качество в потоке: линтеры, типы, тесты и ревью как автоматика
Есть два способа думать о качестве. Первый — «качество проверяют в конце»: разработчики пишут, отдел тестирования проверяет, менеджер решает, выпускать или нет. Второй — «качество встроено в поток»: изменение по дороге от редактора до продакшена проходит через последовательность ворот, каждое из которых дешевле и быстрее следующего, и большинство проблем умирает на первых метрах пути. Первый способ проигрывает не потому, что «неправильный», а потому, что экономически хуже: он копит проблемы там, где их исправление стоит дороже всего, и превращает выпуск в событие с высоким риском.
Эта глава — про инженерную композицию ворот: какие бывают, в каком порядке их ставить, сколько каждое стоит и что делать с тестами, которые падают через раз. Виды тестирования и тест-дизайн вглубь — в треке testing, принципы кода и культура ревью — в principles, безопасность конвейера — в devops/17-security-in-pipeline.
Цена дефекта как функция момента обнаружения
Вы наверняка слышали: «ошибка в проде обходится в 100 раз дороже, чем найденная при проектировании». Цифру приписывают то Боэму (Software Engineering Economics, 1981), то несуществующему «IBM Systems Sciences Institute». Лоран Боссави в книге The Leprechauns of Software Engineering показал, что надёжного первоисточника у множителя нет — он кочует из презентации в презентацию. Не опирайтесь на «в 100 раз» в спорах: вас разоблачат. Но механизм роста стоимости реален и объясняется без магических чисел.
Потеря контекста. Тест упал через 30 секунд — вы помните, зачем писали эту строку; через две
недели вы читаете собственный код как чужой, а контекст — самый дорогой ресурс отладки.
Расширение радиуса поражения. Дефект в рабочей копии касается одного человека, в main —
блокирует команду, в проде — задевает пользователей, данные, поддержку и иногда юристов.
Стоимость координации. Исправление в проде — это не патч, а откат или хотфикс, уведомления,
постмортем, проверка данных: в коде строка, в процессе человеко-дни. Работа поверх дефекта.
Чем позже найден, тем больше кода написано в предположении, что всё правильно. Отсюда принцип
сдвига влево (shift left): не «тестировать больше», а «тестировать раньше» — если проверка может
выполниться на уровень ближе к разработчику, она должна выполняться там.
Лестница ворот
Ядро главы. Путь изменения — лестница: каждая ступень дороже предыдущей по времени отклика, но ловит то, что предыдущая не могла. Стрелки назад показывают, во что обходится возврат с этой ступени.
типы, опечатки, мёртвый код"] B["2. Pre-commit хук · 2–5 с
формат, быстрый линт, секреты"] C["3. Локальные быстрые тесты · десятки секунд
регрессии в изменённом модуле"] end subgraph L2["Pull request"] D["4. CI на PR · 5–10 минут
линт, типы, тесты, сборка, SAST"] E["5. Ревью человеком · часы
замысел, имена, границы"] end subgraph L3["После слияния"] F["6. Пост-merge конвейер · 20–60 минут
e2e, нагрузочные, сканы"] G["7. Прод · канарейка, флаги, мониторинг
реальный трафик"] end A --> B --> C --> D --> E --> F --> G D -->|"чиним за минуты"| A F -->|"откат ветки или хотфикс"| A G -->|"чиним под давлением инцидента"| A
| Уровень | Время отклика | Что ловит | Чем платим |
|---|---|---|---|
| Редактор / LSP | 0,1–2 с | синтаксис, типы, неиспользуемые импорты | настройка окружения у каждого |
| Pre-commit хук | 2–5 с | формат, быстрые линт-правила, секреты, большие файлы | задержка каждого коммита |
| Локальные быстрые тесты | 10–60 с | регрессии в изменённом модуле | дисциплина запускать |
| CI на PR | 5–10 мин | полный линт, типы, unit + интеграционные, сборка, SAST, зависимости | деньги за раннеры, ожидание |
| Ревью человеком | часы (SLA — рабочий день) | замысел, имена, границы, уместность решения | самое дорогое время в компании |
| После merge | 20–60 мин | e2e, нагрузочные, длинные security-сканы | сложные стенды, флаки |
| Прод | минуты–часы наблюдения | всё, что зависит от реальных данных и трафика | риск для пользователей |
Правило чтения таблицы: проверка стоит на самой левой ступени, где она физически возможна и где её время отклика приемлемо. Поиск секретов возможен в pre-commit — значит, он там.
1. Редактор и LSP
Самое дешёвое и самое недооценённое звено. Language Server Protocol сделал так, что один и тот же
анализатор (gopls, rust-analyzer, pyright, tsserver, clangd) работает в любом редакторе:
красное подчёркивание за 200 мс — ворота, через которые проходит каждая строка кода в компании.
Инженерное следствие: конфигурация анализаторов лежит в репозитории (pyproject.toml,
tsconfig.json, .golangci.yml), а не в личных настройках редактора; иначе у каждого свой набор
правил, а CI становится источником сюрпризов (Рабочее окружение разработчика,
трек editors).
2. Pre-commit хук
Git-хук pre-commit запускается до создания коммита и может его отменить. Его нагрузка — проверки,
которые работают только по изменённым файлам и укладываются в единицы секунд: автоформатирование
с правкой на месте, быстрые линт-правила, поиск секретов (gitleaks,
detect-secrets), запрет больших бинарников, синтаксис YAML/JSON, формат сообщения коммита
(в commit-msg). Стандарт де-факто — фреймворк pre-commit: конфиг
в репозитории, установка одной командой, изолированное окружение у каждого хука; альтернативы —
lefthook и husky.
# .pre-commit-config.yaml — быстрые ворота, всё только по изменённым файлам
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: check-added-large-files # блокирует случайный коммит дампа на 200 МБ
args: ["--maxkb=512"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
hooks:
- id: ruff # линтер по изменённым файлам, миллисекунды
args: ["--fix"]
- id: ruff-format
- repo: https://github.com/gitleaks/gitleaks
rev: v8.19.2
hooks:
- id: gitleaks # секрет не должен попасть даже в локальную историю
Почему тяжёлые проверки в pre-commit — вредительство. Коммит должен быть дешёвой операцией: его
делают десятки раз в день, в том числе «сохраниться перед экспериментом». Хук на 40 секунд превращает
коммит в событие, люди начинают коммитить реже и крупнее — и вы своими руками ломаете практику мелких
коммитов, на которой держится вся остальная лестница. Второй эффект: все выучивают
git commit --no-verify, и ворота перестают существовать. Правило: pre-commit — до 5 секунд, всё
остальное — в CI. Механика хуков — в git/08-hooks-and-automation.
3–4. Локальные тесты и CI на pull request
Ступень, которую часто пропускают: возможность запустить релевантное подмножество тестов локально
за десятки секунд — выбор тестов по изменённым файлам (pytest --testmon, jest --onlyChanged),
watch-режим, одна команда на всё (make test-fast). Критично, чтобы любую проверку из CI можно
было воспроизвести локально одной командой: ворота, которые нельзя запустить у себя, превращают
отладку в цикл «поправил — запушил — ждал 8 минут — посмотрел лог».
В CI на PR живёт основная масса автоматики: полный линт, проверка типов, unit- и интеграционные тесты, сборка артефакта, статический анализ безопасности (SAST), аудит зависимостей на уязвимости и лицензии. Устройство конвейера — тема следующей главы, CI/CD. Главный параметр ступени — время до обратной связи: 10 минут на PR приемлемо, 5 — хорошо, 20+ — люди переключаются на другие задачи, и стоимость переключения съедает выгоду от автоматизации. Удержаться в бюджете помогают параллельные джобы, кэш зависимостей и разделение на «быстрые» (блокируют merge) и «медленные» (после merge или по расписанию).
5–7. Ревью, пост-merge и прод
Ревью человеком ловит то, чего машина не понимает: уместность решения, имена и границы, протекающие абстракции, пропущенные случаи (пустой список, повторная доставка, откат), обратную совместимость контрактов (principles/11-compatibility-and-contract-evolution) и наблюдаемость. Детали — в разделе «Ревью как ворота» ниже.
После merge живёт то, что слишком дорого блокировать PR: полные e2e-сценарии, нагрузочные тесты, длинные security-сканы, сборка под все платформы, мутационное тестирование. Компромисс честный: узнаёте позже, но не платите за это временем каждого PR. Условие корректности — у падения ночного прогона есть владелец, иначе через месяц там 40 красных сборок, на которые никто не смотрит.
Прод — последние ворота: канареечный релиз (1–5 % запросов на новую версию), фича-флаги, автоматический откат по метрикам ошибок и задержек. Мониторинг здесь буквально последний тест — он проверяет гипотезу «изменение не сломало пользователей» на данных, которых нет ни на одном стенде (sre/13-release-safety, devops/04-cd-and-release-strategies).
Форматирование и линтинг: закрыть спор автоматом
Спор о стиле — самый дешёвый способ потратить дорогое время. Его закрывают один раз, выбрав
форматтер без настроек или с минимумом настроек: gofmt (Go), rustfmt (Rust), Prettier
(JS/TS/CSS/Markdown), black или ruff format (Python), ktlint (Kotlin), clang-format (C/C++).
Ценность здесь не красота, а отсутствие предмета для спора и чистые диффы. Разницу часто путают:
форматтер переписывает код, не меняя смысла (отступы, переносы, кавычки), он идемпотентен,
и спорить с ним бессмысленно; линтер ищет подозрительные конструкции — неиспользуемая
переменная, сравнение с None через ==, забытый await, мутируемый аргумент по умолчанию,
недостижимый код. Часть его правил про стиль, но самая ценная часть — про баги. Отсюда метафора:
линтер — это исполняемое код-ревью; каждое правило — замечание, которое кто-то однажды написал
вручную, а теперь оно ставится автоматически и одинаково для всех.
Правило двух споров: если правило обсуждали на ревью дважды — оно должно стать линт-правилом или пунктом в документе о стиле. Третьего обсуждения быть не должно.
Тактика внедрения в проект, где «всё красное»: включать правила порциями, использовать baseline
(зафиксировать текущие нарушения и запретить новые), форматировать файлы «по касанию» либо одним
коммитом с записью в .git-blame-ignore-revs, чтобы не сломать git blame. Про стандарты —
principles/09-code-review-and-standards.
Типы как ворота
Статическая типизация проверяет все пути исполнения, а не только покрытые тестами. Это её главное
преимущество перед тестами — и её главное ограничение: она проверяет форму, а не смысл. Дёшево
ловятся: обращение к несуществующему полю и опечатки в именах; null там, где значение обязательно;
несовпадение сигнатур после рефакторинга (переименовали поле — компилятор показал все 40 мест);
забытая ветка в разборе union-типа (exhaustiveness checking); перепутанные аргументы одного
примитивного типа, если завести отдельные типы (UserId вместо str). Не ловятся: неверная
бизнес-логика, гонки, неправильный порядок вызовов, утечки ресурсов, производительность. Тип говорит
«функция принимает Order», но не говорит «этот Order уже оплачен».
Постепенная типизация — реальный сценарий большинства проектов: mypy и strict в TypeScript
включаются по модулям, сначала новый код и границы (публичный API, доступ к БД). Практика:
strict = true как база, список исключений (ignore_errors для легаси) в том же конфиге —
и требование, чтобы список только сокращался; плюс линт-правило, запрещающее any/Any в новом
коде. Как типы заменяют часть паттернов и часть тестов —
design-patterns/13-types-instead-of-patterns
и ddd/11-modeling-with-types.
Тесты в потоке: пирамида как экономика, а не догма
Пирамида тестов (Майк Кон, Succeeding with Agile, 2009) обычно подаётся как заповедь: «много unit, меньше интеграционных, совсем мало e2e». Полезнее читать её как экономическую модель. У теста две характеристики: стоимость прогона (время, инфраструктура, хрупкость) и достоверность сигнала. Быстрые и дешёвые можно запускать на каждое сохранение файла — поэтому их много; дорогие и достоверные запускаются редко — поэтому их мало. Форма пирамиды — следствие, а не цель.
Антипаттерн «мороженое» (ice cream cone) — перевёрнутая пирамида: гора медленных UI-тестов и почти нет быстрых; конвейер идёт 50 минут, половина падений — флаки, красному статусу никто не верит. Возникает обычно не по глупости, а исторически: систему покрывали снаружи, потому что внутрь было не подступиться. Лечение — не «удалить e2e», а сделать код тестируемым и переносить проверки вниз, оставив наверху 5–15 критичных пользовательских сценариев.
Расписание, которое из этого следует: при сохранении файла — тесты изменённого модуля и типы;
на каждый push и PR — все unit, ключевые интеграционные, линт, сборка, SAST; после merge в main —
e2e критичных сценариев, сборка образа, деплой на стенд и smoke; ночью — полный e2e-регресс,
нагрузочные, мутационные и сканы зависимостей; перед релизом — прогон на проде-подобных данных
с проверкой миграций и отката. Глубина — testing/12-automation-strategy,
testing/13-tests-in-ci,
principles/06-testing-principles.
Покрытие: что метрика измеряет на самом деле
Покрытие (line/branch coverage) измеряет ровно одно: какие строки были исполнены во время прогона
тестов. Оно не измеряет, проверялись ли результаты: тест, который вызывает функцию и не делает
ни одного assert, даёт 100 % покрытия этой функции. Отсюда правильное употребление — покрытие как
детектор непокрытого, а не цель. Модуль с 12 % — надёжный сигнал «сюда никто не заглядывал»;
85 % не значат ничего конкретного. Ровно это формулирует Мартин Фаулер в
TestCoverage. А когда «80 %» становится
обязательной целью, срабатывает закон Гудхарта: появляются тесты, дёргающие геттеры и «покрывающие»
сгенерированный код, — покрытие растёт, качество нет, время прогона растёт вместе с покрытием.
Практичная замена — coverage на diff вместо coverage на проект: «строки, добавленные или
изменённые в этом PR, покрыты не менее чем на N %». Такое требование не требует похода в легаси,
не даёт ухудшать ситуацию новым кодом и даёт осмысленный разговор на ревью (инструменты:
diff-cover, patch-статусы в Codecov). Полезное дополнение — мутационное тестирование (mutmut,
Stryker, PIT): оно портит код и смотрит, заметят ли тесты. Дорого, поэтому ночью и по ключевым
модулям, зато отвечает на вопрос «а тесты вообще что-нибудь проверяют?».
Флаки-тесты: главный убийца доверия к CI
Флаки-тест (flaky) — тест, который на одном и том же коде даёт то зелёный, то красный результат.
Это не мелкая неприятность, а системный риск: как только доля флаки переваливает за порог заметности,
команда перестаёт читать красный статус и начинает жать «перезапустить» — и с этого момента ваши
ворота декорация. Google в публикации
Flaky Tests at Google
сообщает, что около 16 % их тестов имеют ту или иную степень нестабильности, — и это у компании
с образцовой инфраструктурой. Источники, примерно по частоте: время (sleep(0.5) вместо ожидания
условия; зависимость от текущей даты, границы суток, часового пояса); порядок выполнения (тест А
оставил запись в БД, тест Б на неё наткнулся); общее состояние (глобальные переменные, синглтоны,
кэши, одна БД на всех); сеть и внешние сервисы (реальные HTTP-запросы, таймауты, rate limit);
гонки (тест ждёт фоновый поток, который иногда не успевает); недетерминированные данные
(генераторы без зерна, порядок обхода множеств, UUID в ожидаемых значениях).
Первый инструмент — не отладчик, а детерминизация: убрать из теста всё, что может меняться между прогонами.
"""Было: тест зависит от текущего времени, случайности и порядка выполнения."""
def test_discount_flaky():
# «сегодня» меняется каждый день, random без зерна раз в N прогонов даёт
# граничный случай, а глобальный кэш переживает тест и влияет на соседей
order = make_order(created_at=datetime.now(), amount=random.randint(100, 10_000))
assert calculate_discount(order) > 0 # «хоть что-нибудь» ничего не гарантирует
"""Стало: время заморожено, зерно зафиксировано, состояние изолировано."""
import random
from datetime import datetime, timezone
import pytest
from freezegun import freeze_time
FIXED_NOW = datetime(2026, 3, 15, 12, 0, tzinfo=timezone.utc) # одна точка правды о «сейчас»
@pytest.fixture(autouse=True)
def deterministic_env(monkeypatch, tmp_path):
"""Изолирует общее состояние: своё зерно, своя директория, чистые кэши."""
random.seed(20260315) # воспроизводимая последовательность
monkeypatch.setenv("TZ", "UTC") # одинаковый часовой пояс везде
monkeypatch.setenv("APP_CACHE_DIR", str(tmp_path)) # своя папка на каждый тест
clear_all_caches()
yield
clear_all_caches() # не оставляем наследства соседям
@freeze_time(FIXED_NOW) # тест не зависит от календаря
@pytest.mark.parametrize("amount,expected", [(100, 0), (5_000, 250), (10_000, 1_000)])
def test_discount_deterministic(amount, expected):
"""Проверяем конкретные границы, а не «что-нибудь больше нуля»."""
assert calculate_discount(make_order(created_at=FIXED_NOW, amount=amount)) == expected
Три приёма убирают большую часть флаки: время как зависимость — передавать источник времени
в код (clock: Callable[[], datetime]), а не звать datetime.now() внутри бизнес-логики;
ожидание условия вместо sleep — wait_until(lambda: order.status == "paid", timeout=5);
изоляция состояния — своя схема БД или транзакция с откатом на каждый тест. Ловить флаки помогает
провокация: случайный порядок (pytest -p randomly), параллельный запуск, стократный прогон.
Политика карантина
Стихийный ответ на флаки — «поставим автоматический перезапуск» — работает как обезболивающее: симптом уходит, болезнь прогрессирует. Ретраи маскируют настоящие гонки, которые потом стреляют в проде, и обесценивают каждый зелёный статус. Рабочая политика:
- Обнаружение. CI хранит историю прогонов; тест, менявший результат на неизменённом коде более N раз за неделю, автоматически помечается нестабильным.
- Карантин. Тест уходит в отдельный набор: продолжает выполняться и собирать статистику, но не блокирует merge. Одновременно заводится тикет.
- Владелец и срок. У тикета есть конкретный человек и дедлайн (скажем, 14 дней). Карантин без владельца и срока — это удаление теста, только медленное и с самообманом. По истечении срока тест либо починен и возвращён, либо удалён — с записью, что перестало проверяться и чем компенсировано.
- Лимит. Не более X тестов в карантине одновременно; упёрлись — останавливаем фичи и чиним. Тот же механизм, что error budget в sre/03-error-budget. Ретраи допустимы как временная мера при двух условиях: каждый пишется в метрику, а тест с ретраями автоматически считается нестабильным.
Ревью как ворота
Самый сильный предиктор эффективности ревью — размер изменения. Классическое исследование ревью в Cisco (SmartBear) показало резкое падение плотности найденных дефектов после ~200–400 строк за заход. В работе Modern Code Review: A Case Study at Google (ICSE-SEIP, 2018) медианное изменение — около 24 строк, а медианное время до первого ответа ревьюера — меньше рабочего дня: задачи режут на куски, которые можно вдумчиво прочитать. Практические правила:
- PR до 200–400 строк содержательного диффа. Сгенерированный код, локализация и вендоринг — отдельными коммитами с пометкой «не читать построчно». Один PR — одно намерение: рефакторинг и изменение поведения не смешиваются, иначе ревьюер не отличит одно от другого.
- Автор — первый ревьюер. Прочитайте собственный дифф целиком и сами прокомментируйте места, где возникли бы вопросы. Это снимает половину замечаний и экономит цикл.
- SLA на ревью. Первый ответ — в течение рабочего дня. Ревью чужого PR важнее, чем начать свою следующую задачу: пока PR ждёт, работа лежит в незавершённом производстве и стареет.
- Отделяйте блокирующее от необязательного (
nit:), и помните, что машина проверяет форму, а человек — замысел: если ревьюер пишет про пробелы, чинить надо не ревьюера, а конвейер.
Подробнее — principles/09-code-review-and-standards и git/pull-request.
Ворота на ветке: техническая реализация процесса
Договорённости, которые не проверяются машиной, — это пожелания. Инструменты хостинга превращают
их в правила. Branch protection — запрет прямого push в main, обязательный PR, запрет
force-push и удаления ветки. Required status checks — явный список проверок, без которых кнопка
merge неактивна; список держат коротким, каждая обязательная проверка — налог на каждый PR.
Требования к ревью — минимум N апрувов, обязательный апрув владельца кода (CODEOWNERS), сброс
апрувов при новом push. Линейная история или merge-коммиты — вопрос вкуса, но решение одно
на репозиторий и зафиксировано настройкой, а не устной традицией.
Отдельно стоит merge queue: она решает проблему семантического конфликта, когда два PR
зелёные по отдельности, но вместе ломают main (один переименовал метод, другой добавил вызов
старого имени). Очередь собирает и тестирует именно ту комбинацию, которая получится после слияния,
и вливает, только если она зелёная; без очереди на активном репозитории main краснеет регулярно.
Механика совместной работы — git/10-collaboration,
стратегии ветвления — Контроль версий как инженерная практика.
Метрики здоровья ворот
Ворота — тоже система, и у неё есть свои показатели:
- Время до обратной связи — p50 и p95 длительности обязательного конвейера на PR; смотреть надо p95, длинный хвост и создаёт ощущение «CI тормозит».
- Доля красных сборок на
mainи время восстановления зелёного: еслиmainкрасный дольше 15–20 минут, вся команда работает на сломанном фундаменте. - Flake rate — доля прогонов, где результат теста изменился без изменения кода; плюс размер карантина и возраст самого старого тикета в нём.
- Доля падений по вине инфраструктуры отдельно от падений по вине кода: если раннеры падают в 5 % случаев, никакая политика «не перезапускать» не выживет.
- Время PR в ожидании ревью и распределение размеров PR — не среднее, хвост «PR на 2000 строк» виден только по распределению.
Интегральный внешний индикатор — четыре метрики DORA: медленный конвейер удлиняет lead time, флаки снижают частоту поставки, отсутствие быстрых ворот повышает долю неудачных изменений (project-management/07-dora-and-engineering-metrics). Оговорка: эти метрики — для диагностики системы, а не для оценки людей. «Покрытие по разработчику» или «число замечаний на ревью» как KPI порождают имитацию вместо качества.
Типичные ошибки
- Сорокаминутный CI на PR. Умножается на число PR в день и на стоимость переключения контекста. Лечится профилированием конвейера, кэшем, параллелизмом, шардированием тестов и переносом медленного в пост-merge.
- Блокирующие ворота без владельца. Проверка, которая падает, но никому не принадлежит, проходит стадии «странно» → «наверное, флака» → «жми перезапуск» → «давайте отключим».
- «Отключим тест, он мешает.» Удаление без записи, что именно перестало проверяться, — скрытая потеря покрытия рисков. Бесполезный тест удаляют решением с обоснованием, а не жестом раздражения.
- Ворота, которые нельзя запустить локально. Проверка, воспроизводимая только на раннере, превращает отладку в игру с восьмиминутным ходом: всё, что делает CI, должно запускаться одной командой на ноутбуке — в контейнере, если нужно окружение.
- Ревью на 2000 строк. Апрув будет, дефекты — нет. Честно большое изменение режут на серию PR (сначала механическое переименование, потом смысловое) либо разбирают очно, по экрану.
- Дублирование проверок на всех уровнях. Один и тот же линт в хуке, в CI на PR и в пост-merge — тройная оплата за один сигнал; у проверки должно быть одно «домашнее» место.
- Ворота без права на исключение. Механизм обхода (хотфикс мимо очереди) должен существовать, быть заметным — лог, уведомление, автотикет — и применяться редко: ворота без аварийного выхода люди выламывают вместе с косяком.
Мини-итог
- Качество — не этап и не отдел, а лестница ворот, где каждая ступень дороже предыдущей и ловит то, что предыдущая не могла; проверку ставим на самой левой ступени, где она возможна.
- Pre-commit — до 5 секунд; тяжёлое туда класть нельзя, сломаете практику мелких коммитов.
- Спор о стиле закрывается форматтером; правило, обсуждённое дважды, становится линт-правилом. Типы проверяют все пути исполнения, но только форму, а не смысл, и вводятся постепенно.
- Пирамида тестов — экономическая модель, а не догма; «мороженое» лечится тестируемостью кода. Покрытие — детектор непокрытого, а не цель; на diff оно полезнее, чем на проект.
- Флаки убивают доверие к воротам: детерминизация, карантин с владельцем, сроком и лимитом, ретраи — только с метрикой. Ревью работает на маленьких PR и с SLA, а branch protection, required checks и merge queue — техническая реализация договорённостей.
- Измеряйте здоровье самих ворот: p95 времени отклика, красный
main, flake rate, размер PR.
Источники
- Martin Fowler. TestCoverage
- Google Testing Blog. Flaky Tests at Google and How We Mitigate Them
- C. Sadowski et al. Modern Code Review: A Case Study at Google, ICSE-SEIP 2018
- Laurent Bossavit. The Leprechauns of Software Engineering — разбор мифа «в 100 раз дороже»
- Nicole Forsgren, Jez Humble, Gene Kim. Accelerate — про метрики DORA; инструменты: pre-commit.com, gitleaks, Ruff
Что дальше
CI/CD — как из этих ворот собирается конвейер, доводящий изменение до продакшена: анатомия стадий, артефакт, который собирают один раз, фича-флаги и измерение результата.