Рефакторинг мобильного приложения: где копится долг
Технический долг мобильного приложения редко выглядит как один сломанный модуль. Обычно он копится тихо: сетевой слой оброс исключениями под конкретные экраны, бизнес-правила расползлись между экраном, кешем и аналитикой, а разросшаяся кодовая база перестала помещаться в голову даже старшему инженеру. Правку вносит один человек, а последствия разбирает вся команда.
Связанность модулей растёт вместе с числом сценариев. Чтобы показать новую карточку в каталоге, изменение приходится вносить в модель данных, в слой загрузки, в разметку списка и в локальное хранилище — четыре места, из которых три к задаче отношения не имеют. Чем плотнее склейка, тем меньше можно предсказать, что именно сломается на соседнем экране.
Дальше включается регресс после правки. Смена формата ответа тянет за собой проверку оформления заказа, авторизации и уведомлений, потому что все они читают те же данные. Ручной прогон занимает дни, автотесты покрывают не все ветки, и выпуск версии сдвигается на неопределённый срок.
Для продукта это оборачивается тем, что мелкие доработки перестают быть мелкими. Команда планирует не то, что нужно бизнесу, а то, что безопасно менять; вместо развития появляются пачки исправлений и внеплановые выпуски. Разработчики начинают бояться трогать старые участки — и долг растёт ещё быстрее.
При этом накопившееся измеримо: по числу связей между модулями, времени сборки, доле путей без тестов и количеству дефектов на выпуск. Эти цифры показывают, где именно резать. Дальше разбираем, как это закрывается.
Эффект рефакторинга: скорость версий и меньше отказов
Рефакторинг оправдан, когда его следствия видны в метриках релизного цикла, а не в аккуратности кода. Пока экраны, сетевой слой и хранилище связаны напрямую, правка в одном месте тянет цепочку изменений: сборка идёт дольше, проверка — вручную, часть времени съедают регрессии, всплывшие уже после публикации.
Разделение на независимые модули меняет картину. Участки собираются и тестируются параллельно, типовое изменение локализуется внутри одного модуля, автотесты на критичные сценарии прогоняются на каждое изменение кода. Ускорение выпуска версий здесь не лозунг: доля времени, которую команда тратит на подготовку релиза, сокращается в разы, а сам выпуск становится плановой операцией, а не авралом.
Снижение числа отказов считаем по фактам: доля сессий без сбоя в Crashlytics, количество возвратов после публикации, частота откатов на предыдущую сборку. Явные границы модулей убирают целый класс падений — те, что рождались на стыках слоёв и проявлялись только под нагрузкой. Отдельно прогоняем пиковые сценарии: устойчивость к нагрузке растёт, потому что тяжёлые операции уходят с главного потока, а повторные сетевые запросы не дублируются.
Четвёртый эффект — трудоёмкость поддержки приложения и предсказуемость оценки. Когда модули отвечают каждый за своё, новая задача считается по объёму работ внутри одного участка, а не «как повезёт»: разброс оценок сужается с недель до дней, и релизный цикл планируется по календарю, а не по факту.
Способы переработки: полная, постепенная, точечная
Режим переработки выбирают по двум вопросам: насколько глубоко проблема сидит в архитектуре и можно ли заморозить релизы на время работ. Ни один из вариантов не требует остановки действующего продукта — разница в том, сколько переходного кода команда тащит параллельно и как долго живут два слоя одновременно.
| Вариант | Когда подходит | Ограничения |
|---|---|---|
| Точечная оптимизация узких мест | Проседает один-два сценария: холодный старт, прокрутка длинного списка, фоновая синхронизация. Остальное ядро устраивает | Лечит симптом. Долг в слоях доступа к данным и навигации остаётся, и через несколько итераций правки возвращаются в те же модули |
| Постепенная переработка | Продукт активно развивается, релизы идут каждые две недели, остановка невозможна | Старый и новый слои живут параллельно: растёт объём переходного кода, на каждом шаге нужны проверки совместимости |
| Вынос модулей в отдельную библиотеку | Общая логика повторяется в двух и более приложениях или командах | Требует контракта версий и своего цикла релизов, иначе сборки расходятся и пользователи получают разное поведение |
| Полное переписывание ядра | Архитектура не даёт развивать продукт, платформенные интерфейсы устарели, правки обходятся дороже новой реализации | Параллельная поддержка старой ветки, пока новая не достигнет паритета по функциям, и полный цикл тестирования заново |
Начинают обычно с точечной оптимизации: она быстро показывает, где настоящее узкое место. Если правки после неё возвращаются в те же модули, переходят к постепенной переработке и выносу общих частей в отдельную библиотеку, а полное переписывание ядра оставляют на случай, когда архитектура перестала держать нагрузку развития.
Как проходит работа: этапы от аудита до передачи
Рефакторинг ломает продукт, когда идёт одной большой перепиской: пока ветка живёт неделями, релизы встают, а регресс всплывает уже у пользователей. Мы идём короткими шагами, после каждого приложение остаётся собираемым и рабочим. Порядок ниже типовой — под конкретный проект меняются объём и глубина.
- Аудит кодовой базы. Разбираем историю правок и карту зависимостей, находим дублирование и модули, которые меняются чаще всего. На выходе — список узких мест с оценкой риска и очередностью работ.
- Схема целевой структуры. Раскладываем приложение на слои и модули, фиксируем контракты между ними. Что переносим как есть, а что переписываем — согласуем с вашей командой до первой строки нового кода.
- Пилотный модуль. Переносим один участок целиком, чтобы проверить схему на живом коде и оценить трудоёмкость остального. Дальше масштабируем подтверждённый подход, а не предположения.
- Перенос по частям. Идём мелкими порциями: каждая правка закрыта тестом и уходит в основную ветку, не дожидаясь окончания работ. Релизы продолжают выходить по обычному графику.
- Автоматическая сборка и проверки. Настраиваем сборку на каждое изменение, статический анализ и прогон тестов. Регресс ловится до проверки кода, а не в день выката.
- Приёмочные проверки. Сверяем поведение с исходной версией по ключевым сценариям — вход, оплата, офлайн-режим. Время старта и доля падений не должны ухудшиться ни на одном шаге.
- Передача и обучение команды. Отдаём документацию по структуре, правила внесения изменений и разбор с инженерами заказчика. Приложение остаётся поддерживаемым без нас.
Артефакты проекта: схемы, регламенты, документация
Рефакторинг завершён не тогда, когда сборка стала зелёной, а когда в репозитории остались артефакты, по которым команда работает без нас. Каждый лежит рядом с исходниками и обновляется в том же изменении, поэтому не расходится с кодом через месяц.
- Карта модулей и зависимостей — какие модули есть, кто от кого зависит, где разорваны циклические связи и какой слой за что отвечает.
- Документация по архитектуре — схема слоёв и границ доменов, точки расширения, журнал решений с обоснованием и отклонёнными альтернативами.
- Регламент работы с кодом — модель ветвления, порядок взаимной проверки изменений, ограничение на их размер, обязательные проверки перед слиянием.
- Описание проверок — какие автотесты закрывают какие пути, где проходит граница покрытия и что остаётся на ручную проверку.
- Инструкция по выпуску версии — порядок сборки, подготовка подписи, раскатка на канал и откат на предыдущий выпуск.
- Карта владения — за каким модулем закреплён ответственный и к кому идти с вопросом.
- Журнал технического долга — что осознанно отложено, по каким признакам вернёмся и что будет, если не вернуться.
Проверяем комплект просто: релиз по инструкции собирает инженер, не участвовавший в рефакторинге. Справился без подсказок — артефакты полные; застрял — дописываем до передачи.
Кейс: сократили срок выпуска версии втрое
Что было
Приложение развивалось несколько лет без общей архитектуры: логика экранов, работа с сетью и хранение состояния были перемешаны, а один и тот же расчёт дублировался в четырёх местах. Кодовая база на Flutter собиралась одной командой, но каждое изменение тянуло за собой цепочку связанных правок.
Выпуск версии занимал шесть недель, из которых три уходили на стабилизацию после регрессий. Около 40% задач возвращались на доработку, 60% изменений требовали правок в смежных модулях, а новый инженер выходил на самостоятельные задачи только через месяц.
Что изменили и что стало
Разделили приложение на слои с явными границами: представление, бизнес-логика, доступ к данным. Общие расчёты свели в один модуль, сетевой слой закрыли единым клиентом с обработкой ошибок, критичные пути — оплату, авторизацию, синхронизацию — покрыли автоматическими проверками. Выпуск перевели на конвейер, где сборка, проверки и подпись идут одной командой без ручных шагов.
Срок выпуска версии сократился втрое — с шести недель до двух. Доля задач, возвращаемых на доработку, упала с 40% до 8%, доля изменений с правками в смежных модулях — с 60% до 15%. Ввод нового инженера в проект занимает около пяти рабочих дней вместо трёх-четырёх недель: структура предсказуема, а проверки показывают ошибку до ручной проверки кода.
Чем переписывание с нуля отличается от постепенной переработки?
Переписывание с нуля — это второй проект рядом с первым: логика переносится в чистую архитектуру, а старый код замораживают. Постепенное вытеснение модулей идёт иначе — новый слой появляется внутри работающего приложения и забирает участок за участком.
Дорогое в полной замене не объём работы, а пауза в поставке. Пока идёт перенос, ни новые возможности, ни исправления не выходят, требования уходят вперёд, и запуск смещается на величину этого отставания. Риск полной замены в том, что сбой здесь один и общий: платформа не выходит частично — она не выходит совсем.
Полная замена оправдана, когда проблема в фундаменте: связи размазаны по экранам, новый модуль некуда подключить, тесты не пишутся, а правка тянет за собой половину приложения. Тогда вытеснение обходится дороже — каждый слой обрастает оговорками, и обвязка съедает выигрыш.
Постепенная переработка почти всегда быстрее даёт результат. Первый заменённый модуль уходит к пользователям через один-два цикла и проверяется на живом поведении; дальше доля нового кода растёт, а откат шага ограничен одним участком. Продуктовые релизы при этом не останавливаются, и сроки переработки считаются по этапам, а не одной датой.
Выбор делаем по замеру: сколько кода действительно нужно заменить и сколько внешних зависимостей держится на этом участке. Если меняется меньше трети и связи развязываются — идём через постепенное вытеснение модулей. Если ядро не даёт зацепиться — планируем полную замену и заранее решаем, как живёт текущая ветка до переключения.
Частые технические сомнения перед стартом работ
Рефакторинг мобильного приложения почти всегда идёт на живом продукте: полностью останавливать выпуск версий нельзя, иначе пользователи не получают правки, а позиции в магазинах проседают. Поэтому переработка разбита на короткие изменения — каждое проходит сборку и уходит в ближайший релиз отдельно, а основная ветка в любой момент остаётся готовой к выкладке. Часть кода при этом действительно остаётся переписанной частично, и это рабочее состояние, а не незавершённость: границы модулей зафиксированы, старый слой вызывается через стабильные интерфейсы и снимается по мере готовности замены.
Выпуск версий придётся приостановить на время работ?
Нет. Работа идёт параллельно релизному циклу: изменения вливаются по одному, каждое проходит проверки до слияния. Если правка не проходит, она остаётся в отдельной ветке и не задерживает остальные задачи.
Как вы убеждаетесь, что ничего не сломалось?
Проверка регресса строится на автотестах по критичным путям — запуск, авторизация, оплата, переходы между экранами — и прогоняется на каждом изменении в Appium. Дополнительно сверяем отчёты о сбоях новой сборки с предыдущим выпуском: заметный рост ошибок блокирует слияние до разбора причин.
Насколько точно вы оцениваете сроки работ?
Оценка идёт по модулям, а не по коду целиком: сначала инвентаризация зависимостей и покрытия проверками, затем порядок работ и точки, где можно остановиться без потери работоспособности. Так сроки пересчитываются по факту закрытия каждого модуля, а вы видите объём на каждом этапе, а не одну общую цифру.
Обсудим задачу и вернёмся с планом работ
Оценка рефакторинга без доступа к коду превращается в диапазон «от и до», который на первой же неделе расходится с реальностью. Чтобы назвать сроки по этапам и порядок работ, нам нужны вводные: как устроен репозиторий, как выходят релизы и где именно болит сейчас. Присылайте в удобном виде — текстом, ссылкой или голосом, разберём.
- Доступ к репозиторию: достаточно прав на чтение — важны структура модулей, история изменений и ветка, из которой собирается релиз.
- Схема выпуска версий: как часто выходят сборки, кто их собирает, какие каналы тестирования задействованы и где остался ручной шаг.
- Описание узких мест: долгая сборка, падения во время работы приложения, дублирование логики между экранами, места, которые правят с осторожностью.
- Карта модулей и экранов с пометкой, какие в активной доработке, а какие заморожены.
- Что нельзя ломать: контракты с серверной частью, схему хранения данных на устройстве, дизайн-систему, поведение авторизации.
- Состояние автотестов: доля покрытых путей, что падает в наборе и что проверяется только руками.
- Приоритеты продукта на ближайший цикл — задачи, из-за которых рефакторинг нельзя останавливать надолго.
С этим набором мы возвращаемся с планом: последовательность этапов, что входит в каждый, что сознательно оставляем за рамками, сроки и точки согласования объёма. Опишите задачу — вернёмся с оценкой и планом, дальше фиксируем порядок работ и стартуем.







