«Кострубати» або «підпирати милицями» означає створити тимчасове рішення проблеми, яке закриває баг або додає функціонал, але не усуває першопричину та не відповідає архітектурним стандартам проекту. Милиці неминучі в будь-якій розробці: дедлайни, неповне розуміння системи та зовнішні обмеження змушують приймати компромісні рішення. За даними Refactoring Guru, ключова відмінність між прагматичною милицею та технічним боргом — в усвідомленості рішення та наявності плану з його усунення. Грамотне використання тимчасових рішень вимагає дисципліни та документування.
Головне
Милиця (crutch) — програмне рішення, яке працює, але зроблене «на швидку руку»: воно закриває конкретну проблему, але не усуває її причину, не слідує архітектурі проекту та може зламатися при найменших змінах середовища. Метафора точна — як і реальна милиця, такий код допомагає «йти», але не лікує «ногу».
Розробники «підпирають милицями» баги, несумісності версій, особливості платформи та термінові вимоги замовника. Типова милиця — милиця-умова: якщо iOS 15 додай відступ, якщо Huawei — сховай кнопку. Такі перевірки множаться та перетворюють код у «шаруватий пиріг» з платформених та версійних відгалужень.
Милиці бувають різних масштабів: від одного рядка з миличною умовою до цілого модуля-прокладки, який «виправляє» поведінку бібліотеки. Важливо розуміти, що милиця — це не завжди зло: у правильних руках це інструмент, що дозволяє випустити продукт вчасно. Проблема починається, коли милиця залишається в коді назавжди.
Основна причина появи милиць — конфлікт між ідеальним рішенням та реальними обмеженнями проекту. Розробник знає, як зробити правильно, але час, гроші або технічні обмеження не дозволяють цього зробити. В результаті з’являється компромісне рішення, яке «просто працює».
Розглянемо чотири основні причини, через які розробники свідомо йдуть на милиці. Розуміння цих причин допомагає ставитися до милиць не як до помилки, а як до прагматичного інструменту, який потребує управління.
Найчастіша причина. Реліз завтра, баг відтворюється тільки на конкретній моделі, чинити архітектурно — два тижні. Милиця-умова займає годину та закриває проблему. Після релізу команда обіцяє повернутися та переписати правильно. «Немає нічого більш постійного, ніж тимчасове» — саме про такі милиці.
Бібліотека A вимагає Android 12, але ваш додаток підтримує Android 10. Рішення — написати прошарок, який перевіряє версію ОС та обирає шлях виконання. Це милиця, тому що при оновленні бібліотеки прошарок доведеться переписувати. Але альтернатива — відмова від бібліотеки або підтримки старих пристроїв — може бути гіршою.
// Милиця для сумісності з API 29
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
useModernApi()
} else {
useLegacyFallback()
}
Бібліотека, від якої залежить проект, містить баг, але її оновлення може зайняти тижні (потрібен PR, код-рев’ю, публікація). Замість очікування команда пише wrapper, який патчить поведінку бібліотеки на льоту. Після виходу виправленої версії бібліотеки wrapper видаляється. Якщо не видалити — це вже архітектурна проблема.
Новий розробник у legacy-проекті не розуміє, чому код працює саме так. Замість того щоб розібратися, він додає нову умову поверх існуючої. Це найнебезпечніший тип милиці, тому що автор не усвідомлює, що це милиця. Єдині ліки — код-рев’ю та парне програмування для нових членів команди.
Не кожна милиця — зло. У реальній розробці абсолютна чистота коду недосяжна та часто недоцільна. Прагматичний підхід визнає, що тимчасові рішення — частина процесу, але вимагає їх усвідомленості, документування та планування усунення. Милиця виправдана, коли вона вирішує бізнес-завдання швидше, ніж чисте архітектурне рішення.
Критерії виправданої милиці: вона закриває конкретну проблему, у неї є owner (хто відповідає за її видалення), та існує план рефакторингу. Якщо хоча б однієї з трьох умов немає — милиця перетворюється на технічний борг. Інструменти на кшталт TODO-коментарів з тікетом у трекері — мінімальний спосіб документування.
Критичний баг у релізній гілці, який потрібно закрити до завтрашнього деплою. Чисте рішення вимагає рефакторингу архітектури та займе два тижні. Милиця — додати перевірку на nil та відправити фікс як hotfix. Умови виправданості: у трекері створено тікет на рефакторинг, відповідальний призначений, милиця позначена коментарем. Через два тижні команда повертається до завдання.
// TODO: IT-1234 — видалити цю милицю після рефакторингу AuthService
guard let userId = session.user?.id else {
return Result.failure(.notAuthenticated)
}
Межа між усвідомленою милицею та архітектурною проблемою (технічним боргом) проходить за двома параметрами: усвідомленість рішення та наявність плану з його усунення. Милиця — це завжди тимчасове рішення з відомим терміном життя. Технічний борг — це наслідки безлічі милиць, залишених без уваги.
| Параметр | Усвідомлена милиця | Технічний борг |
|---|---|---|
| Усвідомленість | Команда знає, що це тимчасове рішення | Ніхто не пам’ятає, чому код такий |
| Документація | Є TODO, тікет у трекері | Немає коментарів, посилань, опису |
| План усунення | Призначено спринт для рефакторингу | «Колись перепишемо» |
| Вплив | Локальний, не заважає новій функціональності | Блокує зміни, уповільнює розробку |
Ситуація погіршується, коли кількість милиць перевищує критичну масу. Кожна нова милиця збільшує «крихкість» системи: зміна в одному місці ламає інше. У підсумку розробка сповільнюється, баги множаться, а новий розробник не може розібратися в коді без допомоги автора. У цей момент милиці перестають бути тимчасовими рішеннями та стають архітектурною проблемою.
Якщо в коді зустрічається п’ять вкладених перевірок на версію ОС, виробника пристрою та наявність конкретної бібліотеки — це не милиця, це архітектурна проблема. Якщо додавання одного фіксу викликає три регресії в суміжних модулях — милиці перестали бути локальними. Якщо код-рев’ю регулярно відхиляється через «ще одну милицю» — час планувати рефакторинг.
Рефакторинг милиць — процес заміни тимчасових рішень на архітектурно коректні. Це вимагає часу, тому потрібна стратегія пріоритизації: не всі милиці потрібно усувати негайно. Хороша стратегія — оцінювати кожну милицю за двома параметрами: частота змін у цій області коду та вплив на користувачів.
Високий пріоритет — милиці в модулях, що часто змінюються (бізнес-логіка, UI загального призначення), які уповільнюють розробку та викликають регресії. Середній пріоритет — милиці в рідко змінюваних модулях, але з потенційним впливом на користувачів (обробка платежів, авторизація). Низький пріоритет — милиці в legacy-коді, який працює стабільно та не планується до модифікації.
Крок 1: інвентаризація — знайди всі TODO та FIXME, пов’язані з милицями. Крок 2: оцінка — визнач, які з них все ще актуальні. Крок 3: планування — признач рефакторинг милиць у спринт, починаючи з високопріоритетних. Крок 4: заміна — реалізуй чисте рішення, видали милицю та її TODO-коментар. Крок 5: верифікація — переконайся, що тести проходять та регресій немає.
# Знайти всі TODO-милиці в проекті
grep -r "TODO\|FIXME\|HACK\|XXX" --include="*.kt" --include="*.swift" .
Найкращий спосіб боротися з милицями — не створювати їх без необхідності. Перед тим як написати милицю, постав собі три запитання: чи можна зробити чисте рішення за розумний час? Чи є альтернатива, яка не є милицею? Чи буде у команди час повернутися та переписати це? Якщо хоча б на одне запитання відповідь «ні» — подумай ще раз, перш ніж «підпирати» код.
Часті запитання
Кострубати — написати тимчасове рішення, яке закриває проблему, але не усуває її причину. Код працює, але не відповідає архітектурі проекту та може зламатися при змінах.
Милиця — усвідомлене тимчасове рішення з планом усунення. Технічний борг — наслідки безлічі забутих милиць. Милиця локальна, борг системний і блокує розвиток.
Коли дедлайн критичний, чисте рішення потребує часу, а милиця задокументована TODO-коментарем та тікетом у трекері. Умова: у милиці є план видалення в осяжному майбутньому.
Додайте TODO або FIXME з номером тікету та коротким описом правильного рішення. Приклад: // TODO: IT-567 — rewrite using Factory pattern. Без тікету милиця буде забута.
Проведіть інвентаризацію всіх TODO, оцініть пріоритет, почніть з модулів, що часто змінюються. Замініть милицю на чисте рішення, видаліть коментар та перевірте тестами.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також