QA-Логия
Ir al canal en Telegram
Все о QA. Канал для тестировщиков Личный блог автора - @just_genych По вопросам рекламы или разработки: @g_abashkin
Mostrar más8 032
Suscriptores
-324 horas
-247 días
-2330 días
Archivo de publicaciones
8 032
Как я перестал писать длинные чек-листы и начал учить разработчиков самопроверке
Раньше я считал, что идеальный чек-лист - это подробный документ на 200 пунктов. Я тратил часы на расписывание каждого шага: "нажать кнопку", "проверить цвет", "убедиться, что текст не съехал". Разработчики смотрели на это и либо игнорировали, либо пробегали по диагонали. А потом - баги, конфликты, фразы вроде "это не моя зона ответственности".
Однажды я понял: длинные чек-листы не учат разработчика думать. Они учат механически выполнять команды. И вместо контроля качества получается имитация.
Я перестроил подход. Теперь перед сдачей фичи я даю разработчику не список проверок, а три вопроса.
Что именно ты изменил? - требует осознания кода. Какие сценарии могут сломаться? - развивает критическое мышление. Как ты это проверил? - вынуждает показать доказательства: скриншоты, логи, тесты.Результат: баги стали находить раньше, а не в день релиза. Разработчики перестали сваливать ответственность - они начали проговаривать риски сами. А я экономлю часы на рутинной регрессии. Что делать, если внедрить такой подход сложно? Начинайте с малого: предложите для одной фичи вместо чек-листа провести мозговой штурм. Покажите выгоду: меньше переделок - быстрее доставка. Не давите: если разработчик упорно просит чек-лист, дайте короткий (10-15 пунктов) и постепенно сокращайте. Главная ошибка новичков: копировать мои вопросы дословно, не адаптируя под команду. Например, если разработчики не пишут юнит-тесты, вопрос "как ты это проверил" вызовет раздражение. Адаптируйте: "Покажи, что код работает в браузере". Почему это круто для карьеры: Переход от "контролера" к "наставнику" повышает ваш авторитет. Разработчики начинают уважать вас как эксперта, а не как надзирателя. Вы освобождаете время для сложных кейсов: нагрузки, безопасности, UX-анализа. Недостатки метода (честно): Нужна хорошая коммуникация. Если команда не готова к диалогу, сначала настройте контакт. Медленнее на старте: первые фичи могут потребовать больше обсуждений, чем проверка по чек-листу. Не подходит для регрессионных блоков, где нужно быстро убедиться, что ничего не сломалось. Как я пришел к этому выводу: Я проработал 5 лет на проекте, где каждый релиз сопровождался 50-страничным чек-листом. Разработчики сдавали баги, тестировщики находили новые, сроки горели. Когда я предложил сократить чек-листы, мой лид сказал: "так исторически сложилось". Я ослушался - и через месяц команда сама попросила не возвращаться к старому подходу. Совет для роста: начните с малого - замените чек-лист на 3 вопроса для одной фичи. Через месяц сравните количество багов от разработчиков и время на тестирование. Увидите разницу. P.S. Не выкидывайте все чек-листы - они спасают при регрессии. Но для самой фичи научите разработчика самопроверке. Это даст больше, чем любой документ.
8 032
Тупик скиллов: почему ты перестал расти на middle и как из него выйти без смены проектов
Знакомо? Ты уже уверенно тащишь задачи, но внутри - ощущение, что упёрся лбом в стену. Всё, что можно было выучить на этом проекте, вроде выучено. А свалить на другой проект или дёргаться с поиском не хочется - и так норм.
Почему так бывает? Частая штука: middle путает «делаю работу» с «расту». Когда ты перестаёшь подолгу смотреть на процессы, не задаёшь вопросы «а почему так?», а просто закрываешь тикеты - ты не развиваешься. Ты становишься надёжным исполнителем, но не специалистом, который двигается выше.
Вот что можно попробовать без смены проекта.
* Ищи слепые зоны. Посмотри на проект как на систему рисков, а не список багов. Например, часто ли падает тестовое окружение? Есть данные, которые уходят в прод без проверки? Обычно такие вещи висят в зоне «не моя забота». А senior’ы этим и отличаются - залезают туда, где не просили.
* Автоматизируй не ради фреймворка, а ради выгоды. Вместо «надо выучить новый инструмент» спроси: «Какая рутина сжирает 80% времени?». Может, написать скрипт для генерации тестовых данных или бота для напоминаний о регресс-прогоне.
* Учи других. Это звучит банально, но работает - собери чек-лист граблей для новичков, прочитай мини-воркшоп по тест-дизайну. Когда объясняешь, вылезают твои собственные дыры.
* Дневник решений. Просто записывай раз в день одну нестандартную ситуацию: что сломалось, как искал причину, что сделал бы иначе. Через месяц перечитай - удивишься, сколько нового уже накопилось.
* Не гадай. Вместо «вроде работает» используй модели. Добавили новый API - проверь не только его, но и обратную совместимость, нагрузку, безопасность. Интуиция хороша, но она же и тормозит.
Что обычно тормозит:
- надежда, что новый инструмент сам вытянет рост;
- замыкаться только на тестировании и не лезть в процессы команды;
- думать, что на скучном проекте негде расти - всегда есть куча серых зон.
Для меня middle - это не галка в резюме, а точка выбора. Если перестать искать проблемы только в багах, а начать смотреть на процессы, даже самый «серый» проект даст кучу поводов расти.
8 032
Волна сокращений в IT: к чему готовиться джунам
Этой весной Oracle уволил почти 30 000 сотрудников. Председатель совета директоров Ларри Эллисон объясняет это тем, что выиграют те, кто сегодня строит инфраструктуру для ИИ. Компания инвестирует миллиарды в дата-центры — в 2027 году объем таких вложений составит $70 млрд. Увольнения же высвободят до $12 млрд в год, что за несколько лет покроет эти затраты.
Oracle не одинока. В прошлом году Microsoft уволила 15 000 сотрудников, Meta — 8 000. Amazon за два года избавилась почти от 50 000. В сумме без работы остались 165 000 представителей технологического сектора. Хотя масштабы впечатляют, такие сокращения не редкость в переходные времена, когда появляются новые технологии.
Пока тренд заметен на западном рынке, но волна, вероятно, дойдет и до России. Признаки уже есть: сокращение найма, «скрытые» увольнения и заморозка зарплат.
Источник
8 032
Вчера на код-ревью баг-репорта я чуть не лопнул от смеха и раздражения одновременно. Коллега прислал отчёт с 15 скриншотами, пояснениями в духе "нажал кнопку, курсор моргнул, потом не моргнул, потом снова моргнул" и стеной логов браузера. Разработчик закрыл баг через 3 минуты словами "не воспроизводится, недостаточно данных".
Знакомая ситуация? Мы, тестировщики, часто впадаем в крайности: либо пишем "всё сломалось" без шагов, либо раздуваем отчёт до размеров диплома. Но золотая середина — это баланс между полнотой информации и скоростью принятия решения.
Когда детализация становится вредной?
1. Перегрузка контекстом. Если указать все 50 шагов, включая "открыл браузер, нажал F12" — вы заставляете разработчика тратить время на фильтрацию. Ему нужно только то, что ведёт к багу. Лишнее — шум.
2. Избыточные логи. Один раз я вложил дамп логов за час, хотя баг воспроизводился за 2 секунды. Разработчик искал иголку в стоге сена и попросил сократить логи до момента ошибки.
3. Субъективные оценки. "Интерфейс выглядит ужасно" — не данные, а мнение. Нужны чёткие критерии: пиксели, размер, время в миллисекундах.
Как найти золотую середину?
* Принцип "минимально необходимого". Спросите: "Что разработчику нужно для воспроизведения за 30 секунд?" Шаги — только обязательные. Если баг проявляется после 10 повторов — напишите "повторить 10 раз", но не описывайте каждый.
* Разделите факты и контекст. Факты: "При вводе символа
# поле сбрасывает значение". Контекст: "Только в Firefox 120 на macOS". Контекст — в среде или примечаниях, а не в шагах.
* Используйте визуальную иерархию. Скриншот с красной стрелкой и подписью "Ошибка в элементе X" — золото. Каша из 10 обводок — шум. Один баг — один скриншот с чётким указанием.
* Проверяйте время на чтение. Если я трачу больше минуты на понимание отчёта — я переписываю его. Разработчик не будет разбираться дольше.
Мой чек-лист перед отправкой:
- Может ли разработчик воспроизвести баг без дополнительных комментариев?
- Убрал ли я все шаги, не влияющие на воспроизведение?
- Самодостаточны ли скриншоты/логи?
- Не заменил ли я объективные данные субъективными оценками?
Помните: ваша цель — не показать, как много вы натестировали, а помочь разработчику исправить проблему быстро. Детализация — инструмент, а не самоцель. Если отчёт мешает принятию решений — он вреден.
Пробуйте завтра взять свой самый старый баг-репорт и сократить его наполовину. Удивитесь, как много было лишнего.8 032
Удалёнка съедает ROI: неочевидные потери, которые не считает бухгалтер
Мы в агентстве «Найт Стрит» уверены, что затраты на удалёнку обычно считают по очевидной экономии — не нужен большой офис, падают расходы на командировки, нанимать можно по всей стране. С этой стороны всё сходится. Но у распределённой работы есть вторая колонка расходов, которая почти не попадает в расчёты бухгалтера. Сюда идёт лишний созвон там, где хватило бы сообщения, согласование на два дня вместо десяти минут общения в переговорке, новичок, который вникает на проект месяц, а не положенные две недели.
По отдельности это мелочи, но в команде из сотни дорогих специалистов все эти мелочи складываются в миллионы потерянной выручки за год. Спорить, надо ли срочно возвращать всех в офис, мы не будем. Лучше с холодной головой посчитаем, где именно удалёнка бьёт по ROI и что с этим делать. Цифры дальше модельные: смысл не в том, чтобы поверить моим процентам, а в том, чтобы подставить в формулы свои данные.
Читать далее на Habr
8 032
Три вопроса, которые middle QA перестаёт задавать себе после года работы — и почему это тормозит рост
Когда дорос до middle QA, показалось, что все важные вопросы решены. Баги ищу, тест-кейсы пишу, с разработчиками спорю — нормально. Но спустя год заметил, что стою на месте. Оказалось, я перестал задавать себе три вопроса — и это незаметно съедало рост.
1. «Почему этот баг вообще появился — и как его можно было предотвратить?»
На junior-уровне радуешься, когда нашёл баг. На middle — когда закрыл задачу. Но после года работы многие перестают анализировать корни. Пропущенная проверка в автотестах? Ошибка в требованиях? Слабая коммуникация на уточнении? Если не спрашивать, остаёшься в роли ловца багов, а не инженера по качеству. Шанс повлиять на процесс улетает.
2. «Как мои тесты влияют на бизнес-метрики?»
Middle QA часто залипают на технику: покрытие, скорость прогона, количество багов. Но забывают спросить, что это даёт продукту. Уменьшает ли работа количество инцидентов в продакшене? Сокращает ли время выхода фичи? Если не связывать тесты с целями продукта, теряешь видимость своего вклада. На собеседованиях это сразу считывается — тебя не видят как стратегического игрока.
3. «Что я могу сделать по-другому, чтобы вырасти через полгода?»
После года легко уйти в рутину: автотесты пишутся, прогоны проходят, баги закрываются. Но рост сам не случается. Если не спрашивать, ищешь точки развития: новый инструмент, разобраться в архитектуре, взять ревью тестов у других. Иначе остаёшься на плато — и через год твой опыт просто «ещё год на той же позиции».
Middle — не статус, а этап, где легко застрять. Возвращайте эти вопросы в ежедневку. Они помогают не просто тестировать, а строить карьеру осознанно. Попробуйте сегодня — спросите себя про последний баг. Удивитесь, сколько нового увидите.
8 032
Рутина в QA — штука коварная. Сидишь полгода на проекте, repeat-баги одни и те же: сломалось — починили — упало опять. Искра гаснет. Начинаешь ненавидеть эти баги не потому что они важные, а потому что опять. Работа превращается в один бесконечный круг.
Но вот что я замечал: рутина не равно стагнация. Стагнация — это когда ты с ней смирился как с данностью.
Ошибка, которую я видел много раз: человек превращается в робота. Открывает баги по шаблону, голову отключает. А потом на собеседовании не может вспомнить, что предлагал по улучшению процесса.
Когда тонешь в рутине, теряешь способность анализировать причины. Repeat-баг воспринимаешь как «опять сломалось», а не как симптом кривого кода или архитектурной проблемы. Перестаешь рефлексировать: «Что я делаю, чтобы такого вообще не было?». Soft skills деградируют: с разработчиком общаешься через автомат «открыл-закрыл».
Мне помогали три вещи:
Первое — сдвиг фокуса. Каждую неделю беру однотипную проверку и ищу необычное. Просто кликать скучно, поэтому спрашиваю: «А что, если разработчик изменил логику, а мне не сказал?». Обычный repeat-баг может открыть скрытую регрессию. Мозг переключается с конвейера на следопыта.
Второе — не сводить обратную связь к «я забагал». Повторный баг — это повод предложить автотест или пойти к разработчику и спросить: «Почему код написан так, что он падает? Может, проблема глубже, в архитектуре?». Ты перестаешь быть пассивным исполнителем.
Третье — искусственные ограничения. Ставлю таймер: 30 минут на стандартные проверки, но параллельно выписываю гипотезы, что еще может сломаться. Через месяц так накидал 5 предложений по регрессионному набору — их взяли в работу.
По поводу AI — тут ловушка. Он сгенерирует тест-кейсы, но про твою мотивацию не подумает. Если скормить ему всю рутину, перестанешь замечать паттерны, которые ведут к росту. Именно человек видит неудобные баги, ломающие сценарий. Рутина может эту внимательность развить, если не позволить себе уснуть.
Repeat-баги — это маркер. Если они одни и те же месяц, значит, что-то не так. Пора бить тревогу и предлагать коренное решение, а не просто тушить пожары.
А ты как с этим справляешься? Может, есть фишки, которые переворачивают подход к рутине.
8 032
6 ошибок в метриках дефектов, из-за которых QA теряет контроль над качеством
Когда дашборд зеленеет, число багов падает, а команда получает похвалу — это может быть ловушкой. Через пару недель прод ловит инцидент на ровном месте. Так бывает, если метрики дефектов становятся целью для отчёта и подменяют реальное управление качеством.
В статье разобрано шесть типовых ошибок. Первая — количество багов как личный KPI. Это заставляет QA скрывать дефекты или не заводить их вовсе, чтобы выглядеть лучше в глазах руководства. Вторая — доля переоткрытых задач. Эту метрику легко обойти красивой статистикой: если закрыть баг без реального воспроизведения, процент переоткрытых останется низким, хотя проблема никуда не делась. Третья — игнорирование сегментации дефектов. Например, когда все баги складываются в одну корзину, теряется понимание, какие из них критичны для прода, а какие — косметические. Четвёртая — фокус на проценте закрытых багов без учёта времени их жизни. Пятая — неиспользование плотности дефектов: метрика, показывающая количество багов на единицу кода, часто упускается, хотя именно она выявляет проблемные модули. Шестая — стремление к нулю открытых багов. Это иллюзия: некоторые дефекты невозможно закрыть, их нужно документировать и взвешивать риски.
Каждая из этих ошибок ведёт к тому, что реальное качество продукта ухудшается, а цифры на дашбордах остаются зелёными. Статья предлагает пересмотреть подход к метрикам: не гнаться за красивыми отчётами, а использовать метрики для честного анализа состояния продукта.
Читать далее
8 032
Роль продукта в тестировании: как понять, что ты поймал настоящий баг или просто «пофиг» для бизнеса
Каждый, кто работает в тестировании, через это проходил. Находишь баг, аккуратно описываешь шаги, прикладываешь логи. А продакт смотрит и говорит: «пофиг, так и задумано». Или просто закрывает задачу. Бесит. Но если присмотреться — продукт это не про идеальный код, а про бизнес.
Первое, что стоит усвоить: баг и ошибка в коде — не одно и то же. Багом я называю то, что мешает пользователю сделать то, зачем он пришёл. Или мешает бизнесу заработать. Если кнопка «Купить» не работает — это баг, и его надо фиксить вчера. Если съехал отступ на пиксель — скорее всего, пофиг. Если только вы не тестируете интерфейс для дизайнеров с лупой.
Как отличить одно от другого? Настоящий баг ломает логику. Пользователь не может завершить сценарий, теряет деньги или время. А «пофиг» — это косметика, редкий кейс у пары человек из десяти тысяч, или то, что можно отложить без последствий.
Продакт обычно считает так: «Сколько мы потеряем, если не починим?» Вопрос, который стоит задавать себе: «Сколько пользователей от этого пострадает?» Если ответ «единицы» — готовься, что баг закроют. Даже если технически он выглядит критичным.
Типичная ошибка джуниоров — считать, что любой баг надо фиксить немедленно. Если дефект не влияет на конверсию или удержание — продакт вправе сказать «пофиг». И это не про то, что ты плохо проверил. Это про разницу между ошибкой в коде и ошибкой в продукте.
Что с этим делать? Пара вещей, которые работают у меня.
* Перед тем как заводить баг, спрашиваю: «Почему это важно для пользователя?» Если ответ абстрактный — скорее всего, приоритет низкий.
* Потом смотрю в аналитику. Если баг ловит 5 из 10 000 — не трачу время на многостраничное описание.
* И аргументирую бизнес-цифрами: «Из-за этого 20% пользователей кликают мимо, теряем столько-то конверсий».
И не обесценивай себя, когда продакт закрывает баг. Твоя задача не найти всё, а научиться отфильтровывать через призму бизнеса. Тогда из просто «багоискателя» вырастаешь в инженера, который на продукт влияет.
А у тебя было, что продакт закрывал баг, а он потом «выстрелил»? Расскажи в комментариях.
8 032
AI-инструменты, которые искажают реальные баги: когда генерация тестовых данных создаёт ложную уверенность
Я часто вижу, как QA-инженеры, особенно junior, попадаются в ловушку "магии AI". Берут инструмент, генерирующий тестовые данные по описанию требований, и расслабляются. Кажется, что все под контролем. А потом продакшен падает из-за граничного случая, который AI не заметил.
AI-генерация данных - штука полезная, но и зона риска. Она искажает картину реальных багов.
Во-первых, AI учится на "идеальных" данных. Он знает, как выглядит типичная строка, дата, email. Но баги живут в аномалиях. AI не сгенерирует строку с нестандартным Unicode-символом или дату 29 февраля 2023 года, если проект этого не требует. Он создает комфортную "прическу" для данных, а не реальную грязь, на которой падает система.
Во-вторых, инструменты игнорируют бизнес-контекст. AI может сгенерировать 1000 корректных заказов на складе, но не поймет, что 500 из них - просрочка, а 300 - без цены. Вы тестируете функционал, думая, что данные корректны, а система работает как идеальная.
В-третьих, ложная уверенность. Автотесты на AI-данных зеленые, но вручную их никто не проверяет. Баг "товар не отображается при нулевом остатке" маскируется AI-генерацией, которая не создала таких записей.
Решения:
* Не доверяйте AI без ручной проверки. Каждый сгенерированный набор анализируйте на граничные условия.
* Используйте AI как черновик, а не финал. Проверяйте, где могут быть дыры.
* Тестируйте на "грязных" данных: аномальные символы, кривые даты, пустые поля, переполнения.
* Смешивайте AI (80% базиса) и ручную генерацию (20% "бомб" из опыта продакшена).
AI - молоток. Если им забивать шурупы, стена кривая. Генерация без контроля создает иллюзию покрытия, а не защиту. Будьте умнее: проверяйте, где AI слеп, и закрывайте дыры руками.
8 032
Анатомия парного доклада: как мы собирали «PROvoke» на Analyst Days. Часть 1 — подготовка
На прошлых Analyst Days авторы с Татьяной Маркиной зашли на территорию, где обычно стараются не светиться — тему манипуляций и провокаций в системной аналитике и IT. Они решили сделать парный доклад, чтобы показать не только результаты, но и сам процесс взаимодействия в аналитике на сцене.
Подготовка к такому выступлению потребовала особого подхода. Сначала была задумка сделать воркшоп о манипуляциях, но это было бы слишком сложно для формата конференции. Поэтому решили переделать в доклад с элементами интерактива. Весь путь от идеи до сцены занял около нескольких месяцев, включая несколько итераций согласования содержания.
Ключевым решением стал выбор сценария: решили строить выступление вокруг реального кейса, но с долей театрализации. Каждый из докладчиков взял на себя определенную роль, чтобы проиллюстрировать типичные конфликты в команде. Это позволило погрузить зрителей в атмосферу рабочей дискуссии, не скатываясь в сухую теорию. В итоге получился формат, который авторы назвали «PROvoke», балансирующий между обучением и развлечением.
Читать на Хабре
8 032
Resource-leak в async context propagation: детекция orphaned tracing spans при неполной очистке OpenTelemetry baggage
Baggage в OpenTelemetry — тихий убийца памяти в асинхронных средах. В высоконагруженных продакшн-системах неполная очистка контекста после завершения span приводит к орфан-спанам и утечкам, которые часто маскируются под необъяснимый OOM. QA и разработчики ошибочно фокусируются на самих трейсах, игнорируя корень — незакрытый baggage.
Как возникает утечка: асинхронный контекст без очистки
В Go или Kotlin Coroutines после завершения span baggage остаётся в контексте. Пример на Go:
func process(ctx context.Context) {
ctx, span := tracer.Start(ctx, "operation")
defer span.End()
baggage.Set(ctx, baggage.String("key", "value"))
// goroutine продолжает работать с контекстом
}
Span закрылся, но baggage живёт. Этот контекст кочует между горутинами, цепляя orphaned spans — трейсы без родителя. Они не закрываются, память растёт, trace tree превращается в кашу.
Четыре метода детекции
* Метрики: смотри на otel_span_count с status_code = Unset. Высокий счётчик — явный признак утечки.
* Логи: middleware после завершения scope проверяет, пустой ли baggage. Если нет — предупреждение в лог.
* Анализ trace tree: если span при ожидании не видит родителя с помощью Span.current(), он почти гарантированно orphaned.
* Алерты по baggage: отслеживай рост количества spans с одинаковыми ключами — триггер для баг-трекинга.
Практический совет: обязательная очистка
В Go: defer baggage.Clear(ctx) после завершения операции. В Kotlin Coroutines используй OpenTelemetryContextElement. В Java с CompletableFuture — явно чисти контекст перед каждым .thenApply. Это предотвращает утечки независимо от инструмента.
Типичная ошибка: игнорирование асинхронных блоков
Опасность не в падении одного сервиса. В high-load системе тысяча болтающихся контекстов через пару часов даёт OOM, а трейсы в дампе ничего не объясняют: spans есть, цепочек нет. Проверять надо все асинхронные блоки: goroutine, async/await, CompletableFuture, корутины. Если есть JVM-мониторинг — смотри jvm.memory.used в разрезе контекстов.
Для тестов полезна эмуляция контекста: запускаешь асинхронную операцию, ждёшь completion, проверяешь, что baggage чистый. Если нет — тест красный.
Вывод: В production-oriented QA первым делом проверяй очистку baggage в асинхронных цепочках — это дешевле, чем разбирать OOM с кашей из orphaned spans в дампе.8 032
Диагностика дрейфа библиотек шифрования в gRPC middlewares через слепую смену версий в production
Регресс на стейджинге — не всегда следствие плохого кода. Часто проблема скрыта в библиотеке шифрования, которая тихо поменяла версию: OpenSSL на BoringSSL или GnuTLS, а gRPC middleware перестал согласовывать cipher suites. Разработчики и QA это редко замечают, пока production не падает на handshake.
Почему это ломает gRPC
Шифрование в gRPC работает через middlewares — перехватчики всех запросов. Если подменить BoringSSL на GnuTLS, может измениться порядок cipher suites, отвалиться OCSP stapling или сломаться handshake для клиента с устаревшим proto. Пример из практики: после замены OpenSSL на BoringSSL на прокси Envoy упал весь трафик, так как новый порядок suites не поддерживали старые клиенты. Ни один CI регресс это не поймал — тесты запускались на том же стейджинге, без изоляции версий.
Пассивная диагностика
Добавьте в gRPC клиент interceptors для логирования версии библиотеки. Например, через переменную окружения
TLS_LIB_VERSION (если не задана — unknown). Это даёт картину при разборе ошибок. Но пассивное логирование не ловит дрейф до сбоя.
Активное тестирование с подменой
Запустите канареечный тест на малом проценте трафика: временно подмените OpenSSL на BoringSSL через подготовленный контейнер. Метрики для мониторинга:
* время handshake (допуск — не более 5% отклонения)
* количество ошибок UNAVAILABLE
* разница в cipher suites на прокси (HAProxy или Envoy)
В production мне так вскрылась несовместимость по OCSP, которую регресс не поймал: старый сертификат не проходил проверку на новом BoringSSL. Обязательно используйте feature flag для отката и логируйте sha256 бинарника библиотеки, чтобы точно знать, что подменили.
Типичная ошибка
Не верьте, что если регресс прошёл на стейджинге, то всё чисто. Без изоляции версий вы не заметите дрейф библиотек. Слепая смена — не проблема, если есть active monitoring. Но без неё вы просто молитесь, что handshake не сломается.
Вывод:
Активное тестирование с подменой библиотек и мониторинг метрик handshake — единственный способ поймать несовместимости шифрования, которые регресс пропускает до падения production.8 032
«Освоение модульного тестирования с использованием Pytest» курс на Stepik
Сегодня умение писать тесты ценится почти так же, как и умение писать сам код. Если pytest, fixtures, CI/CD и coverage всё ещё вызывают вопросы самое время это исправить
Программа курса:
• Pytest: от базовых тестов до CI/CD
• fixtures, mocking, parametrization
• Flask/API testing
• Selenium и UI тестирование
• Docker + Docker Compose
• GitHub Actions
• coverage и отчёты
• debugging и refactoring тестов
Курс построен вокруг практики: много примеров, готовых кейсов и разборов рабочих сценариев
48 часов действует скидка 25%
↗️ Пройти курс на Stepik
8 032
Retry-шторм как скрытая DDOS при cascading-отказах в async-оркестраторах
Ретри-логика кажется безобидной, пока не происходит каскадный отказ. В production я встречал ситуацию, где падение одного сервиса через саги превратилось в лавину ретраев, положившую всю инфраструктуру за 3 минуты. Ошибка в том, что разработчики и QA считают retry безопасным механизмом, не учитывая exponential growth нагрузки при одновременных вызовах.
Анатомия retry-шторма
Когда async-оркестратор (сага, workflow-движок) видит ошибку от downstream-сервиса, каждый экземпляр workflow запускает ретрай с экспоненциальной задержкой. При cascading-отказе, например падении БД, сотни оркестраторов одновременно выполняют шаги, каждый генерирует 3-5 ретраев. Итоговая нагрузка растет квадратично: 100 запросов в секунду превращаются в 10 тысяч ретраев за пару минут. Мониторинг показывает рост 5xx, но истинная причина — забитые очереди — не видна на графиках. Восстановление занимает часы вместо минут, как на реальном проекте после падения кластера БД.
Примитивный код-пример с проблемой
Допустим, в aiohttp воркфлоу вызывает сервис с ретраем:
async def call_service(session, url, retries=0):
try:
async with session.get(url) as resp:
resp.raise_for_status()
except ClientError:
if retries < MAX_RETRIES:
await asyncio.sleep(2 ** retries) # экспоненциальная задержка без jitter
return await call_service(session, url, retries + 1)
raise
Проблема: нет circuit breaker и rate limiter, jitter отсутствует. Экспоненциальная задержка без случайности даёт синхронные волны ретраев.
Практический совет: защита через три механизма
Добавьте circuit breaker, который отключает ретраи при ошибке сервиса. Используйте jitter для размазывания задержки:
import random
await asyncio.sleep((2 ** retries) + random.uniform(0, 0.5))
Также внедрите глобальный rate limiter на уровне оркестратора. После 2-3 ретраев сразу отправляйте запрос в dead-letter queue для ручного разбора.
Типичная ошибка: игнорирование cascading-отказов в тестах
Многие QA ограничиваются unit-тестами ретраев с изолированными вызовами. В реальном production retry-шторм возникает только при массовых отказах. Если у вас нет тестов, где симулируется падение БД с последующим восстановлением — вы не видите, как нагрузка расходится по очередям. Это скрытая DDOS, которая не выдаёт себя на стандартном мониторинге (CPU, memory, latency). Мой совет: заведите сценарий с cascading-отказом, используя инструменты вроде chaos engineering, и измеряйте количество ретраев в очередях. Источники: Martin Kleppmann, "Designing Data-Intensive Applications"; AWS Well-Architected Framework; GitHub Outage 2023.
Вывод: Retry-шторм — скрытая DDOS, требующая circuit breaker, jitter и rate limiting, иначе cascading-отказ в async-оркестраторах может привести к часам простоя.8 032
Метрика rogue-запросов: как поймать SQL-инъекцию по аномалии плана выполнения
Классические WAF и сигнатурки пропускают инъекции, когда атакующий вшивает UNION в конец легитимного WHERE, избегая типичных паттернов. В production такие запросы незаметны до момента утечки данных. Метрика аномалии плана выполнения ловит их по поведению оптимизатора, а не по тексту.
Почему план выполнения сбоит при инъекции
Нормальный запрос даёт стабильный план: Index Scan, Nested Loop, предсказуемый cost. Инъекция вводит нестандартные операторы (OR, UNION, комментарии), которые ломают оптимизатор. Появляются неожиданные Filter, SeqScan вместо IndexScan, или cost подскакивает в 10 раз. Это выглядит как выброс в статистической выборке.
Формула детекции rogue-запроса
Я считаю аномалию так:
Deviation = |Cost_actual - Median(Cost_history)| / IQR(Cost_history). Если значение больше 5 — это повод проверить запрос. Но cost — не единственный индикатор. Упрощённый пример на Python:
normal_plans = {"IndexScan->NestedLoop": 12.3}
rogue_plan = "SeqScan->Filter" # cost = 450.0
if rogue_plan not in normal_plans:
alert("Неизвестный тип плана — потенциальная инъекция")
else:
deviation = (450.0 - 12.4) / 0.3 # > 1000
if deviation > 5:
alert("Статистическая аномалия затрат")
Признаки аномалии и типичная ошибка
Главные признаки: смена типа сканирования (SeqScan вместо IndexScan), появление сортировки там, где её не было, резкий скачок estimated_rows в 100+ раз, необычный join (NestedLoop вместо HashJoin). Типичная ошибка: игнорировать ложные срабатывания при выкатке фичи или изменении данных. Без калибровки на контрольной выборке метрика завалит алертами.
Практический совет и trade-offs
Для production используй медиану и IQR — они устойчивы к кратковременным пикам. Cтоимость: хранение истории планов для каждого запроса и вычислительные затраты на анализ. Но trade-off оправдан: rogue-запросы выявляются до того, как инъекция выполнится. Не полагайся только на cost — сравнивай структуру plan tree, чтобы отсечь случайные колебания.
Вывод: Статистическая аномалия плана выполнения превращает оптимизатор базы данных в сенсор безопасности, ловящий инъекции по поведению, а не по тексту запроса.8 032
Архитектурная гниль в read-оптимизированных кэшах: детекция утечки старого бизнес-логика через stale projection в CQRS-потоках
Каждый, кто работал с CQRS, знает: проекции живут своей жизнью. Со временем read-модели превращаются в «кладбище полей» — хранят то, что уже нигде не используется бизнесом. Это не баг, а stale projection — классическая архитектурная гниль, когда кеш возвращает данные, не соответствующие актуальным правилам, что приводит к скрытым дефектам в production.
Как это выглядит в production
Когда команда меняет требования, а projection продолжает агрегировать данные по старым правилам, кеш честно отдает то, что накопили. Пример: событие
OrderShipped приходит в projection. В коде когда-то было поле IsDelayed — его вычисляли на основе expectedDeliveryDate и actualShipDate. Потом бизнес изменил метрики: задержка считается только после статуса «On Hold». Но projection продолжает считать IsDelayed по-старому.
func (p *OrderProjection) Handle(event OrderShipped) {
// Старая логика — уже не актуальна
p.IsDelayed = event.ShipDate.After(event.ExpectedDelivery)
// Новая логика — требует проверки статуса
// p.IsDelayed = event.ShipDate.After(event.ExpectedDelivery) && event.Status == "OnHold"
p.ShipmentId = event.ShipmentId
// ... остальные поля
}
Как детектировать гниль
* Мониторинг "мертвых полей" — заведите метрику, сколько раз поле из projection читается за период. Если 0 в течение двух недель — кандидат на удаление.
* Дата последнего изменения — храните updated_at как часть projection. Если проекция не обновлялась дольше N циклов бизнес-событий — она stale.
* Audit-лог использованных событий — проверяйте, какие события применяются к projection, а какие — нет. Если из 10 ивентов применяется только 3, остальные — legacy.
Типичная ошибка и практический совет
Ошибка: чистка проекции только при обнаружении бага в production, а не на этапе changelog. Совет: при каждом рефакториге бизнес-логики сразу обновляйте код обработчика и очищайте projection. Если бизнес позволяет простой, выполняйте тотальный пересчет (replay) после изменения логики.
Вывод: Stale projection — это не ошибка кеша, а сигнал о рассинхронизации между бизнес-требованиями и read-моделью, который можно предотвратить мониторингом использования полей и обязательным cleanup при changelog.8 032
Sleeping-read consistency gaps: детекция stale коммитов при пониженной изоляции read-committed в production
Работаете с read-committed и считаете, что это "достаточно безопасно"? Есть нюанс, который в production вылезает неочевидными гонками. Sleeping-read consistency gap.
Проблема stale чтений
Транзакция читает данные, потом делает паузу — sleep, внешний вызов API, что угодно. После пробуждения читает снова. Read-committed не обещает snapshot между двумя SELECT. Только что первый запрос выполнился на старых данных — второй ловит свежий коммит. Для тестировщика это значит: отчёт, собранный за два запроса, может быть неконсистентным. Проверили баланс, пошли списывать — а между ними пришёл внешний платёж. Бизнес-логика ломается, хотя СУБД формально права.
Методы детекции
Как ловить в production?
* Логируйте snapshot age. В PostgreSQL — pg_current_snapshot() до и после паузы. Если снапшоты разные — gap есть.
* Реперные точки. Вставляйте timestamp начала транзакции. Если после паузы читаете строки с меткой времени больше старта — это stale-чтение.
* SQL + Python для воспроизведения:
import psycopg2, time
conn = psycopg2.connect("...")
conn.autocommit = False
cur = conn.cursor()
cur.execute("SELECT COUNT(*) FROM orders")
cnt1 = cur.fetchone()
time.sleep(2)
cur.execute("SELECT COUNT(*) FROM orders")
cnt2 = cur.fetchone()
print(cnt1, cnt2) # разные — gap
Типичная ошибка
Многие QA прогоняют паузы только в идеальных условиях. В реальности задержки от внешних API или очередей могут превышать 100 мс и гарантированно вызывать stale чтения. Проверяйте с искусственными задержками — имитируйте реальное поведение, а не идеальное окружение.
Защита в production
Для транзакций с паузами переходите на REPEATABLE READ или SERIALIZABLE. Вставляйте оптимистичные блокировки, проверяйте версии строк. Это не баг PostgreSQL. Это контракт read-committed. И QA нужно проверять, что приложение терпит изменения данных между чтениями в одной транзакции. Иначе в проде начинается "а почему у нас баланс разъехался".
Вывод:
Sleeping-read gap — это не ошибка изоляции, а инженерный компромисс между производительностью и консистентностью, который QA обязано детектировать через тесты с реальными задержками и переходом на уровень repeatable read для критичных операций.8 032
Non-idempotent retry poisoning: как повторная обработка успешных Kafka-сообщений плодит дубликаты в production
Классическая ситуация: consumer упал после того, как обработал сообщение, но до коммита offset. Kafka переотправляет то же сообщение при ребалансе. Если обработка не идемпотентна, каждый retry создаёт дублирующие side-effect записи — в БД, внешних API, логах. Это retry poisoning, который часто остаётся незамеченным до инцидента.
Как это выглядит в production
Обычный pipeline:
1. Получить сообщение.
2. Выполнить бизнес-логику — списать деньги, создать заказ, отправить письмо.
3. Записать результат в БД.
4. Закоммитить offset.
Крах на шаге 2 или 3 — offset не зафиксирован. После рестарта consumer получает то же сообщение снова. Если side-effect записи не имеют уникальных ключей — появляются дубли:
* Один и тот же заказ с разными order_id.
* Двойное списание с одного счёта.
* Повторная отправка email или SMS.
Как выявить дубликаты
Метрики и мониторинг:
*
consumer_lag резко падает, но processed_messages растёт быстрее committed_offsets.
* Искать записи с одинаковым businessKey, но разными id в базе.
* Логи: повторная обработка сообщения с тем же partition/offset в течение короткого окна.
Практический совет: идемпотентный ключ
Храните уникальный event_id в сообщении. При обработке:
INSERT INTO orders (event_id, data)
VALUES (:event_id, :data)
ON CONFLICT (event_id) DO NOTHING;
Или сначала проверяйте:
SELECT 1 FROM outbox WHERE event_id = :id;
Это дешевле повторного вызова внешнего API.
Типичная ошибка
Использовать idempotent producer на стороне Kafka и думать, что этого достаточно. Он гарантирует, что сообщение не запишется дважды в partition, но не защищает от повторной обработки одного сообщения после краша consumer. Идемпотентность должна быть на уровне consumer-логики.
Trade-off: скорость vs надежность
Автокоммит enable.auto.commit=true или ручной коммит до выполнения логики ускоряет throughput, но увеличивает риск потери данных при краше. Выбирать между потерей и дублированием — риск-ориентированное решение. Для финансовых транзакций дубли критичнее.
Вывод:
Retry poisoning — не баг в коде, а архитектурная дыра, которая лечится только внедрением идемпотентности на уровне consumer с уникальными идентификаторами и upsert-операциями.8 032
Детекция split-brain в сессионной аффинности при частичном отказе WebSocket-шлюзов
Коллеги, разберем кейс, который многие QA видели в логах, но не всегда могут воспроизвести в тестовой среде: частичный отказ WebSocket-шлюзов и расщепление сессий. Эта ситуация критична для real-time коммуникаций, где балансировщик переключает клиента на новый шлюз без синхронизации состояния.
Проблема: потеря контекста сессии
При отказе одного шлюза балансировщик переключает клиента на другой. Если session affinity настроена жестко по IP или хэшу, новый шлюз может не иметь состояния сессии. Клиент остается в полуживом состоянии: WebSocket сломан, HTTP-запросы проходят, но данные расходятся. Это классический split-brain, который сложно отловить в production без специальной детекции.
Техника: health-check с версионностью и gateway ID
Добавьте в каждый шлюз заголовок
X-Gateway-Sequence при WebSocket-рукопожатии. Клиент хранит sequence и gateway ID, а при смене шлюза отправляет RECONNECT с force: true. Дополните сессионный heartbeat: ping/pong содержит ID текущего шлюза. Если клиент получает ответ от другого шлюза без предупреждения, это триггер split-brain. Пример кода на Python:
class GatewayAwareWebSocket:
def __init__(self, url):
self.current_gateway_id = None
self.session_token = str(uuid.uuid4())
async def on_health_check(self, message):
if self.current_gateway_id is None:
self.current_gateway_id = message['gateway_id']
return
if message['gateway_id'] != self.current_gateway_id:
await self.resync_session(message['gateway_id'])
async def resync_session(self, new_gateway_id):
log.warning(f"Split-brain: {self.current_gateway_id} -> {new_gateway_id}")
response = await http_client.post(
"/session/resync",
json={"session_token": self.session_token}
)
if response.status == 200:
self.current_gateway_id = new_gateway_id
Как тестировать: симуляция отказа шлюза
Используйте Docker Compose с разными gateway_id. Выключите один шлюз (docker stop) во время активного WebSocket-соединения. Проверьте, что клиент детектирует смену gateway_id и вызывает resync_session. После рестарта старого шлюза клиент не должен переключаться обратно — sticky session должна оставаться стабильной. Типичная ошибка: health-check успешен, но состояние устарело. Решение: добавьте в health-check поле session_valid: boolean для проверки актуальности.
Торговля инженерными рисками
Split-brain в WebSocket — не баг, а архитектурная особенность при масштабировании. Быстрое обнаружение через идентификацию шлюза снижает время восстановления до миллисекунд, но добавляет complexity в клиентскую логику. Убедитесь, что resync_session не вызывает race conditions при параллельных вызовах. Для экономии ресурсов можно использовать кумулятивные heartbeat с флагами вместо полной синхронизации.
Вывод: Детекция split-brain через gateway ID и версионированный health-check позволяет предотвратить расхождение сессий за миллисекунды, что критично для любой real-time архитектуры с сессионной аффинностью.
¡Ya disponible! Investigación de Telegram 2025 — los principales insights del año 
