У любого процесса есть своё бутылочное горлышко. Если разобрать его на одной стадии процесса, оно непременно появится на другой. Но одно бутылочное горлышко может соответствовать максимальной эффективности команды, а другое будет тормозить поставку вашей ценности заказчику. При этом заранее угадать на старте проекта, где будет ваше бутылочное горлышко — очень сложно.
Мы расскажем, как искали путь выхода из подобной ситуации. Почему при достаточном соотношении QA и DEV-инженеров в команде у нас сформировалось бутылочное горлышко на стадии тестирования. Как обсуждения пирамиды тестирования и распределения зон ответственности за качество продукта помогли избавиться от недоверия в команде и ускорить поставку ценности.

Будет полезно тем, кто ищет реальные кейсы по оптимизации процессов тестирования и хочет узнать, как построить работающий QA-процесс в условиях ограниченных ресурсов и быстро меняющихся требований.

В 2024 году наша команда приступила к изменению функционала системы раннего выявления проблем у корпоративных клиентов. Ядром этой системы является понятие сигнала. То есть, отражение в системе события, которое показывает, что у клиента могут возникнуть проблемы с обслуживанием долга. Например, информация о том, что компания пропустила платёж по кредитному договору или против нашего клиента возбудили арбитражное дело. Таких сигналов много и именно с их разнообразием связана основная сложность системы. Но сигналы позволяют запустить процессы внутри системы, с помощью которых можно минимизировать риск получения убытков связанных с обслуживанием клиента.

Мы изменяли функционал системы в рамках импортозамещения. Потому что использование продуктов Oracle в России приостановлено, а преобразование событий в системе осуществлялось ETL-инструментом Oracle Data Integrator (ODI): с помощью SQL-запросов выполнялся сбор данных из внешних источников, преобразование в единый реляционный формат и загрузка в корпоративную CRM. Там проводилась основная работа с сигналами: анализ, оценка рисков и принятие решений об изменении порядка взаимодействия с клиентом.

Верхнеуровневая постановка задачи

Перед нами стояли две основные задачи:

  • Реализовать механизм, который заменил бы ETL-преобразования с упором на потоковые источники данных (такие как очереди Artemis и Kafka), а также обеспечить расширяемость для добавления новых источников в режиме request/response (REST, SQL, Artemis).

  • Реализовать интерфейс отображения сигнала с его сложной динамической структурой.
    Например сигнал о том, что клиент просрочил платёж в банк может содержать сумму просроченного платежа(тело кредита, проценты, пени и пр.), а сигнал о том, что клиент нарушил условие договора содержит информацию о самом соглашении, и периоде, когда это нарушение произошло. Есть и более сложные сигналы, когда сигнал состоит из множества внешних подсигналов, которые мы называем детализациями.

В рамках этих задач планировалось запустить несколько сигналов через новый инструментарий в старую систему, а также настроить получение данных из старой системы в новую, чтобы оценить возможности обработки всего потока данных и отображения «богатого мира» динамической структуры сигналов.

Software архитектура

Новую систему мы назвали FERNS и в рамках реализации первой версии разделили на два модуля:

  • Event Pipeline — безинтерфейсный модуль по преобразованию произвольного события в единый формат. Задачи модуля:

    • На основании конфигурации подключиться к нужному источнику.

    • Обогатить исходное событие дополнительными данными (например, найти внутренний идентификатор компании в банке, получив от внешней системы только ИНН). В первой версии обогащение происходит только через обращение к системам справочникам по REST.

    • Сформировать объект ProtoSignal, который содержал бы все необходимые поля, которые необходимы для последующего формирования сигнала и его последующей обработки.

  • Signal Holder - Модуль хранения и отображения сигналов:

    • Получает ProtoSignal.

    • По собственным правилам формирует из ProtoSignal сигнал и/или детализацию.

    • Отвечает за правила отображения уникального набора полей для конкретного сигнала.

Мы хотели написать LOW-код систему, которая с помощью конфигурационных файлов подключается к внешним источникам, по описанным правилам обогащает и преобразовывает события в модуле Even Pipeline, а также на базе конфигураций создаёт/обновляет сигналы и отображает их.

Процессы команды на старте

На старте проекта в команде было 3 backend-разработчика, один frontend-разработчик, три QA-инженера и один аналитик.  При этом, фактически, до ноября команда жила с двумя QA-инженерами.

У нас было два уровня тестирования E2E и Unit-тестирование, которые никак не были связаны друг с другом. Unit-тестирование должно было покрывать не менее 80% кода. При этом основой обеспечения качества были именно E2E тесты.

