Бесплатная консультация
← Все статьи

Почему технический долг всплывает перед релизом

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

Мобильная игра на смартфоне крупным планом

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

Проблема становится заметной ближе к релизу.

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

Именно тогда «временные» решения начинают связываться друг с другом. Один небольшой фикс затрагивает несколько систем, сборки становятся нестабильными, QA возвращает знакомые баги, а команда всё чаще боится менять код, потому что никто до конца не понимает последствия.

Технический долг не появляется за неделю до релиза. Просто именно перед релизом становится видна его настоящая стоимость.

Пока проект растёт, скорость маскирует проблему

На ранних этапах разработки главным ограничением часто является скорость.

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

Это нормально.

Прототипный код может быть временным, а часть архитектуры действительно выгоднее уточнить после того, как команда поймёт, что механика вообще работает. Именно поэтому мы отдельно разбирали, какой объём действительно нужен игровому прототипу.

Проблема начинается тогда, когда временное решение перестаёт быть временным.

Сначала поверх него появляется ещё одна функция. Затем вторая. Потом к системе подключаются сохранения, аналитика, UI и сетевой код. Через полгода никто уже не хочет её трогать, потому что изменение выглядит опаснее, чем очередной workaround.

При этом проект продолжает двигаться, поэтому долг практически не ощущается.

Цена становится понятна позже, когда скорость разработки перестаёт быть единственным критерием и на первый план выходит стабильность.

Перед релизом изменение правил игры неизбежно

Production и стабилизация требуют разного отношения к изменениям.

Игра на телефоне в руках пользователя

Во время активной разработки нормально, если какая-то система несколько раз меняет интерфейс. Ближе к релизу то же изменение означает дополнительный regression scope, новый build и ещё один цикл проверки.

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

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

Каждое решение в момент появления могло занимать несколько часов.

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

Теперь стоимость бага определяется уже не сложностью самого дефекта, а стоимостью изменения системы.

По этой причине к моменту формирования release candidate особенно важны понятные правила релизной ветки. Чем выше техническая связанность проекта, тем опаснее становятся даже небольшие поздние изменения.

Один и тот же баг начинает возвращаться

Очень характерный признак накопленного долга — повторяющиеся дефекты.

Клавиатура, геймпад и портативная консоль

Команда исправляет проблему с загрузкой уровня. Через две недели она появляется в другом переходе.

Исправляется ошибка reconnect. После следующего обновления ломается соседний сетевой сценарий.

QA снова находит потерю состояния после suspend/resume, хотя похожий баг уже закрывался несколько месяцев назад.

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

Возможно, модуль слишком сильно связан с другими частями проекта. Возможно, у него нет одного понятного источника состояния. Возможно, код настолько сложно тестировать изолированно, что разработчик каждый раз проверяет только конкретный happy path.

В этот момент очередной локальный fix уже не уменьшает технический долг. Иногда он добавляет ещё один слой.

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

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

CI и сборки тоже показывают накопленный долг

Технический долг существует не только внутри gameplay-кода.

Иногда проект работает достаточно стабильно, но выпуск каждой новой версии превращается в маленькую операцию.

Build собирается только на компьютере одного разработчика. Перед Android-версией нужно вручную заменить несколько конфигов. Release build требует последовательности действий, которая нигде не записана. Один пакет периодически ломает CI, поэтому его просто перезапускают.

Пока релизы происходят редко, команда привыкает к такому процессу.

Перед запуском частота сборок резко растёт.

QA нужен новый candidate. Исправили blocker — ещё один. Обнаружили регрессию — следующий. Затем понадобится production build, патч и, возможно, дополнительная платформенная версия.

То, что раньше стоило двадцать минут ручной работы раз в месяц, теперь повторяется несколько раз в день.

Именно поэтому проблемы build pipeline часто ощущаются наиболее болезненно на этапе стабилизации. Скорость и воспроизводимость сборки становятся частью способности команды вообще выпускать исправления.

Производительность долго позволяет жить в долг

Похожая история происходит с оптимизацией.

Геймпад в руках игрока

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

Затем появляется финальный контент.

Добавляется больше персонажей, VFX, интерфейсов, аналитики, локализаций и сторонних SDK. Сцены становятся тяжелее, а системы, которые раньше редко работали одновременно, начинают пересекаться.

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

Новая система не обязательно плохая сама по себе. Она просто становится последними 10%, которые больше не помещаются в доступный бюджет.

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

QA не создаёт технический долг — он делает его видимым

Ближе к релизу растёт и глубина тестирования.

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

Игрок меняет настройки, загружает старое сохранение, несколько раз переходит между сценами, теряет интернет, возвращает приложение из background и продолжает сессию ещё несколько часов.

