Виправити, рефакторити чи переписати успадкований застосунок
Павло Рубановський
· 6 хв читання

Лишайте те, що працює, виправляйте локальні баги, рефакторте те, що гальмує кожну зміну, і переписуйте лише те, що дешевше замінити, ніж полагодити, — для кожної частини окремо, після читання коду, а не для всього застосунку одразу. Більшості успадкованих застосунків потрібно кілька таких рішень у різних місцях. Переписати все — виняток, а не відправна точка.
Чотири висновки поруч
| Висновок | Коли підходить | Ризик |
|---|---|---|
| Лишити — не чіпати частину | Вона працює, і зміни в ній лишаються локальними | Немає — нічого не змінюється |
| Виправити — змінити зламане, структуру лишити | Архітектура здорова, проблеми локальні | Низький |
| Рефакторити — перебудувати код, користувачі змін не бачать | Кожна зміна повільна або ламає щось інше | Середній — спершу напишіть тести навколо цієї зони |
| Переписати — замінити частину новим кодом | Лагодити дорожче, ніж замінити | Високий — усе, що «знав» старий код, доведеться відкривати заново |
Ключове слово — *частина*: це рішення про частини застосунку, а не ярлики для нього цілого. Платіжний сценарій (флоу) може потребувати переписування, а онбординг поруч — нічого.
Ознаки, що частину варто лишити й виправити
- Вона робить те, що потрібно користувачам, а баги конкретні: збій на одному екрані, хибний розрахунок, проблема з версткою на одному пристрої.
- Код достатньо читабельний, щоб новий розробник знайшов, куди вносити зміну.
- Зміни в ній лишаються в ній — виправлення однієї речі не ламає дві інші.
- Вона збирається сучасними інструментами збірки (тулчейном) або збереться після звичайних оновлень.
Сюди потрапляє більша частина типового успадкованого застосунку. Старе не означає погане, а незнайоме — неправильне.
Ознаки, що потрібен рефакторинг
- Кожна дрібна зміна займає дні, бо логіка розкидана по екранах, віджетах і сервісах.
- Одне бізнес-правило реалізоване в трьох місцях, і вони суперечать одне одному.
- Станом — даними, які екран показує й змінює, — керують кількома різними способами в одному застосунку, нашарованими один на одного.
- Ніхто не наважується торкатися файлу, бо останній, хто торкнувся, зламав оплату.
Рефакторинг зберігає поведінку незмінною, а наступну зміну робить дешевою. Найбезпечніше робити його малими кроками, випускаючи кожен окремо, і писати тести навколо зони *до* перебудови — щоб довести, що для користувачів нічого не змінилося.
Ознаки, що переписати дешевше
Переписування частини виправдане, коли справджується одне чи кілька з цього:
1. Її не можна зробити безпечною. Логіка переплетена з інтерфейсом, тестів немає, а частина працює з грошима, автентифікацією чи персональними даними. 2. Фундаменту більше немає. Вона залежить від покинутого фреймворку чи пакета, який блокує оновлення всього іншого. 3. Продукт став іншим. Частину будували під сценарій, якого вже немає, і тепер її підганяють під новий. 4. Вона невелика. Замінити кілька екранів дешевше, ніж розбиратися в їхній історії.
Навіть тоді переписування має обмежуватися цією частиною, мати власну ціну й виходити окремим релізом.
Чому «переписати все» — хибна перша відповідь
Нова команда, яка пропонує повне переписування, ще не прочитавши код, описує свої вподобання, а не ваш застосунок. Ризики добре відомі — есей Джоела Спольскі Things You Should Never Do досі лишається класичним описом — і для мобільних застосунків вони так само актуальні:
- Старий код зберігає знання. Кожна дивна на вигляд умова — зазвичай баг, який хтось виправив для реального користувача на реальному пристрої.
- Відтворити всі старі функції (паритет функцій) довше, ніж будь-хто пам’ятає. Крайні випадки, налаштування, міграції збережених даних, пуш-сценарії, діплінки.
- Ви платите двічі. Старий застосунок однаково потребує виправлень, поки будується новий.
- Довго нічого не виходить. Користувачі й інвестори не бачать прогресу, доки нова версія не готова повністю.
DriveCode розробляє застосунки для iOS та Android, Flutter-first. Коли ми підхоплюємо застосунок, за замовчуванням лишаємо те, що працює, і замінюємо лише те, що за результатами огляду дешевше замінити.
Рішення для кожної частини
Ці питання до кожної зони застосунку зазвичай усе вирішують:
| Питання | Якщо так | Якщо ні |
|---|---|---|
| Чи робить вона те, що потрібно користувачам сьогодні? | Лишити або виправити | Переписати може бути дешевше |
| Чи можна змінити її, не зламавши щось інше? | Виправити | Спершу рефакторинг |
| Чи є тести, або чи можна їх додати до того, як чіпати? | Безпечний рефакторинг | Додати тести або розглядати як кандидата на переписування |
| Чи збирається вона сучасним тулчейном після звичайних оновлень? | Виправити | Перевірити, чи можна замінити її залежності |
| Чи займе заміна менше часу, ніж розібратися в ній? | Переписати | Лишити |
Відповіді йдуть у письмовий висновок для кожної частини — лишити, виправити, рефакторити чи переписати, і чому, — а роботу ділимо на модулі з фіксованою ціною кожного. Що саме ми дивимося, щоб дійти до висновку, — у статті що ми перевіряємо, перш ніж підхопити застосунок.
Якщо застосунок нативний: перехід на Flutter — інше рішення
Перенесення нативного застосунку для iOS та Android на Flutter — це за визначенням переписування, але заплановане й зі своїми причинами: одна кодова база замість двох, одна команда замість двох. Це не вирок якості нативного коду.
Ми робимо це за принципом «спершу ядро»: Flutter-ядро виходить як компактний MVP, поки нативні застосунки й далі отримують оновлення; решта приходить у регулярних оновленнях, починаючи з найпотрібніших. Детальніше — у статтях як перенести нативний застосунок на Flutter без заморозки фіч і скільки коштує такий перехід, а також на сторінці міграції.
Не зупиняйте релізи, поки вирішуєте
Хоч яким буде висновок, релізи не мають зупинятися. На практиці перший модуль роботи часто найменш ефектний: повернути застосунок у стан, коли звичайне оновлення можна зібрати й опублікувати з ваших акаунтів. Після цього виправлення й рефакторинг виходять звичайними оновленнями, а переписана частина — тоді, коли готова вона, а не весь застосунок.
Що часто питають
Чи буває повне переписування правильним рішенням? Так — коли застосунок невеликий, коли майже кожна частина не проходить питань вище або коли продукт змінився настільки, що від старого мало що лишилось. Але це має бути висновок із прочитаного коду, а не стартова пропозиція.
Як зрозуміти, що команда не просто продає мені переписування? Попросіть висновок для кожної частини з причинами. Команда, яка читала ваш код, назве конкретні файли й сценарії; та, що не читала, говоритиме про «технічний борг» загалом.
Чи можна рефакторити й додавати функції водночас? Так — у різних частинах застосунку або рефакторячи зону безпосередньо перед тим, як додати в неї функцію. Уникати варто довгого рефакторингу без релізів.
Скільки коштує кожен варіант? Залежить від розміру кожної частини, тому ми встановлюємо фіксовану ціну за кожен модуль після огляду вашого застосунку. Проєкти — від $10K.