Thursday, April 18, 2013

Стоит ли проверять свои изменения перед комитом? Задаем вопрос к сети Байеса.


Предисловие.

На этот пост меня вдохновила профессор Дафна Коллер (Daphne Koller), которая за первую неделю курса Probabilistic Graphical Models расширила мое мировозрение простой, понятной и довольно эффективной моделью Байесовских сетей (Bayesian Network).


Цель исследования.

Дело в том, что я давненько заметил за собой тенденцию отдавать свои изменения тестировщику без интеграционного тестирования. Слишком уж доверяю юнит тестам, и, к тому же, что греха таить, ленюсь лишний раз запустить приложение :). Обоснованность такого решения была основана исключительно на интуиции, которую я решил проверить с помощью новоприобритенных знаний.

Ожидаемый результат.

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

Построение модели.

План такой (жутко абстрактный:)).
Прежде всего надо определить что я хочу измерить, потом  определить зависимости между измеряемыми элементами, потом на основании этих знаний построить модель.

Итак, я могу влиять на решение проверять или не проверять приложение перед комитом.

Что зависит от моего решения делать проверку или нет? 

  1. Прежде всего, если я не проверю свои изменения, то логично предположить, что увеличится вероятность того, что тестировщик попросту "переоткроет багу" (Reopen *). То есть изменения, которые я отдал на тестирование не устранили проблему (если же речь идет о новой фиче, то это адекватно тому, что новую функциональность тестировщик не увидел).
  2. Возможно, что тестировщик обнаружит, что начальная проблема решена, но появились новые проблемы (Introduced new issues). То есть вместе с фиксом я наплодил кучу новых проблем.
  3. На что влияют эти 2 потенциальные проблемы? Рискну предположить, что они влияют на качество системы (Quality).
* Амсори, но дальше в описании я нечаянно подменил Reopen на Reproduce.

Исходя из вышеизложенных умозаключений в стиле КО определим переменные модели.
  • Check (C). Это мое решение делать проверку или нет
  • Reopen (R). Это вероятность того, что тестировщик "переоткроет" баг.
  • Introduced new issue (I). Это вероятность того, что тестировщик найдет новые проблемы, связанные с моими изменениями.
  • Quality (Q). Качество системы
Вот и зависимости:
Мое решение делать проверку (C) влияет на вероятность того, что тестировщик переоткроет баг (R) и на то, обнаружит ли он новые проблемы (I). Обе переменные (R и I) влияют на качество системы (Q).

Изобразим это в виде диаграммы зависимостей.
Рис 1. Сеть Байеса.
Это и есть сеть Байеса. Я построил ее с помощью специальной программы SamIam.

Установка значений модели.

Что можно сделать с помощью полученной сети? Один из основных вариантов использования - это анализ того, как переменные влияют друг на друга. 

Например, я хочу посмотреть, как мое решение проверить мой фикс (C) влияет на качество (Q) через вероятность переоткрытия (R) и внесения новых багов (I).
Для этого необходимо определить с какой вероятностью тестировщик переоткроет багу или найдет новые проблемы в зависимости от того, проверю я систему или нет.

КО подсказывает простые правила:

  1. Если я перед комитом проверю приложение, то вероятность переоткрытия (R) очень маленькая. Вероятность внесения новых проблем (I) в этом случае тоже невысока.
  2. Если я перед комитом поленюсь делать проверку, то вероятности появления событий R и I будут чуть выше. Но поскольку я доверяю юнит тестам они тоже будут небольшими.

Изобразим вероятности событий в виде таблиц.

Таблица 1Вероятность возникновения R при фиксированном C
Таблица 2. Вероятность возникновения I при фиксированном C
В таблице представлены бинарные переменные. Если значение переменной 1, то в колонке Probability указана вероятность того, что событие произойдет. Если 0, то вероятность того, что события не будет.

Например, вот как следует читать первую строчку таблицы #1:
Если проверить систему перед комитом (колонка C=1), то баг переоткроется с вероятностью 10% (R=1, Probability=0.1). 
Значения я брал из головы, исходя из своего опыта и субьективных предположений. Никаких исследований на этот счет не проводилось :). Как видишь, я довольно оптимистично отношусь к фиксам без дополнительных проверок, хотя до оптимизма Чака Норриса еще далековато.