Процесс работы над задачей в тот момент выглядел следующим образом:

  • после декомпозиции задачи по ней проводилась встреча 3-амиго

  • разработка

  • тестирование

На встрече 3-амиго определялись критерии приёмки конкретной задачи, включая обсуждение объёма E2E тестирования. У нас оно полностью автоматизированное, в силу специфики продукта (мало интерфейса, много логики на интеграциях, логика событий динамическая и зависит от конфигураций).

Затор в самом начале

Разработка нового сервиса началась во втором квартале 2024 года, но к середине третьего квартала возник затор на стадии «готово к тестированию». У нас скопилось 24 задачи на стадии «готово к тестированию».

На тот момент мы уже понимали пропускную способность команды. За 2 недели команда в среднем закрывала 12-13 задач но с большим разбросом.

Нам казалось, что причина была не только в некомплекте QA-инженеров, в команде не хватало одного положенного по штату, и корень проблемы скрыт в особенностях организации процессов команды.

Меняем процессы первый раз

Решение проблемы мы начали с увеличения вовлеченности DEV-инженеров в обеспечение качества. Данное решение принималось командой достаточно тяжело, поскольку некоторые DEV-инженеры пришли к нам из-за того что на предыдущем месте работы «было слишком много тестирования». Мы провели несколько встреч, на которых мы договорились:

1. Как выглядит пирамида тестирования в нашем случае.

  • unit-тест проверяет один публичный метод класса, проверяя корректность вызовов внешних методов и результат работы этого метода при мокировании поведения других классов/интерфейсов — ответственность DEV-инженеров.

  • Компонентный тест проверяет работу сценария на бекенде, не используя интеграционное взаимодействие. В нашем случае это подразумевало, что поднимается полный или частичный Spring контекст, который проверял совместную работу нескольких связанных классов, при этом обращение во внешние системы, если они были, за исключением БД, мокируются на уровне классов вызова внешней системы — ответственность DEV-инженеров.

  • Интеграционный тест проверяет работу бекенда, проверяя работу интеграционных потоков, на этом этапе внешние системы мокируются на уровне мокирования внешней системы — ответственность QA-инженеров.

  • UI-тест проверяет корректность работы интерфейса системы — ответственность QA-инженеров.

2. Обсуждать распределение тест-кейсов между всеми уровнями пирамиды тестирования на встречах 3-амиго. 

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

Идея состояла в том, что формальное введение компонентных тестов, должно снизить нагрузку на QA-инженеров в части проверки вариативности тяжёлых тестов и починку тестов, падение которых не связано с изменением функциональных требований, и повысить вовлеченность DEV-инженеров в процесс обеспечения качества продукта.

После введения этих изменений QA инженеры отметили, что количество тестов на их уровне сократилось на 3/4. И, казалось, что надо ещё одну две итерации, и тренд на увеличение затора будет переломлен и пойдёт на снижение, но цифры в трекере говорили нам об обратном. К середине 4-го квартала мы подошли со следующей статистикой.

Задач на этапе тестирования:

  • Готово к тестированию = 37

  • В тестировании = 22

  • Ревью тестов = 14

  • Итого = 73

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

Стало понятно, что процессы надо продолжать менять, но для этого сначала было нужно уточнить требования к написанию UI-тестов. Это совпало с выходом в команду ещё одного QA-инженера.

Второй заход на изменение процессов

Обсуждая сложившуюся ситуацию мы выяснили несколько моментов:

  1. Первые UI-тесты только проходили ревью и вызвали большие обсуждения, как именно реализовывать их UI-часть.

  2. Мы продолжаем писать UI и/или интеграционные тесты, хотя бы один кейс на задачу., даже если по задаче написаны интеграционные тесты. Часть QA-инженеров считала что это необходимо, потому что нет гарантии, что DEV-инженеры смогут корректно покрыть функционал на уровне компонентных тестов.

Написание первого такого теста требовало больше всего времени: нужно было сделать обвязку для теста; новые шаги и код для новых интеграций.

Предыдущие договорённости были расширены и дополнены:

  1. Всё что можно тестировать на более низком уровне пирамиды, мы тестируем там и не дублируем проверки на более высоком уровне пирамиды.

  2. Верификацию корректности покрытия компонентными тестами реализовали через ревью новых тестов в Allure TestOps. Делая MR в мастер-ветку, разработчик был обязан получить согласование от QA-инженеров, что компонентные тесты полностью охватывают все классы эквивалентности и проверяют все необходимые граничные условия.

  3. Задачи, которые ещё не дошли до стадий тестирования идут по новым требованиям. Задачи, которые уже дошли до стадий тестирования, обрабатываются по старым договорённостям.

