Суть Чтение реальных артефактов петли доставки — теста, feature flag, CI-гейта и action item постмортема — и выбор изменения, которое сеньор делает первым.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min
Петля — не абстракция: она проявляется как тест, флаг, CI-гейт и action item постмортема. Прочитайте каждый артефакт, найдите, какое звено петли доставки он усиливает или ломает, и выберите фикс, который сеньор сделает первым.
Цель
Потренировать чтение конкретных артефактов, которые порождает каждая практика — теста, feature flag, гейта пайплайна, action item постмортема — и оценку того, реально ли каждый из них замыкает петлю или тихо оставляет стык открытым.
Сниппет 1 — проверка флага на горячем пути
// поставлен 14 месяцев назад под раскатку "new-checkout", так и не удалёнexport function priceCart(cart: Cart): Money { if (flags.isEnabled("new-checkout-pricing")) { return newPricing(cart); // раскатка "completed" много месяцев назад } return legacyPricing(cart); // всё ещё здесь, всё ещё достижим}
Викторина
Completed
Раскатка new-checkout завершилась больше года назад. Какое сеньорное действие с этим флагом и почему оставить его дороже, чем кажется?
Heads-up Завершённый флаг — не инструмент отката, а живая ветка, которую никто не тестирует. Жизненный цикл (initial → live → completed → archived) существует потому, что пропуск удаления — это как release toggle становится следующим инцидентом в духе Knight Capital.
Heads-up Тихое перепрофилирование release toggle в permission toggle — классическая ловушка flag debt: 'временный' переключатель становится несущим навсегда, без владельца и без тестов на legacy-ветке.
Heads-up Тестировать ветку, которую вы намерены удалить — потраченная работа, легитимизирующая мёртвый код. Фикс — удаление, которое заодно снимает бремя теста, а не добавление покрытия к долгу.
Сниппет 2 — тест, написанный первым
// написан до того, как появилась реализацияtest("refund splits across original tender lines", () => { const order = orderWith([cash(30), card(70)]); const refund = refundService.refund(order, money(50)); expect(refund.allocations).toEqual([cash(15), card(35)]); // пропорционально});
Викторина
Completed
Команда пишет этот тест до того, как существует refundService. В чём главная ценность написать его первым — в терминах петли?
Heads-up Покрытие — побочный продукт. Выигрыш test-first — обратная связь по дизайну (плохой интерфейс виден за секунды) плюс надёжный гейт; погоня за покрытием как целью рождает хрупкие тесты, проверяющие реализацию, а не поведение.
Heads-up Тесты ловят регрессии против известной спеки; ревью ловит дизайн и намерение, которые автор теста не подумал проверить. Это разные звенья — зелёный тест не заменяет ревьюера, читающего изменение.
Heads-up Выигрыш design pressure сильнее всего на уровне unit/API, где вы формируете интерфейс. Петле нужен быстрый unit/контрактный сигнал как гейт мержа; e2e остаётся тонким слоем smoke.
Сниппет 3 — гейт CI-пайплайна
# .ci/merge-gate.yml — обязателен для мержа в trunkon: pull_requestjobs: gate: steps: - run: make unit # ~90с, детерминированно - run: make contract # верификация consumer + provider - run: make e2e-smoke # только 1 критичный путь - run: make e2e-full || true # полный набор, разрешено падать
Викторина
Completed
Этот гейт обязателен для мержа. Какой фикс конфига даёт наибольший рычаг и почему?
Heads-up Навешивание медленных флакающих e2e на гейт мержа — ровно то, что делает сигнал медленным и недоверенным, так что его заглушают — отказ, который гейт уже показывает. Петле нужен быстрый детерминированный гейт; e2e-full — для ночного или асинхронного потока.
Heads-up Это удаляет гейт целиком: сломанная работа свободно садится в trunk, и вы теряете предусловие безопасного частого мержа. Фикс — меньше детерминированных блокирующих проверок, а не превращение всего в неблокирующее.
Heads-up Держать в обязательном гейте шаг, который никогда не может упасть — это театр: похоже на покрытие, но не гейтит ничего, и инженеры узнают, что красное у набора бессмысленно. Либо он блокирует, либо уходит из гейта.
Сниппет 4 — action item постмортема
## Action items- [ ] Улучшить коммуникацию во время инцидентов owner: ? due: ?- [x] Добавить контрактный тест: `cart.total` неотрицателен owner: @ana due: 2026-06-02 ticket: PAY-1183- [ ] Быть аккуратнее при изменении конфига цен owner: team due: ongoing
Викторина
Completed
Читая эти три action item из blameless-постмортема, какой реально замыкает петлю и почему два других проваливаются?
Heads-up Action item должен быть конкретным, owned и отслеживаемым, иначе это театр. 'Быть аккуратнее' не назначает работу и не меняет систему, так что сбой повторяется — ровно тот стык, что ломает петлю.
Heads-up Может, и важен, но без владельца и даты он ничего не выкатывает. Пункт без владельца неотличим от отсутствия пункта; петля замыкается только когда работа owned и отслеживается.
Heads-up Таймлайн — для понимания; owned action items — то, что превращает инцидент в систему, которую вы починили. Без них постмортем — документ, по которому никто не действует, и тот же инцидент возвращается через квартал.
Итог
Каждая практика оставляет конкретный артефакт, и его чтение говорит, цела ли петля: завершённый флаг всё ещё на горячем пути — это долг к удалению, не инструмент отката; тест, написанный первым — это design pressure и сигнал мержа, с покрытием как побочным продуктом; CI-шаг, которому ‘разрешено падать’ — не гейт и тихо приучает команду игнорировать красное; а пункт постмортема замыкает петлю, только когда он owned, датирован, отслеживается и возвращает конкретное утверждение в CI. Читайте артефакт, находите звено, чините стык — не любуйтесь церемонией.