
Пропуск тестирования кажется экономией времени, пока баг не попадёт в продакшен. Разберём, во что это обходится и как выстроить QA под масштаб бизнеса.
Это одно из самых распространённых решений в software-проектах — и одно из самых дорогих: срок сдачи приближается, функция вроде бы работает, если проверить её самому, и тестирование откладывается на "потом". "Потом" часто означает уже после запуска, когда баги находит не QA-инженер, а платящий клиент. К этому моменту исправление обходится намного дороже, чем несколькими неделями ранее — в часах разработки, в нагрузке на поддержку и в доверии пользователей.
Это не призыв тестировать всё исчерпывающе перед каждым релизом. Большинство растущих компаний не могут себе этого позволить, да и не нуждаются в этом. Это разговор о том, что на самом деле стоит пропуск тестирования, где команды чаще всего срезают углы, и как выглядит стратегия тестирования, соразмерная реальному масштабу бизнеса, а не скопированная у гораздо более крупной организации. Речь идёт не о выборе между "тестировать" и "не тестировать", а о том, чтобы направить ограниченное время команды туда, где риск ошибки обходится дороже всего.
У каждого дефекта есть кривая стоимости, и она становится всё круче, чем позже проблема обнаружена. Баг, найденный, пока разработчик ещё пишет код, исправляется за несколько минут: контекст свежий, изменение небольшое, и ничего другого от него ещё не зависит. Тот же баг, найденный при код-ревью, стоит чуть дороже: другому человеку сначала нужно разобраться в контексте, прежде чем исправлять. Обнаруженный в тестовой среде перед релизом, он обходится ещё дороже: кто-то должен его воспроизвести, локализовать и проверить исправление, не мешая другой работе, уже идущей параллельно.
Если же баг обнаружен уже в продакшене, меняется сама категория затрат. Теперь это не просто исправление кода — это тикеты в поддержку, спешный хотфикс под давлением, откат, который может сломать что-то ещё, а в некоторых случаях — реальный финансовый или репутационный ущерб. Данные, возможно, уже были созданы или повреждены из-за ошибочной логики, а значит, исправление должно учитывать и уборку последствий, а не только предотвращение проблемы в будущем. То, что было бы правкой на пять минут, превращается в инцидент на несколько дней с участием людей, которые не планировали провести неделю именно так.
Издержки недостаточного тестирования редко проявляются как отдельная строка в бюджете — именно поэтому их легко недооценить. Они проявляются как часы разработчиков, потраченные на тушение пожаров вместо создания новых функций. Они проявляются как тикеты в поддержку, которых можно было избежать, и каждый из них требует, чтобы человек его прочитал, воспроизвёл и, возможно, эскалировал. Они проявляются как клиенты, которые тихо перестают пользоваться функцией — или продуктом целиком — после того как дважды столкнулись с одной и той же проблемой.
Есть и аспект безопасности, который легко упустить из виду. Пропуск тестирования редко означает пропуск только функциональных проверок; часто это означает пропуск менее приятной работы — тестирования граничных случаев, неожиданных входных данных и путей отказа, а именно в этих местах чаще всего и скрываются уязвимости. Функция, которая "работает" на основном сценарии, всё равно может неправильно обрабатывать права доступа, раскрывать данные, которые не должна раскрывать, или отказывать таким образом, что этим можно воспользоваться со злым умыслом. Ничего из этого не видно на демонстрации. Рано или поздно видно становится всё.
В командах, которые не халатны намеренно, а просто работают под давлением сроков и без чёткой стратегии тестирования, снова и снова повторяется несколько паттернов.
Тестирование только в конце, отдельным этапом. Когда тестирование сжимается до последних дней перед релизом, не остаётся времени исправить найденное — поэтому проблемы понижают до статуса "известных" и всё равно выпускают в релиз.
Тестируется только основной сценарий. Естественно тестировать тот сценарий, для которого функция создавалась. Гораздо реже, без сознательного усилия, проверяют, что происходит, когда пользователь вводит неожиданные данные, теряет соединение посреди действия или делает что-то в порядке, которого никто не предусмотрел.
Отсутствие защитной сетки от регрессий. Без набора тестов, автоматически перепроверяющих существующую функциональность, каждый новый релиз — это ставка на то, что ничего важного не сломалось. Команда узнаёт об обратном, когда об этом сообщает клиент.
Ручное тестирование, которое не масштабируется. У ручного тестирования есть реальная ценность, особенно для исследовательской проверки новых функций, но полагаться только на него означает, что усилия по тестированию не растут вместе с продуктом — они становятся всё более разреженными по мере роста кода, даже если старательность команды не меняется.
Тестовые окружения, не похожие на продакшен. Код, который проходит на ноутбуке разработчика, но ведёт себя иначе при реальных объёмах данных, реальных сетевых условиях или реальных интеграциях с третьими сторонами, создаёт ложное чувство уверенности.
Цель — не 100%-ное покрытие тестами: это нереалистично и не лучшее применение времени небольшой команды. Цель — приоритизация на основе риска: понимать, какие части продукта нанесут наибольший вред в случае поломки, и защищать их в первую очередь.
Для большинства компаний это означает автоматизированные регрессионные тесты вокруг сценариев, которые приносят выручку или обрабатывают чувствительные данные — оформление заказа, аутентификация, биллинг, ключевые процессы, от которых клиенты зависят ежедневно. Именно здесь тихая регрессия обходится дороже всего, а автоматизация позволяет проверять эти области при каждом релизе без затрат ручных часов каждый раз.
Это означает, что ручное, исследовательское тестирование оставляют для действительно новых или сложных функций, где человеческое суждение о том, "что попробовал бы сделать реальный пользователь", ценнее сценарной проверки. И это означает, что тестовые окружения держат достаточно близкими к продакшену — по структуре данных, конфигурации и масштабу, — чтобы прохождение тестов в staging действительно что-то значило.
Ничто из этого не требует большого выделенного отдела QA. Требуется осознанное решение о том, куда направить усилия по тестированию, принятое один раз и пересматриваемое по мере роста продукта, а не оставленное на волю случая перед каждым новым дедлайном.
Самые устойчивые команды не относятся к тестированию как к контрольному пункту в конце разработки — они делают его частью того, как выполняется работа. Разработчики пишут unit-тесты как часть завершения функции, а не как необязательное дополнение. Код-ревью включает вопрос "что может сломаться", а не только "компилируется ли код". Пайплайн непрерывной интеграции автоматически запускает набор тестов при каждом изменении, находя регрессии ещё до того, как код увидит человек.
Этот сдвиг — часто называемый "shift left", потому что тестирование перемещается раньше в процессе, — обычно оказывается дешевле и быстрее, чем звучит, потому что заменяет дорогое тушение пожаров на позднем этапе дешёвыми проверками на раннем. Он также создаёт полезную обратную связь: когда что-то действительно ломается в продакшене, исправление сопровождается новым тестом, гарантирующим, что та же проблема незаметно не вернётся, — так защитная сетка со временем становится прочнее, а не остаётся статичной.
Несколько честных вопросов раскрывают больше, чем любой аудит. Сколько из багов, о которых сообщают клиенты, тест мог бы правдоподобно поймать заранее? Как часто за релизом в течение нескольких дней следует хотфикс? Сколько времени проходит, прежде чем замечают, что что-то сломалось, — часы, или об этом должен сообщить клиент? Существует ли растущий набор автоматизированных тестов, запускаемый при каждом изменении, или "тестирование" означает, что кто-то кликает по приложению перед релизом?
Если вы работаете с внешним партнёром по разработке, те же вопросы применимы и к нему. Партнёр, который относится к тестированию как к полноценной части инженерного процесса, а не как к идее, добавленной, если останется время, обычно способен конкретно описать свой подход: что автоматизируется, что тестируется вручную и почему, и как регрессии отлавливаются раньше, чем их заметят клиенты.
Пропуск тестов не экономит время — он берёт время в долг у будущего, обычно с процентами. Компании, у которых это получается хорошо, — не те, у кого самые большие отделы QA, а те, кто принял чёткое, основанное на риске решение о том, где усилия по тестированию важнее всего, встроил его в свой рабочий процесс, а не добавил в самом конце, и продолжает корректировать его по мере роста продукта. Это гораздо более достижимая цель, чем "тестировать всё", и именно она по-настоящему защищает и продукт, и людей, которые на него полагаются. Начать можно с малого: выбрать один критически важный сценарий, покрыть его автоматическими тестами, и постепенно расширять эту практику по мере того как растёт сам продукт.
Explore more from Software Development