Теперь внесем эти значения в модель (с помощью той же программы SamIam)
Рис 2. Значения переменных.
На первый взгляд картинка кажется слегка сложной, но думаю после пояснений будет понятнее что на ней изображено:
  1. Значения переменной C (проверка приложения). В 30% случаях я проверяю систему, в 70% - отправляю тестировщикам без проверки. Значение Yes=0.3 соответствует 30%-й вероятности проверки перед комитом. Также No=0.7 соответствует 70%-й "безответственности" :).
  2. Значения переменной R.
    1. Если C=Yes (если я проверяю приложение перед комитом), то в 10% бага репродюсится, в 90% - нет (значения в Yes-Reproduced и Yes-NotReproduced соответственно
    2. Если C=No (если я ленюсь выполнить проверку), то в 20% бага репродюсится, в 80% - нет (значения в No-Reproduced и No-NotReproduced соответственно).
  3. Значения переменной I
    1. Если C=Yes, то в 20% случаев тестировщик найдет новые проблемы в 80% - нет (значения в Yes-Introduced и Yes-NotIntroduced соответственно).
    2. Если C=No, то в 40% случаев тестировщик найдет новые проблемы в 60% - нет (No-Introduced / No-NotIntroduced соответственно)
Значения переменной Q (качество приложения) я вынес в отдельный рисунок, т.к. из-за того, что эта переменная зависит от 2-х других, в ней больше вариантов значений:
Рис 3. Значения переменных-2. Значения Q.

Пояснения значений качества (Q)
  1. Если багу переоткрыли (R=Reproduced), И
    1. Если к тому же еще тестировщик нашел новые проблемы (I=Introduced), то в 1% случаев можно сказать, что качество хорошее, в 99% - нет
    2. Если новых проблем не найдено (I=NotIntroduced), то качество хорошее в 40% случаев, плохое в 60%
  2. Если багу не переоткрыли (R=NotReproduced), И
    1. Тестировщик нашел новые проблемы (I=Introduced), то с вероятностью 0.2 система хорошего качества, соответственно 0.8 - плохого
    2. Если тестер новых проблем не обнаружил, то в 99% качество хорошее и 1% - плохое

Запросы к модели.

Отступление. 
Я заранее хочу предупредить, что здесь не будет дано обьяснение того, как именно происходит "магия" и по каким принципам переменные влияют друг на друга. Это тема отдельного поста, а ищущие могут найти объяснения в первых же лекциях курса Probabilistic Graphical Models

Теперь самое интересное. Перейдем в Query mode и посмотрим на то, как повлияли зависимости между переменными на их значения

Событий не зафиксировано

Рис 4. Никаких событий не наблюдается.
Мы видим вероятностые значения переменных если не наблюдается никаких событий. То есть это состояние, когда неизвестно, есть ли в системе переоткрытые или новые баги и я никому не говорю, проверяю я свой код перед комитом или нет.
Чуть детальнее:

  • Я продолжаю делать проверку в 30% случаев. При этом
    • Вероятность того, что я своими изменениями внесу новые баги 34%. I приймет значение  Introduced в 34 комитах из 100 *
    • Вероятность того, что тестировщик переоткроет багу после комита 17%. R станет равным 1 в 17 комитах *
    • При таких раскладах хорошее качество системы можно наблюдать в 64.42% случаев

* Ты верно обратил внимание на значения для переменных I и R (34-60 и 17-83). Незнание значения C делает эти узлы зависимыми. На интуитивном уровне я это понимаю так : если после комита появились новые баги, то, возможно это повлияет на вероятность переоткрытия старых через то, что, возможно кто-то просто забыл провести интеграционный тест.

Дальше с помощью этой модели можно попытаться ответить на следующие вопросы

Как изменится качество системы, если я никогда не буду тестировать свои  изменения перед тем, как отдать их тестировщику?

То есть я совсем разленюсь и перестану запускать приложение в интеграции. Для этого зафиксирую значение переменной C в No:

Рис 5. Совсем разленился - не тестирую в интеграции.
 Как видишь, вероятность того, что мы будем наблюдать качественную систему упала с 64% до 58%. Вроде логично. *

* Видишь, здесь переменные I и R приняли те значения, которые мы вводили изначально. Это потому, что "зафиксировав" C мы разорвали связь между I и R. То есть, если я постоянно буду забивать на локальное тестирование, то будут действовать те вероятности, которые я выдумал и записал в таблицах 1 и 2, где C=0.

Как изменится качество системы, если я всегда буду тестировать перед комитом?

Перед тем, как отдать изменение тестировщику я всегда одолеваю свое нежелание прогнать самому несколько важных интеграционных тестов. Фиксирую значение C в Yes:
Рис 6. Всегда тестирую перед тем, как отдать тестировщику.
Предполагаемый уровень качества приложения повысился. В этом варианте из 78 комитов из 100 можно ожидать сносного качества.

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

А если тестировщих обнаружил новые баги после моего комита?




Что в этом случае можно сказать о моей прилежности и добросовестности? А можно сказать, что с вероятностю 82.35% я не тестировал интеграционно свой код перед отправкой.
А как изменилось качество? КО говорит, что качество не очень. Конкретнее можно надеятсья, что только в 16% подобных ситуаций качество будет приемлемо. *

* Еще одна примечательность. Вероятность того, что бага переоткроется (R) немного увеличилась. Это потому, что знание того, какое значение приняла I (появились новые баги) повлияло на (логично же - раз есть новые баги, то я не добросовестно отнесся к интеграционному тестированию). А поскольку вероятность того, что я тестировал свои изменения уменшилась, то и увеличилась вероятность того, что бага переоткроется.

Выводы.

Мы построили сеть Байеса чтобы проанализировать зависимости между проведением "предкомитного"  тестирования (C), вероятностями появления новых багов (I), переоткрытия старых (R) и как это влияет на качество системы (Q). 
Согласно нашей модели если я, как разработчик, никогда не буду тестировать свои изменения перед тем, как отдать тестировщику, то в 58% случаев это сработает и качество системы не пострадает. Если же я всегда буду прогонять предкомитные тесты, то следует ожидать качественную систему в 78%.
Можно предположить, что даже имея довольно оптимистичные прогнозы насчет качества моих изменений все равно влияние предкомитного тестирования довольно существенное.

Должен сказать, что это очень простая модель, которая не учитывает многие другие факторы, которые могут влиять на качество. К тому же исходные данные могут очень сильно отличаться в разных проектах и командах.

Дальнейшие исследования.

Все-таки мне не хватило аргументов, чтобы заставить себя чаще тестировать свои изменения интеграционными тестами. Хотя результат меня немного удивил. Я ожидал математическое подтверждение своей интуиции.
Поэтому в следющем посте я постараюсь привести эти вероятностные характеристики к более ощутимым вещам. А именно к трудозатратам и, как следствие к стоимости своего решения игнорировать комплексное тестирование своих изменений перед их отправкой тестировщику.


Disclaimer (что по-русски означает письменный отказ от ответственности)
  1. Прошу не относиться к этому исследованию, как к сколь-нибудь весомому аргументу, который может повлиять на твои методы разработки. Это всего лишь один из множества вариантов построения и оценки взаимодействия взаимозависимых элементов модели. А как известно, любая модель зависит от данных, которыми она оперирует, другими словами "garbage in - garbage out" :). К тому же и в модели тоже бывают ошибки ;)
  2. Я старался избегать специальных терминов и попытался изложить идею максимально просто, но если ты нашел, что изложение слишком сложно для восприятия, то пожалуйста оставь коментарий или задай вопрос.

Ссылки на источники, исходные файлы
- Средство моделирования сетей Байеса SamIam
- Курс на Coursera Probabilistic Graphical Models
- Файл модели для SamIam можно скачать отсюда http://bit.ly/ZALP1J

PS. Я давно заметил, что программисты склонны давать очень аргументированные отмазки лишь бы не делать того, что им не хочется :). Пока что мои аргументы работают против меня, посмотрим, что получится во 2-й части исследования.