Перевод статьи Ричарда Тейлора «How to write fewer bugs: tips for game developers».
Я создал много игр. Финальный этап разработки игры обычно тяжелый. Ужасная спешка, а лишний крюк на исправление багов и полировка всей игры требуют напряженного труда всех участников процесса.
Если вам повезло, то спешка проистекает из страстного желания достигнуть нужного команде результата. В противном случае это следствие принятия на себя чрезмерных обязательств и невозможных дедлайнов. (На самом деле удача тут не при чем, но это отдельная тема).
Язык, который мы, разработчики, используем, говоря о багах, может поведать многое. Обычно это что-то вроде «QA нашел баг в моем коде!». Подразумевается, что баг и код это совершенно разные сущности. Это как если вы нашли гусеницу в салате, который собирались съесть.
Но это, конечно же, далеко от истины, поскольку без кода не бывает багов. Салат можно удалить, оставив лишь гусеницу, но нельзя удалить код и оставить лишь баг. Без кода бага просто нет. На самом деле багов вообще нет, есть только код, который в большей или меньшей степени удовлетворяет необходимым требованиям.
Этот язык может быть совершенно бесполезным, когда вы пытаетесь говорить о подходах, позволяющих создавать меньше багов. «Что ты имеешь в виду? Баги просто случаются, а избавление от них это часть цикла производства программ».
Судя по моему опыту, в большинстве проектов по разработке игр значительная часть ресурсов тратится на исправление багов, и обычно это происходит ближе к концу проекта. Это напрасная трата времени и сил, ведь если бы инженеры не исправляли баги, они могли бы заняться полировкой игры и повышением ее качества.
Рецепт прост: пишите меньше багов!
Исходя из того что баги это просто код, любой существующий баг был внесен командой разработчиков. Может, стоит попросить их писать меньше багов?
Не удивлюсь, если это предложение вас рассмешило. По моему опыту, это обычная реакция инженеров: никто же не пишет баги нарочно, верно? Но с точки зрения проекта это целиком резонный запрос. Представьте, как может повыситься качество и уменьшиться сверхурочная работа, если время на исправление багов будет просто вычеркнуто из расписания.
Если мы разделяем код и баги, то действительно очень сложно серьезно воспринять мой совет. Мы так привыкли мыслить подобным образом, что такое предложение кажется бессмыслицей. Как же нам превратить «пишите меньше багов» в разумный запрос, который может быть принят командой?
Старт нового проекта. Шаг первый: не повторять предыдущие ошибки
Наша команда работала в существующем составе над несколькими проектами и мы только что закончили еще один. Все мы знали, что не хотим проводить финальные месяцы нашего следующего проекта, подчищая хвосты. Пришло время что-то с этим сделать.
Мы хотели не фиксить баги, а полировать нашу игру. При старте нового проекта мы сели и занялись исследованием идеи написания меньшего количества багов.
Нам нужно было разработать язык и рассуждения, позволяющие команде взять на себя ответственность за эту проблему. Должно быть ясно, что цель достижима, и что ответственность лежит на конкретных людях.
Первым шагом было определить «зачем». Наше «зачем» на верхнем уровне было таким: мы хотим уменьшить количество времени, необходимое для исправления багов, и уделять больше времени повышению качества.
Но дело ведь не только в уменьшении количества времени, которое тратится впустую. Речь идет скорее о возможности проводить больше времени за интересными задачами, чем-то таким, ради чего вы будете радостно вставать утром. Больше R&D, оптимизации, геймплеев, визуальной точности, больше совершенства. Это видение описывает то, почему мы вообще занимаемся разработкой игр. Больше времени на интересные вещи.
Но даже с ясным видением «пишите меньше багов» является настолько общей фразой, что она практически бесполезна. Это заявление о намерениях, не дающее никаких подсказок насчет того, как нам это реализовать. И, что более важно, никаких сведений о том, как окупится время, вложенное в «написание меньшего количества багов».
Чтобы превратить запрос «пишите меньше багов» в некое техническое направление, которое может быть принято инженерами, потребуется значительное количество времени и сил. На этом этапе велик соблазн посмотреть на дедлайны проекта и решить оставить все, как есть. В конце концов, баги неизбежны, а нам надо поставить проект.
Чтобы раскрыть «как», мы применяли основной научный метод: исследование, гипотезы, тестирование, повторение.
Исследование
Категоризация и анализ
Откройте базу данных с багами вашего последнего законченного проекта и взгляните на них. Есть много разных видов багов, поэтому просто говорить «меньше багов» практически бессмысленно.
Чтобы иметь возможность обсуждать проблему, нам нужно точнее определить спецификации. В нашем случае нас особенно интересовали баги, исправление окторых съедает значительное количество времени. Мы проанализировали базу данных багов и выявили две основные группы, которые нам хотелось бы затронуть:
- баги, которые очень долго исправлять,
- постоянно ухудшающиеся баги.
По существу для багов есть два ключевых показателя эффективности. Это «приемлемое количество дней на исправление» и «количество переходов состояния бага».
Баги, которые долго исправлять, обычно тяжело воспроизвести или диагностировать. Баги с большим количеством изменений состояний часто крутятся вокруг циклов «cannot repo» или «fix / reopen».
Используя названные показатели эффективности мы выделили меньший набор багов, исправление которых занимает непропорционально большое количество времени. У нас было несколько несоответствующих экземпляров в этих группах, но мы от них избавились на следующем этапе.
Анализ
Имея на руках конкретные группы багов, можно было сделать следующий шаг: попытаться определить, что у них общего и каковы их причины. Для такой работы нужна комбинация опыта в программировании и трактовки описаний багов. А больше всего для этого нужны время и настойчивость.
Наша база данных была интегрирована с SCM. Это давало некоторую прямую корреляцию с исходным кодом, хотя погрешности были большими и по-прежнему требовалась интерпретация на основе опыта.
Корень проблемы
Наконец, спустя достаточное количество времени, проявились некоторые шаблоны и мы увидели, что один набор багов действительно требует значительного времени на исправление. Имея сведения об этих багах, мы были полны решимости перейти к поиску их первопричины, к исходному коду.
Работая с инженерами, мы нашли, какие изменения вносились. Затем мы составили дополнительные списки систем, файлов и строк кода, которые имели отношение к значительным проблемам. В конечном счете мы получили список конкретных изменений кода, который можно было обсудить с командой разработчиков.
Я же говорил
После всего этого мы получили возможность сесть и обсудить наши открытия со всей большой инженерной командой (причем они почти наверняка знали обо всех проблемах в коде!!!). Но если команда уже знала о проблемах, то чего же мы достигли?
Мы провели связь между потерями времени и конкретными зонами кода. Это дало нам возможность объективно оценивать значимость любого предлагаемого рефакторинга и поддержки.
Это также помогло высветить области кода, считавшиеся «приемлемо плохими». И это был один из самых больших сюрпризов. Я мог сказать, что «нам определенно нужно сделать рефакторинг этой системы», хотя команда инженеров до того считала, что это невозможно или потребует слишком большой работы.
Конечно, многие из причин появления багов были системными. В частности, многие из «невидимых» проблем были связаны с совершенно приемлемыми, широко применяемыми шаблонами программирования. Это означало, что для исправления этих проблем нужно будет бросить вызов текущим трендам программирования, моде и прочим мантрам.
Наконец у нас оказалось достаточно результатов исследований для выдвижения гипотез, и вся команда занялась этим увлекательным делом.
Гипотезы
Гипотеза 1: определенные шаблоны программирования статистически чаще могут послужить причиной багов в крупном проекте разработки.
Гипотеза 2: Если избежать использования шаблонов из п.1, мы сможем уменьшить количество времени на исправление багов в проекте.
Для целей данной статьи давайте определим «крупный проект» как требующий участия больше 25 программистов и больше года разработки. Такой проект будет достаточно большим, чтобы соответствовать следующим истинам:
- Любой код проживет достаточно, чтобы стоимость его поддержки превысила стоимость разработки.
- Сложность, возникающая на стыке между системами, значительнее сложности любой отдельной системы.
Почему это важно? В маленьких проектах вы можете обойтись практически без чего угодно, код весь в ваших руках.
В больших проектах код вам не принадлежит. Вам придется работать с кодом, который вы не понимаете. Ваши инженерные решения будут базироваться на неполном знании и предположениях.
Теперь, когда мы говорим с другими членами команды, наша риторика меняется (по сравнению с «пишите меньше багов»).
«Проанализировав наши предыдущие проекты, мы выдвинули следующие гипотезы…»
«Данные показывают, что вот эти конкретные шаблоны программирования часто были фактором, связанным с проблемами. Мы считаем, что если этих шаблонов избегать, то это снизит количество времени, необходимое для исправления багов и улучшение качества».
Далее нам нужно превратить гипотезы в нечто полезное на практике.
Тестирование
Во многом техническое направление и системная архитектура нашего следующего проекта должна была основываться на уклонении от использования рискованных шаблонов в коде. К ним относились:
- Связь memory allocation lifetime с конструкцией объекта и его временем жизни.
- Перегрузка операторов и денормализованные соглашения об именах.
- ‘auto’, полиморфические функции и исключение типобезопасности.
- Увеличение сложности путем dependency injection, обратных вызовов, лямбда.
- Использование мьютексов, семафоров и других примитивов потоков в высокоуровневом коде.
Каждый из этих пунктов заслуживает технического обсуждения того, как он изменил наш подход к системам и дизайну API, но об этом я расскажу в отдельных публикациях.
Что было дальше?
Как я уже говорил, нам невероятно повезло: у нас была возможность начать новую игру с новыми подходами. Наша маленькая команда сумела построить новый движок игры с нуля за 24 месяца и поставить игру вовремя.
Мы хорошо отполировали нашу игру, а количество ошибок было относительно небольшим, причем ошибок с высокой стоимостью было совсем немного. Это было независимо подтверждено отделом QA.
Но просто попросить команду избегать вышеуказанных шаблонов было недостаточно. В процессе работы легко забыть руководства по написанию кода и скатиться к старым привычкам. Ключевым решением было спроектировать код, системы и интерфейсы таким образом, чтобы в них не использовались опасные шаблоны. Это быстро перешло в мантру «сделай так, чтобы было сложно сделать неправильно», которая и направляла команду в этом проекте.
Что важно – команда стала счастливее. Мы определили подход, результатом которого стало большее количество первоначальной работы с кодом. Команда знала, что очевидный и легкий способ использования системы вероятно является правильным, а API предотвратит плохие шаблоны. Багов стало меньше, а время стало тратиться на новые фичи, полировку и итерации.
[customscript]techrocks_custom_after_post_html[/customscript]
[customscript]techrocks_custom_script[/customscript]