И именно на таких комбинациях начинают проявляться зависимости, которые разработчики раньше почти не встречали.

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

Иногда просто появился первый этап, на котором проект действительно начали проверять как целую систему.

Вот почему QA полезно подключать до content lock. Чем раньше системные сценарии начинают регулярно проходить через сборку, тем меньше технических сюрпризов остаётся на последние недели.

Если проверять интеграцию только на финальном candidate, времени на архитектурные исправления практически не остаётся.

Страх изменений — один из самых понятных сигналов

Есть простой индикатор состояния кодовой базы, который не требует сложных метрик.

Коллекция игровых контроллеров разных платформ

Насколько команда боится менять конкретную систему?

Если задача выглядит небольшой, но разработчик сразу говорит: «Туда лучше не лезть перед релизом», стоит понять причину.

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

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

В хорошем проекте перед релизом команда избегает ненужных изменений.

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

Разница принципиальная.

Не весь технический долг нужно закрывать перед релизом

Самая опасная реакция на обнаруженный долг — попытаться срочно исправить всё.

За месяц до запуска команда открывает старую систему и решает наконец сделать её «правильно». Начинается большой refactoring, меняются интерфейсы, зависимости и данные.

Технически новый вариант может быть намного лучше.

Но для текущего релиза он создаёт огромную новую область regression.

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

Если система неидеальна, но стабильна, понятна команде и не создаёт текущих рисков, часто безопаснее оставить её до следующего цикла.

Если же она регулярно вызывает blockers, делает невозможным исправление критического бага или не позволяет выполнить требования платформы, долг уже стал частью release scope.

Это причина, почему перед большим этапом иногда выгодно провести аудит Unity-проекта. Его задача не в том, чтобы составить длинный список «неидеального кода», а в том, чтобы отделить архитектурные проблемы, реально влияющие на продукт, от косметического refactoring.

Технический долг нужно переводить в стоимость

Фраза «у нас много технического долга» слишком абстрактна для планирования.

Игра на консоли в домашней обстановке

Гораздо полезнее говорить о последствиях.

Из-за этой системы каждая новая механика занимает на два дня больше.

Этот pipeline заставляет QA ждать новый билд сорок минут.

Эта архитектура сохранений создаёт большую часть критических regression.

Этот модуль невозможно нормально покрыть автоматическими тестами.

Теперь долг можно сравнивать с другими задачами.

Если refactoring занимает неделю, но после него следующие пять функций становятся заметно дешевле, решение выглядит иначе, чем «разработчики хотят переписать старый код».

То же работает в обратную сторону. Если переделка займёт месяц и почти не влияет на roadmap ближайшего года, возможно, бизнесу выгоднее оставить текущую систему.

Технический долг — не моральная характеристика кода. Это экономическая характеристика проекта.

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

После релиза долг не исчезает

Есть удобная мысль: сейчас дотянем до запуска, а потом спокойно всё почистим.

Иногда это действительно происходит.

Но чаще после релиза появляются новые приоритеты: hotfix, отзывы игроков, первый контентный апдейт, аналитика, LiveOps, новая платформа.

Если долг не получил конкретного места в roadmap, он снова начинает откладываться.

В итоге старая система, которую команда боялась менять перед первой версией, продолжает жить ещё несколько лет.

Поэтому после выпуска полезно возвращаться не ко всему списку технических проблем, а к тем точкам, которые реально мешали релизу. Что заставляло делать лишние regression passes? Где исправления занимали непропорционально много времени? Какие ручные операции регулярно создавали ошибки?

Это уже хороший backlog следующего технического этапа.

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

Хороший момент для работы с долгом — до кризиса

Технический долг становится дорогим не потому, что код перестаёт соответствовать какому-то абстрактному стандарту качества.

Тестировщик за монитором с запущенной игрой

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

Если сборки ещё стабильны, критические системы понятны, performance budgets контролируются, а regression ограничен, у команды есть выбор. Можно спокойно планировать refactoring там, где он окупается.

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

Поэтому признаки долга полезно искать не только непосредственно перед запуском. Нестабильный CI, повторяющиеся дефекты, системы, которые боятся трогать, растущий regression и постоянные временные обходы — всё это ранние сигналы.

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

Если перед каждым релизом команда снова сталкивается с одними и теми же проблемами, а даже небольшие изменения требуют большого regression — пришлите описание проекта, текущего pipeline и наиболее проблемных систем. Разберём, где технический долг уже влияет на продукт и какие изменения дадут максимальный эффект без ненужного переписывания всей кодовой базы.

Следующая статьяБюджет производительности мобильной игры
→