Мы исходили из того, что реализация задачи, которая должна в рамках конфигурации предоставлять возможность делать, например, ветвление или сложение, может быть покрыта компонентным тестом, в рамках которого мы на вход подаём конфигурацию и входной объект, а тест проверяет корректность произведённых преобразований, не требует работы приложения, и написания кода, который проверяет эти вычисления в рамках интеграционных или UI тестов, и может быть проверен в рамках компонентного теста, когда поднимается ограниченный контекст приложения, и входной объект подаётся на вход в метод, а не через интеграцию (например по очереди).

Для более фокусной работы на этом направлении, мы, начиная с 15 декабря, выделили на планировании и на дейли отдельный интервал времени, что бы обсудить фокус разгребания хвоста тестирования. Мы не стали делать фокус «разобрать завал» единственным. Мы продолжили брать новые задачи в разработку с максимально возможной скоростью.
Первые итерации декабря-января не давали видимого эффекта из-за договорённости делать в старой парадигме задачи уже находящиеся на стадиях тестирования. Но к началу марта количество задач на всех стадиях тестирования стало меньше, чем в самом начале в стадии «готово к тестированию», и мы сочли возможным убрать фокус «разобрать завал» из планирования и дейли. В результате, со второй попытки, за 3 месяца, включая новогодние праздники, нам удалось полностью ликвидировать весь «хвост». Ниже табличка движения команды по двухнедельным интервалам:

Дата

Готово к тестированию

В тестировании

Ревью тестов

Итого

13.12

42

8

27

77

27.12

53

7

3

63

17.01

48

4

3

55

31.01

35

6

7

48

14.02

26

10

5

41

28.02

14

4

11

29

05.03

12

3

8

23

При этом, в последние 4 итерации, скорость поставки задач выросла в несколько раз.

Результаты и выводы

Может показаться, что эффект дала работа нового QA-инженера, который вышел к нам в начале ноября, но это не так. В середине марта один QA-инженер сменил специализацию и перешёл в разработку. Поэтому в команде снова было 2 QA-инженера, а количество задач на стадиях от «готово к тестированию» до «ревью тестов» стабильно находится в небольшом диапазоне около 15 задач. При этом пропускная способность команды выросла до ~15 - 40 задач за две недели, без изменения процесса декомпозиции задач.

Таким образом, нам удалось расширить узкое горлышко на этапе тестирования. Если раньше команда обрабатывала ~12 задач за две недели, то теперь этот показатель вырос до ~22. При этом количество задач на этапах тестирования снизилось с ~30 до ~15 и стабилизировалось на этом уровне.

В качестве заключения

Не смотря на то, что в нашем случае бутылочным горлышком были стадии на этапе тестирования, наш подход можно применить в любом процессе. Вашими помощниками должны стать:

  • прозрачность вашего процесса и понятные, измеримые метрики,

  • желание команды меняться,

  • и классическое: не бояться пробовать и оценивать результаты изменений.

В нашем случае для достижения полученных результатов мы:

  • зафиксировали уровни пирамиды тестирования для нашего проекта;

  • чётко закрепили ответственность за разные уровни пирамиды между QA и DEV инженерами;

  • определили, что достаточность покрытия тестами функционала при MR проверяется QA-инженерами через Allure TestOps;

  • договорились доверять друг другу и не дублировать тесты;

  • сделали основной точкой взаимодействия инженеров и фиксации распределения тестирования встречи «3-амиго».

Сейчас наша пирамида тестирования выглядит почти как в учебниках, и команда успешно справляется с задачами без прежних задержек на этапе тестирования.

Комментарии (2)


  1. onets
    14.07.2025 12:28

    Так, в кратце-то - что вы сделали? Переложили написание части тестов на программистов?

    Как решили вопрос с программистами, кто был против тестирования? Уволили/уволились?


    1. gubber Автор
      14.07.2025 12:28

      Если коротко:

      1. формализовали пирамиду тестирования

      2. расширили зону ответственности за написания тестов у разработчиков, да разработчики стали писать больше тестов,

      3. Убрали дублирование тестов на разных уровнях

      Эта ситуация показала уровень зрелости команды, когда все члены команды поняли проблему, приняли её и совместно решили. Кадровых изменений в команде в связи с этими изменениями не было.